Math.test.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers');
  2. const { expect } = require('chai');
  3. const { MAX_UINT256 } = constants;
  4. const { Rounding } = require('../../helpers/enums.js');
  5. const MathMock = artifacts.require('MathMock');
  6. contract('Math', function (accounts) {
  7. const min = new BN('1234');
  8. const max = new BN('5678');
  9. const MAX_UINT256_SUB1 = MAX_UINT256.sub(new BN('1'));
  10. const MAX_UINT256_SUB2 = MAX_UINT256.sub(new BN('2'));
  11. beforeEach(async function () {
  12. this.math = await MathMock.new();
  13. });
  14. describe('max', function () {
  15. it('is correctly detected in first argument position', async function () {
  16. expect(await this.math.max(max, min)).to.be.bignumber.equal(max);
  17. });
  18. it('is correctly detected in second argument position', async function () {
  19. expect(await this.math.max(min, max)).to.be.bignumber.equal(max);
  20. });
  21. });
  22. describe('min', function () {
  23. it('is correctly detected in first argument position', async function () {
  24. expect(await this.math.min(min, max)).to.be.bignumber.equal(min);
  25. });
  26. it('is correctly detected in second argument position', async function () {
  27. expect(await this.math.min(max, min)).to.be.bignumber.equal(min);
  28. });
  29. });
  30. describe('average', function () {
  31. function bnAverage (a, b) {
  32. return a.add(b).divn(2);
  33. }
  34. it('is correctly calculated with two odd numbers', async function () {
  35. const a = new BN('57417');
  36. const b = new BN('95431');
  37. expect(await this.math.average(a, b)).to.be.bignumber.equal(bnAverage(a, b));
  38. });
  39. it('is correctly calculated with two even numbers', async function () {
  40. const a = new BN('42304');
  41. const b = new BN('84346');
  42. expect(await this.math.average(a, b)).to.be.bignumber.equal(bnAverage(a, b));
  43. });
  44. it('is correctly calculated with one even and one odd number', async function () {
  45. const a = new BN('57417');
  46. const b = new BN('84346');
  47. expect(await this.math.average(a, b)).to.be.bignumber.equal(bnAverage(a, b));
  48. });
  49. it('is correctly calculated with two max uint256 numbers', async function () {
  50. const a = MAX_UINT256;
  51. expect(await this.math.average(a, a)).to.be.bignumber.equal(bnAverage(a, a));
  52. });
  53. });
  54. describe('ceilDiv', function () {
  55. it('does not round up on exact division', async function () {
  56. const a = new BN('10');
  57. const b = new BN('5');
  58. expect(await this.math.ceilDiv(a, b)).to.be.bignumber.equal('2');
  59. });
  60. it('rounds up on division with remainders', async function () {
  61. const a = new BN('42');
  62. const b = new BN('13');
  63. expect(await this.math.ceilDiv(a, b)).to.be.bignumber.equal('4');
  64. });
  65. it('does not overflow', async function () {
  66. const b = new BN('2');
  67. const result = new BN('1').shln(255);
  68. expect(await this.math.ceilDiv(MAX_UINT256, b)).to.be.bignumber.equal(result);
  69. });
  70. it('correctly computes max uint256 divided by 1', async function () {
  71. const b = new BN('1');
  72. expect(await this.math.ceilDiv(MAX_UINT256, b)).to.be.bignumber.equal(MAX_UINT256);
  73. });
  74. });
  75. describe('muldiv', function () {
  76. it('divide by 0', async function () {
  77. await expectRevert.unspecified(this.math.mulDiv(1, 1, 0, Rounding.Down));
  78. });
  79. describe('does round down', async function () {
  80. it('small values', async function () {
  81. expect(await this.math.mulDiv('3', '4', '5', Rounding.Down)).to.be.bignumber.equal('2');
  82. expect(await this.math.mulDiv('3', '5', '5', Rounding.Down)).to.be.bignumber.equal('3');
  83. });
  84. it('large values', async function () {
  85. expect(await this.math.mulDiv(
  86. new BN('42'),
  87. MAX_UINT256_SUB1,
  88. MAX_UINT256,
  89. Rounding.Down,
  90. )).to.be.bignumber.equal(new BN('41'));
  91. expect(await this.math.mulDiv(
  92. new BN('17'),
  93. MAX_UINT256,
  94. MAX_UINT256,
  95. Rounding.Down,
  96. )).to.be.bignumber.equal(new BN('17'));
  97. expect(await this.math.mulDiv(
  98. MAX_UINT256_SUB1,
  99. MAX_UINT256_SUB1,
  100. MAX_UINT256,
  101. Rounding.Down,
  102. )).to.be.bignumber.equal(MAX_UINT256_SUB2);
  103. expect(await this.math.mulDiv(
  104. MAX_UINT256,
  105. MAX_UINT256_SUB1,
  106. MAX_UINT256,
  107. Rounding.Down,
  108. )).to.be.bignumber.equal(MAX_UINT256_SUB1);
  109. expect(await this.math.mulDiv(
  110. MAX_UINT256,
  111. MAX_UINT256,
  112. MAX_UINT256,
  113. Rounding.Down,
  114. )).to.be.bignumber.equal(MAX_UINT256);
  115. });
  116. });
  117. describe('does round up', async function () {
  118. it('small values', async function () {
  119. expect(await this.math.mulDiv('3', '4', '5', Rounding.Up)).to.be.bignumber.equal('3');
  120. expect(await this.math.mulDiv('3', '5', '5', Rounding.Up)).to.be.bignumber.equal('3');
  121. });
  122. it('large values', async function () {
  123. expect(await this.math.mulDiv(
  124. new BN('42'),
  125. MAX_UINT256_SUB1,
  126. MAX_UINT256,
  127. Rounding.Up,
  128. )).to.be.bignumber.equal(new BN('42'));
  129. expect(await this.math.mulDiv(
  130. new BN('17'),
  131. MAX_UINT256,
  132. MAX_UINT256,
  133. Rounding.Up,
  134. )).to.be.bignumber.equal(new BN('17'));
  135. expect(await this.math.mulDiv(
  136. MAX_UINT256_SUB1,
  137. MAX_UINT256_SUB1,
  138. MAX_UINT256,
  139. Rounding.Up,
  140. )).to.be.bignumber.equal(MAX_UINT256_SUB1);
  141. expect(await this.math.mulDiv(
  142. MAX_UINT256,
  143. MAX_UINT256_SUB1,
  144. MAX_UINT256,
  145. Rounding.Up,
  146. )).to.be.bignumber.equal(MAX_UINT256_SUB1);
  147. expect(await this.math.mulDiv(
  148. MAX_UINT256,
  149. MAX_UINT256,
  150. MAX_UINT256,
  151. Rounding.Up,
  152. )).to.be.bignumber.equal(MAX_UINT256);
  153. });
  154. });
  155. });
  156. describe('sqrt', function () {
  157. it('rounds down', async function () {
  158. expect(await this.math.sqrt('0', Rounding.Down)).to.be.bignumber.equal('0');
  159. expect(await this.math.sqrt('1', Rounding.Down)).to.be.bignumber.equal('1');
  160. expect(await this.math.sqrt('2', Rounding.Down)).to.be.bignumber.equal('1');
  161. expect(await this.math.sqrt('3', Rounding.Down)).to.be.bignumber.equal('1');
  162. expect(await this.math.sqrt('4', Rounding.Down)).to.be.bignumber.equal('2');
  163. expect(await this.math.sqrt('144', Rounding.Down)).to.be.bignumber.equal('12');
  164. expect(await this.math.sqrt('999999', Rounding.Down)).to.be.bignumber.equal('999');
  165. expect(await this.math.sqrt('1000000', Rounding.Down)).to.be.bignumber.equal('1000');
  166. expect(await this.math.sqrt('1000001', Rounding.Down)).to.be.bignumber.equal('1000');
  167. expect(await this.math.sqrt('1002000', Rounding.Down)).to.be.bignumber.equal('1000');
  168. expect(await this.math.sqrt('1002001', Rounding.Down)).to.be.bignumber.equal('1001');
  169. expect(await this.math.sqrt(MAX_UINT256, Rounding.Down))
  170. .to.be.bignumber.equal('340282366920938463463374607431768211455');
  171. });
  172. it('rounds up', async function () {
  173. expect(await this.math.sqrt('0', Rounding.Up)).to.be.bignumber.equal('0');
  174. expect(await this.math.sqrt('1', Rounding.Up)).to.be.bignumber.equal('1');
  175. expect(await this.math.sqrt('2', Rounding.Up)).to.be.bignumber.equal('2');
  176. expect(await this.math.sqrt('3', Rounding.Up)).to.be.bignumber.equal('2');
  177. expect(await this.math.sqrt('4', Rounding.Up)).to.be.bignumber.equal('2');
  178. expect(await this.math.sqrt('144', Rounding.Up)).to.be.bignumber.equal('12');
  179. expect(await this.math.sqrt('999999', Rounding.Up)).to.be.bignumber.equal('1000');
  180. expect(await this.math.sqrt('1000000', Rounding.Up)).to.be.bignumber.equal('1000');
  181. expect(await this.math.sqrt('1000001', Rounding.Up)).to.be.bignumber.equal('1001');
  182. expect(await this.math.sqrt('1002000', Rounding.Up)).to.be.bignumber.equal('1001');
  183. expect(await this.math.sqrt('1002001', Rounding.Up)).to.be.bignumber.equal('1001');
  184. expect(await this.math.sqrt(MAX_UINT256, Rounding.Up))
  185. .to.be.bignumber.equal('340282366920938463463374607431768211456');
  186. });
  187. });
  188. describe('log', function () {
  189. describe('log2', function () {
  190. it('rounds down', async function () {
  191. expect(await this.math.log2('0', Rounding.Down)).to.be.bignumber.equal('0');
  192. expect(await this.math.log2('1', Rounding.Down)).to.be.bignumber.equal('0');
  193. expect(await this.math.log2('2', Rounding.Down)).to.be.bignumber.equal('1');
  194. expect(await this.math.log2('3', Rounding.Down)).to.be.bignumber.equal('1');
  195. expect(await this.math.log2('4', Rounding.Down)).to.be.bignumber.equal('2');
  196. expect(await this.math.log2('5', Rounding.Down)).to.be.bignumber.equal('2');
  197. expect(await this.math.log2('6', Rounding.Down)).to.be.bignumber.equal('2');
  198. expect(await this.math.log2('7', Rounding.Down)).to.be.bignumber.equal('2');
  199. expect(await this.math.log2('8', Rounding.Down)).to.be.bignumber.equal('3');
  200. expect(await this.math.log2('9', Rounding.Down)).to.be.bignumber.equal('3');
  201. expect(await this.math.log2(MAX_UINT256, Rounding.Down)).to.be.bignumber.equal('255');
  202. });
  203. it('rounds up', async function () {
  204. expect(await this.math.log2('0', Rounding.Up)).to.be.bignumber.equal('0');
  205. expect(await this.math.log2('1', Rounding.Up)).to.be.bignumber.equal('0');
  206. expect(await this.math.log2('2', Rounding.Up)).to.be.bignumber.equal('1');
  207. expect(await this.math.log2('3', Rounding.Up)).to.be.bignumber.equal('2');
  208. expect(await this.math.log2('4', Rounding.Up)).to.be.bignumber.equal('2');
  209. expect(await this.math.log2('5', Rounding.Up)).to.be.bignumber.equal('3');
  210. expect(await this.math.log2('6', Rounding.Up)).to.be.bignumber.equal('3');
  211. expect(await this.math.log2('7', Rounding.Up)).to.be.bignumber.equal('3');
  212. expect(await this.math.log2('8', Rounding.Up)).to.be.bignumber.equal('3');
  213. expect(await this.math.log2(MAX_UINT256, Rounding.Up)).to.be.bignumber.equal('256');
  214. });
  215. });
  216. describe('log10', function () {
  217. it('rounds down', async function () {
  218. expect(await this.math.log10('0', Rounding.Down)).to.be.bignumber.equal('0');
  219. expect(await this.math.log10('1', Rounding.Down)).to.be.bignumber.equal('0');
  220. expect(await this.math.log10('2', Rounding.Down)).to.be.bignumber.equal('0');
  221. expect(await this.math.log10('9', Rounding.Down)).to.be.bignumber.equal('0');
  222. expect(await this.math.log10('10', Rounding.Down)).to.be.bignumber.equal('1');
  223. expect(await this.math.log10('11', Rounding.Down)).to.be.bignumber.equal('1');
  224. expect(await this.math.log10('99', Rounding.Down)).to.be.bignumber.equal('1');
  225. expect(await this.math.log10('100', Rounding.Down)).to.be.bignumber.equal('2');
  226. expect(await this.math.log10('101', Rounding.Down)).to.be.bignumber.equal('2');
  227. expect(await this.math.log10('999', Rounding.Down)).to.be.bignumber.equal('2');
  228. expect(await this.math.log10('1000', Rounding.Down)).to.be.bignumber.equal('3');
  229. expect(await this.math.log10('1001', Rounding.Down)).to.be.bignumber.equal('3');
  230. expect(await this.math.log10(MAX_UINT256, Rounding.Down)).to.be.bignumber.equal('77');
  231. });
  232. it('rounds up', async function () {
  233. expect(await this.math.log10('0', Rounding.Up)).to.be.bignumber.equal('0');
  234. expect(await this.math.log10('1', Rounding.Up)).to.be.bignumber.equal('0');
  235. expect(await this.math.log10('2', Rounding.Up)).to.be.bignumber.equal('1');
  236. expect(await this.math.log10('9', Rounding.Up)).to.be.bignumber.equal('1');
  237. expect(await this.math.log10('10', Rounding.Up)).to.be.bignumber.equal('1');
  238. expect(await this.math.log10('11', Rounding.Up)).to.be.bignumber.equal('2');
  239. expect(await this.math.log10('99', Rounding.Up)).to.be.bignumber.equal('2');
  240. expect(await this.math.log10('100', Rounding.Up)).to.be.bignumber.equal('2');
  241. expect(await this.math.log10('101', Rounding.Up)).to.be.bignumber.equal('3');
  242. expect(await this.math.log10('999', Rounding.Up)).to.be.bignumber.equal('3');
  243. expect(await this.math.log10('1000', Rounding.Up)).to.be.bignumber.equal('3');
  244. expect(await this.math.log10('1001', Rounding.Up)).to.be.bignumber.equal('4');
  245. expect(await this.math.log10(MAX_UINT256, Rounding.Up)).to.be.bignumber.equal('78');
  246. });
  247. });
  248. describe('log256', function () {
  249. it('rounds down', async function () {
  250. expect(await this.math.log256('0', Rounding.Down)).to.be.bignumber.equal('0');
  251. expect(await this.math.log256('1', Rounding.Down)).to.be.bignumber.equal('0');
  252. expect(await this.math.log256('2', Rounding.Down)).to.be.bignumber.equal('0');
  253. expect(await this.math.log256('255', Rounding.Down)).to.be.bignumber.equal('0');
  254. expect(await this.math.log256('256', Rounding.Down)).to.be.bignumber.equal('1');
  255. expect(await this.math.log256('257', Rounding.Down)).to.be.bignumber.equal('1');
  256. expect(await this.math.log256('65535', Rounding.Down)).to.be.bignumber.equal('1');
  257. expect(await this.math.log256('65536', Rounding.Down)).to.be.bignumber.equal('2');
  258. expect(await this.math.log256('65537', Rounding.Down)).to.be.bignumber.equal('2');
  259. expect(await this.math.log256(MAX_UINT256, Rounding.Down)).to.be.bignumber.equal('31');
  260. });
  261. it('rounds up', async function () {
  262. expect(await this.math.log256('0', Rounding.Up)).to.be.bignumber.equal('0');
  263. expect(await this.math.log256('1', Rounding.Up)).to.be.bignumber.equal('0');
  264. expect(await this.math.log256('2', Rounding.Up)).to.be.bignumber.equal('1');
  265. expect(await this.math.log256('255', Rounding.Up)).to.be.bignumber.equal('1');
  266. expect(await this.math.log256('256', Rounding.Up)).to.be.bignumber.equal('1');
  267. expect(await this.math.log256('257', Rounding.Up)).to.be.bignumber.equal('2');
  268. expect(await this.math.log256('65535', Rounding.Up)).to.be.bignumber.equal('2');
  269. expect(await this.math.log256('65536', Rounding.Up)).to.be.bignumber.equal('2');
  270. expect(await this.math.log256('65537', Rounding.Up)).to.be.bignumber.equal('3');
  271. expect(await this.math.log256(MAX_UINT256, Rounding.Up)).to.be.bignumber.equal('32');
  272. });
  273. });
  274. });
  275. });