protocol/contracts/staking/test/unit_tests/lib_fixed_math_test.ts

937 lines
37 KiB
TypeScript

import { blockchainTests, expect, hexRandom, Numberish } from '@0x/contracts-test-utils';
import { BigNumber, FixedMathRevertErrors } from '@0x/utils';
import { Decimal } from 'decimal.js';
import * as _ from 'lodash';
import { artifacts, TestLibFixedMathContract } from '../../src';
import { assertRoughlyEquals, fromFixed, toDecimal, toFixed } from '../utils/number_utils';
blockchainTests('LibFixedMath unit tests', env => {
let testContract: TestLibFixedMathContract;
before(async () => {
testContract = await TestLibFixedMathContract.deployFrom0xArtifactAsync(
artifacts.TestLibFixedMath,
env.provider,
env.txDefaults,
artifacts,
);
});
const BITS_OF_PRECISION = 127;
const FIXED_POINT_DIVISOR = new BigNumber(2).pow(BITS_OF_PRECISION);
const FIXED_1 = FIXED_POINT_DIVISOR;
const MAX_FIXED_VALUE = new BigNumber(2).pow(255).minus(1);
const MIN_FIXED_VALUE = new BigNumber(2).pow(255).times(-1);
const MIN_EXP_NUMBER = new BigNumber('-63.875');
const MAX_EXP_NUMBER = new BigNumber(0);
// e ^ MIN_EXP_NUMBER
const MIN_LN_NUMBER = new BigNumber(new Decimal(MIN_EXP_NUMBER.toFixed(128)).exp().toFixed(128));
const FUZZ_COUNT = 1024;
function assertFixedEquals(actualFixed: Numberish, expected: Numberish): void {
expect(fromFixed(actualFixed)).to.bignumber.eq(fromFixed(toFixed(expected)));
}
function assertFixedRoughlyEquals(actualFixed: Numberish, expected: Numberish, precision: number = 18): void {
assertRoughlyEquals(fromFixed(actualFixed), expected, precision);
}
describe('one()', () => {
it('equals 1', async () => {
const r = await testContract.one.callAsync();
assertFixedEquals(r, 1);
});
});
describe('abs()', () => {
it('abs(n) == n', async () => {
const n = 1337.5912;
const r = await testContract.abs.callAsync(toFixed(n));
assertFixedEquals(r, n);
});
it('abs(-n) == n', async () => {
const n = -1337.5912;
const r = await testContract.abs.callAsync(toFixed(n));
assertFixedEquals(r, -n);
});
it('abs(0) == 0', async () => {
const n = 0;
const r = await testContract.abs.callAsync(toFixed(n));
expect(r).to.bignumber.eq(0);
});
it('abs(MAX_FIXED) == MAX_FIXED', async () => {
const n = MAX_FIXED_VALUE;
const r = await testContract.abs.callAsync(n);
expect(r).to.bignumber.eq(n);
});
it('abs(MIN_FIXED) throws', async () => {
const n = MIN_FIXED_VALUE;
const expectedError = new FixedMathRevertErrors.SignedValueError(
FixedMathRevertErrors.ValueErrorCodes.TooSmall,
n,
);
const tx = testContract.abs.callAsync(n);
return expect(tx).to.revertWith(expectedError);
});
it('abs(int(-1)) == int(1)', async () => {
const n = -1;
const r = await testContract.abs.callAsync(new BigNumber(n));
expect(r).to.bignumber.eq(1);
});
it('abs(int(1)) == int(1)', async () => {
const n = 1;
const r = await testContract.abs.callAsync(new BigNumber(n));
expect(r).to.bignumber.eq(1);
});
});
describe('invert()', () => {
it('invert(1) == 1', async () => {
const n = 1;
const r = await testContract.invert.callAsync(toFixed(n));
assertFixedEquals(r, n);
});
it('invert(n) == 1 / n', async () => {
const n = 1337.5912;
const r = await testContract.invert.callAsync(toFixed(n));
assertFixedRoughlyEquals(r, 1 / n);
});
it('invert(-n) == -1 / n', async () => {
const n = -1337.5912;
const r = await testContract.invert.callAsync(toFixed(n));
assertFixedRoughlyEquals(r, 1 / n);
});
it('invert(0) throws', async () => {
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.DivisionByZero,
);
const tx = testContract.invert.callAsync(toFixed(0));
return expect(tx).to.revertWith(expectedError);
});
});
describe('mulDiv()', () => {
it('mulDiv(0, 0, 1) == 0', async () => {
const [a, n, d] = [0, 0, 1];
const r = await testContract.mulDiv.callAsync(toFixed(a), new BigNumber(n), new BigNumber(d));
assertFixedEquals(r, 0);
});
it('mulDiv(0, x, y) == 0', async () => {
const [a, n, d] = [0, 13, 300];
const r = await testContract.mulDiv.callAsync(toFixed(a), new BigNumber(n), new BigNumber(d));
assertFixedEquals(r, 0);
});
it('mulDiv(x, y, y) == x', async () => {
const [a, n, d] = [1.2345, 149, 149];
const r = await testContract.mulDiv.callAsync(toFixed(a), new BigNumber(n), new BigNumber(d));
assertFixedEquals(r, a);
});
it('mulDiv(x, -y, y) == -x', async () => {
const [a, n, d] = [1.2345, -149, 149];
const r = await testContract.mulDiv.callAsync(toFixed(a), new BigNumber(n), new BigNumber(d));
assertFixedEquals(r, -a);
});
it('mulDiv(-x, -y, y) == x', async () => {
const [a, n, d] = [-1.2345, -149, 149];
const r = await testContract.mulDiv.callAsync(toFixed(a), new BigNumber(n), new BigNumber(d));
assertFixedEquals(r, -a);
});
it('mulDiv(x, y, 0) throws', async () => {
const [a, n, d] = [1.2345, 149, 0];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.DivisionByZero,
);
const tx = testContract.mulDiv.callAsync(toFixed(a), new BigNumber(n), new BigNumber(d));
return expect(tx).to.revertWith(expectedError);
});
it('mulDiv(int(-1), int(1), int(-1)) == int(1)', async () => {
const [a, n, d] = [-1, 1, -1];
const r = await testContract.mulDiv.callAsync(new BigNumber(a), new BigNumber(n), new BigNumber(d));
assertFixedEquals(r, fromFixed(1));
});
it('mulDiv(int(1), int(-1), int(-1)) == int(1)', async () => {
const [a, n, d] = [1, -1, -1];
const r = await testContract.mulDiv.callAsync(new BigNumber(a), new BigNumber(n), new BigNumber(d));
assertFixedEquals(r, fromFixed(1));
});
it('mulDiv(MIN_FIXED, int(-1), int(1)) throws', async () => {
const [a, n, d] = [MIN_FIXED_VALUE, -1, 1];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
n,
);
const tx = testContract.mulDiv.callAsync(a, new BigNumber(n), new BigNumber(d));
return expect(tx).to.revertWith(expectedError);
});
it('mulDiv(MIN_FIXED, int(-1), int(1)) throws', async () => {
const [a, n, d] = [MIN_FIXED_VALUE, -1, 1];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
n,
);
const tx = testContract.mulDiv.callAsync(a, new BigNumber(n), new BigNumber(d));
return expect(tx).to.revertWith(expectedError);
});
it('mulDiv(MIN_FIXED, int(1), int(-1)) throws', async () => {
const [a, n, d] = [MIN_FIXED_VALUE, 1, -1];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.DivisionOverflow,
a,
d,
);
const tx = testContract.mulDiv.callAsync(a, new BigNumber(n), new BigNumber(d));
return expect(tx).to.revertWith(expectedError);
});
it('mulDiv(MAX_FIXED, int(-1), int(1)) == -MAX_FIXED', async () => {
const [a, n, d] = [MAX_FIXED_VALUE, -1, 1];
const r = await testContract.mulDiv.callAsync(a, new BigNumber(n), new BigNumber(d));
expect(r).to.bignumber.eq(MAX_FIXED_VALUE.negated());
});
it('mulDiv(MAX_FIXED, int(1), int(-1)) == -MAX_FIXED', async () => {
const [a, n, d] = [MAX_FIXED_VALUE, 1, -1];
const r = await testContract.mulDiv.callAsync(a, new BigNumber(n), new BigNumber(d));
expect(r).to.bignumber.eq(MAX_FIXED_VALUE.negated());
});
});
describe('add()', () => {
function add(a: Numberish, b: Numberish): BigNumber {
return fromFixed(toFixed(a).plus(toFixed(b)));
}
it('0 + 0 == 0', async () => {
const [a, b] = [0, 0];
const r = await testContract.add.callAsync(toFixed(a), toFixed(b));
assertFixedEquals(r, 0);
});
it('adds two positive decimals', async () => {
const [a, b] = ['9310841.31841', '491021921.318948193'];
const r = await testContract.add.callAsync(toFixed(a), toFixed(b));
assertFixedEquals(r, add(a, b));
});
it('adds two mixed decimals', async () => {
const [a, b] = ['9310841.31841', '-491021921.318948193'];
const r = await testContract.add.callAsync(toFixed(a), toFixed(b));
assertFixedEquals(r, add(a, b));
});
it('throws on overflow', async () => {
const [a, b] = [MAX_FIXED_VALUE, new BigNumber(1)];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a,
b,
);
const tx = testContract.add.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
it('throws on underflow', async () => {
const [a, b] = [MIN_FIXED_VALUE, new BigNumber(-1)];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a,
b,
);
const tx = testContract.add.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
it('MIN_FIXED + MIN_FIXED throws', async () => {
const [a, b] = [MIN_FIXED_VALUE, MIN_FIXED_VALUE];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a,
b,
);
const tx = testContract.add.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
it('MAX_FIXED + MAX_FIXED throws', async () => {
const [a, b] = [MAX_FIXED_VALUE, MAX_FIXED_VALUE];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a,
b,
);
const tx = testContract.add.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
it('MIN_FIXED + MAX_FIXED == int(-1)', async () => {
const [a, b] = [MIN_FIXED_VALUE, MAX_FIXED_VALUE];
const r = await testContract.add.callAsync(a, b);
expect(r).to.bignumber.eq(-1);
});
});
describe('sub()', () => {
function sub(a: Numberish, b: Numberish): BigNumber {
return fromFixed(toFixed(a).minus(toFixed(b)));
}
it('0 - 0 == 0', async () => {
const [a, b] = [0, 0];
const r = await testContract.sub.callAsync(toFixed(a), toFixed(b));
assertFixedEquals(r, 0);
});
it('subtracts two positive decimals', async () => {
const [a, b] = ['9310841.31841', '491021921.318948193'];
const r = await testContract.sub.callAsync(toFixed(a), toFixed(b));
assertFixedEquals(r, sub(a, b));
});
it('subtracts two mixed decimals', async () => {
const [a, b] = ['9310841.31841', '-491021921.318948193'];
const r = await testContract.sub.callAsync(toFixed(a), toFixed(b));
assertFixedEquals(r, sub(a, b));
});
it('throws on underflow', async () => {
const [a, b] = [MIN_FIXED_VALUE, new BigNumber(1)];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a,
b.negated(),
);
const tx = testContract.sub.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
it('throws on overflow', async () => {
const [a, b] = [MAX_FIXED_VALUE, new BigNumber(-1)];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a,
b.negated(),
);
const tx = testContract.sub.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
it('MIN_FIXED - MIN_FIXED throws', async () => {
const [a, b] = [MIN_FIXED_VALUE, MIN_FIXED_VALUE];
// This fails because `-MIN_FIXED_VALUE == MIN_FIXED_VALUE` because of
// twos-complement.
const expectedError = new FixedMathRevertErrors.SignedValueError(
FixedMathRevertErrors.ValueErrorCodes.TooSmall,
b,
);
const tx = testContract.sub.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
it('MAX_FIXED - MAX_FIXED == 0', async () => {
const [a, b] = [MAX_FIXED_VALUE, MAX_FIXED_VALUE];
const r = await testContract.sub.callAsync(a, b);
expect(r).to.bignumber.eq(0);
});
it('MIN_FIXED - MAX_FIXED throws', async () => {
const [a, b] = [MIN_FIXED_VALUE, MAX_FIXED_VALUE];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a,
b.negated(),
);
const tx = testContract.sub.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
it('MAX_FIXED - MIN_FIXED throws', async () => {
const [a, b] = [MAX_FIXED_VALUE, MIN_FIXED_VALUE];
const expectedError = new FixedMathRevertErrors.SignedValueError(
FixedMathRevertErrors.ValueErrorCodes.TooSmall,
b,
);
const tx = testContract.sub.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
});
describe('mul()', () => {
function mul(a: Numberish, b: Numberish): BigNumber {
return fromFixed(
toFixed(a)
.times(toFixed(b))
.dividedToIntegerBy(FIXED_POINT_DIVISOR),
);
}
it('x * 0 == 0', async () => {
const [a, b] = [1337, 0];
const r = await testContract.mul.callAsync(toFixed(a), toFixed(b));
assertFixedEquals(r, b);
});
it('x * 1 == x', async () => {
const [a, b] = [0.5, 1];
const r = await testContract.mul.callAsync(toFixed(a), toFixed(b));
assertFixedEquals(r, a);
});
it('x * -1 == -x', async () => {
const [a, b] = [0.5, -1];
const r = await testContract.mul.callAsync(toFixed(a), toFixed(b));
assertFixedEquals(r, -a);
});
it('multiplies two positive decimals', async () => {
const [a, b] = ['1.25394912112', '0.03413318948193'];
const r = await testContract.mul.callAsync(toFixed(a), toFixed(b));
assertFixedEquals(r, mul(a, b));
});
it('multiplies two mixed decimals', async () => {
const [a, b] = ['1.25394912112', '-0.03413318948193'];
const r = await testContract.mul.callAsync(toFixed(a), toFixed(b));
assertFixedEquals(r, mul(a, b));
});
it('throws on underflow', async () => {
const [a, b] = [MIN_FIXED_VALUE, new BigNumber(2)];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
b,
);
const tx = testContract.mul.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
it('throws on overflow', async () => {
const [a, b] = [MAX_FIXED_VALUE, new BigNumber(2)];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
b,
);
const tx = testContract.mul.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
it('MAX_FIXED * int(1) == MAX_FIXED / FIXED_1', async () => {
const [a, b] = [MAX_FIXED_VALUE, 1];
const r = await testContract.mul.callAsync(a, new BigNumber(b));
expect(r).to.bignumber.eq(MAX_FIXED_VALUE.dividedToIntegerBy(FIXED_1));
});
it('MAX_FIXED * int(2) throws', async () => {
const [a, b] = [MAX_FIXED_VALUE, 2];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
b,
);
const tx = testContract.mul.callAsync(a, new BigNumber(b));
return expect(tx).to.revertWith(expectedError);
});
it('MAX_FIXED * MAX_FIXED throws', async () => {
const [a, b] = [MAX_FIXED_VALUE, MAX_FIXED_VALUE];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
b,
);
const tx = testContract.mul.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
it('MIN_FIXED * MIN_FIXED throws', async () => {
const [a, b] = [MIN_FIXED_VALUE, MIN_FIXED_VALUE];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
b,
);
const tx = testContract.mul.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
it('MAX_FIXED * MIN_FIXED throws', async () => {
const [a, b] = [MAX_FIXED_VALUE, MIN_FIXED_VALUE];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
b,
);
const tx = testContract.mul.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
it('MIN_FIXED * int(-1) throws', async () => {
const [a, b] = [MIN_FIXED_VALUE, -1];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
b,
);
const tx = testContract.mul.callAsync(a, new BigNumber(b));
return expect(tx).to.revertWith(expectedError);
});
it('MAX_FIXED * int(-1) == -MAX_FIXED / FIXED_1', async () => {
const [a, b] = [MAX_FIXED_VALUE, -1];
const r = await testContract.mul.callAsync(a, new BigNumber(b));
expect(r).to.bignumber.eq(MAX_FIXED_VALUE.negated().dividedToIntegerBy(FIXED_1));
});
});
describe('div()', () => {
function div(a: Numberish, b: Numberish): BigNumber {
return fromFixed(
toFixed(a)
.times(FIXED_POINT_DIVISOR)
.dividedBy(toFixed(b)),
);
}
it('x / 0 throws', async () => {
const [a, b] = [1, 0];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.DivisionByZero,
toFixed(a).times(FIXED_POINT_DIVISOR),
toFixed(b),
);
const tx = testContract.div.callAsync(toFixed(a), toFixed(b));
return expect(tx).to.revertWith(expectedError);
});
it('x / 1 == x', async () => {
const [a, b] = [1.41214552, 1];
const r = await testContract.div.callAsync(toFixed(a), toFixed(b));
assertFixedEquals(r, a);
});
it('x / -1 == -x', async () => {
const [a, b] = [1.109312, -1];
const r = await testContract.div.callAsync(toFixed(a), toFixed(b));
assertFixedEquals(r, -a);
});
it('divides two positive decimals', async () => {
const [a, b] = ['1.25394912112', '0.03413318948193'];
const r = await testContract.div.callAsync(toFixed(a), toFixed(b));
assertFixedEquals(r, div(a, b));
});
it('divides two mixed decimals', async () => {
const [a, b] = ['1.25394912112', '-0.03413318948193'];
const r = await testContract.div.callAsync(toFixed(a), toFixed(b));
assertFixedEquals(r, div(a, b));
});
it('MIN_FIXED / int(-1) throws', async () => {
const [a, b] = [MIN_FIXED_VALUE, -1];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
FIXED_1,
);
const tx = testContract.div.callAsync(a, new BigNumber(b));
return expect(tx).to.revertWith(expectedError);
});
it('MAX_FIXED / int(-1) throws', async () => {
const [a, b] = [MIN_FIXED_VALUE, -1];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
FIXED_1,
);
const tx = testContract.div.callAsync(a, new BigNumber(b));
return expect(tx).to.revertWith(expectedError);
});
it('int(-1) / MIN_FIXED == 0', async () => {
const [a, b] = [-1, MIN_FIXED_VALUE];
const r = await testContract.div.callAsync(new BigNumber(a), b);
expect(r).to.bignumber.eq(0);
});
});
describe('uintMul()', () => {
it('0 * x == 0', async () => {
const [a, b] = [0, 1234];
const r = await testContract.uintMul.callAsync(toFixed(a), new BigNumber(b));
expect(r).to.bignumber.eq(0);
});
it('1 * x == int(x)', async () => {
const [a, b] = [1, 1234];
const r = await testContract.uintMul.callAsync(toFixed(a), new BigNumber(b));
expect(r).to.bignumber.eq(Math.trunc(b));
});
it('-1 * x == 0', async () => {
const [a, b] = [-1, 1234];
const r = await testContract.uintMul.callAsync(toFixed(a), new BigNumber(b));
expect(r).to.bignumber.eq(0);
});
it('0.5 * x == x/2', async () => {
const [a, b] = [0.5, 1234];
const r = await testContract.uintMul.callAsync(toFixed(a), new BigNumber(b));
expect(r).to.bignumber.eq(b / 2);
});
it('0.5 * x == 0 if x = 1', async () => {
const [a, b] = [0.5, 1];
const r = await testContract.uintMul.callAsync(toFixed(a), new BigNumber(b));
expect(r).to.bignumber.eq(0);
});
it('throws if rhs is too large', async () => {
const [a, b] = [toFixed(1), MAX_FIXED_VALUE.plus(1)];
const expectedError = new FixedMathRevertErrors.UnsignedValueError(
FixedMathRevertErrors.ValueErrorCodes.TooLarge,
b,
);
const tx = testContract.uintMul.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
it('throws if lhs is too large', async () => {
const [a, b] = [MAX_FIXED_VALUE, new BigNumber(2)];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
a,
b,
);
const tx = testContract.uintMul.callAsync(a, b);
return expect(tx).to.revertWith(expectedError);
});
});
describe('toInteger()', () => {
it('toInteger(n) == int(n)', async () => {
const n = 1337.5912;
const r = await testContract.toInteger.callAsync(toFixed(n));
expect(r).to.bignumber.eq(Math.trunc(n));
});
it('toInteger(-n) == -int(n)', async () => {
const n = -1337.5912;
const r = await testContract.toInteger.callAsync(toFixed(n));
expect(r).to.bignumber.eq(Math.trunc(n));
});
it('toInteger(n) == 0, when 0 < n < 1', async () => {
const n = 0.9995;
const r = await testContract.toInteger.callAsync(toFixed(n));
expect(r).to.bignumber.eq(0);
});
it('toInteger(-n) == 0, when -1 < n < 0', async () => {
const n = -0.9995;
const r = await testContract.toInteger.callAsync(toFixed(n));
expect(r).to.bignumber.eq(0);
});
it('toInteger(0) == 0', async () => {
const n = 0;
const r = await testContract.toInteger.callAsync(toFixed(n));
expect(r).to.bignumber.eq(0);
});
});
describe('toFixed()', () => {
describe('signed', () => {
it('converts a positive integer', async () => {
const n = 1337;
const r = await testContract.toFixedSigned1.callAsync(new BigNumber(n));
assertFixedEquals(r, n);
});
it('converts a negative integer', async () => {
const n = -1337;
const r = await testContract.toFixedSigned1.callAsync(new BigNumber(n));
assertFixedEquals(r, n);
});
it('converts a fraction with a positive numerator and denominator', async () => {
const [n, d] = [1337, 1000];
const r = await testContract.toFixedSigned2.callAsync(new BigNumber(n), new BigNumber(d));
assertFixedEquals(r, n / d);
});
it('converts a fraction with a negative numerator and positive denominator', async () => {
const [n, d] = [-1337, 1000];
const r = await testContract.toFixedSigned2.callAsync(new BigNumber(n), new BigNumber(d));
assertFixedEquals(r, n / d);
});
it('converts a fraction with a negative numerator and denominator', async () => {
const [n, d] = [-1337, -1000];
const r = await testContract.toFixedSigned2.callAsync(new BigNumber(n), new BigNumber(d));
assertFixedEquals(r, n / d);
});
it('converts a fraction with a negative numerator and negative denominator', async () => {
const [n, d] = [-1337, -1000];
const r = await testContract.toFixedSigned2.callAsync(new BigNumber(n), new BigNumber(d));
assertFixedEquals(r, n / d);
});
it('throws if the numerator is too large to convert', async () => {
const [n, d] = [MAX_FIXED_VALUE.dividedToIntegerBy(FIXED_POINT_DIVISOR).plus(1), new BigNumber(1000)];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
n,
FIXED_POINT_DIVISOR,
);
const tx = testContract.toFixedSigned2.callAsync(n, d);
return expect(tx).to.revertWith(expectedError);
});
it('throws if the denominator is zero', async () => {
const [n, d] = [new BigNumber(1), new BigNumber(0)];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.DivisionByZero,
n.times(FIXED_POINT_DIVISOR),
d,
);
const tx = testContract.toFixedSigned2.callAsync(n, d);
return expect(tx).to.revertWith(expectedError);
});
});
describe('unsigned', () => {
it('converts an integer', async () => {
const n = 1337;
const r = await testContract.toFixedUnsigned1.callAsync(new BigNumber(n));
assertFixedEquals(r, n);
});
it('converts a fraction', async () => {
const [n, d] = [1337, 1000];
const r = await testContract.toFixedUnsigned2.callAsync(new BigNumber(n), new BigNumber(d));
assertFixedEquals(r, n / d);
});
it('throws if the numerator is too large', async () => {
const [n, d] = [MAX_FIXED_VALUE.plus(1), new BigNumber(1000)];
const expectedError = new FixedMathRevertErrors.UnsignedValueError(
FixedMathRevertErrors.ValueErrorCodes.TooLarge,
n,
);
const tx = testContract.toFixedUnsigned2.callAsync(n, d);
return expect(tx).to.revertWith(expectedError);
});
it('throws if the denominator is too large', async () => {
const [n, d] = [new BigNumber(1000), MAX_FIXED_VALUE.plus(1)];
const expectedError = new FixedMathRevertErrors.UnsignedValueError(
FixedMathRevertErrors.ValueErrorCodes.TooLarge,
d,
);
const tx = testContract.toFixedUnsigned2.callAsync(n, d);
return expect(tx).to.revertWith(expectedError);
});
it('throws if the numerator is too large to convert', async () => {
const [n, d] = [MAX_FIXED_VALUE.dividedToIntegerBy(FIXED_POINT_DIVISOR).plus(1), new BigNumber(1000)];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
n,
FIXED_POINT_DIVISOR,
);
const tx = testContract.toFixedUnsigned2.callAsync(n, d);
return expect(tx).to.revertWith(expectedError);
});
it('throws if the denominator is zero', async () => {
const [n, d] = [new BigNumber(1), new BigNumber(0)];
const expectedError = new FixedMathRevertErrors.BinOpError(
FixedMathRevertErrors.BinOpErrorCodes.DivisionByZero,
n.times(FIXED_POINT_DIVISOR),
d,
);
const tx = testContract.toFixedUnsigned2.callAsync(n, d);
return expect(tx).to.revertWith(expectedError);
});
});
});
function getRandomDecimal(min: Numberish, max: Numberish): BigNumber {
const range = new BigNumber(max).minus(min);
const random = fromFixed(new BigNumber(hexRandom().substr(2), 16));
return random.mod(range).plus(min);
}
describe('ln()', () => {
const LN_PRECISION = 16;
function ln(x: Numberish): BigNumber {
return new BigNumber(
toDecimal(x)
.ln()
.toFixed(128),
);
}
it('ln(x = 0) throws', async () => {
const x = toFixed(0);
const expectedError = new FixedMathRevertErrors.SignedValueError(
FixedMathRevertErrors.ValueErrorCodes.TooSmall,
x,
);
const tx = testContract.ln.callAsync(x);
return expect(tx).to.revertWith(expectedError);
});
it('ln(x > 1) throws', async () => {
const x = toFixed(1.000001);
const expectedError = new FixedMathRevertErrors.SignedValueError(
FixedMathRevertErrors.ValueErrorCodes.TooLarge,
x,
);
const tx = testContract.ln.callAsync(x);
return expect(tx).to.revertWith(expectedError);
});
it('ln(x < 0) throws', async () => {
const x = toFixed(-0.000001);
const expectedError = new FixedMathRevertErrors.SignedValueError(
FixedMathRevertErrors.ValueErrorCodes.TooSmall,
x,
);
const tx = testContract.ln.callAsync(x);
return expect(tx).to.revertWith(expectedError);
});
it('ln(x = 1) == 0', async () => {
const x = toFixed(1);
const r = await testContract.ln.callAsync(x);
assertFixedEquals(r, 0);
});
it('ln(x < LN_MIN_VAL) == EXP_MIN_VAL', async () => {
const x = toFixed(MIN_LN_NUMBER).minus(1);
const r = await testContract.ln.callAsync(x);
assertFixedEquals(r, MIN_EXP_NUMBER);
});
it('ln(x), where x is close to 0', async () => {
const x = new BigNumber('1e-27');
const r = await testContract.ln.callAsync(toFixed(x));
assertFixedRoughlyEquals(r, ln(x), 12);
});
it('ln(x), where x is close to 1', async () => {
const x = new BigNumber(1).minus('1e-27');
const r = await testContract.ln.callAsync(toFixed(x));
assertFixedRoughlyEquals(r, ln(x), LN_PRECISION);
});
it('ln(x = 0.85)', async () => {
const x = 0.85;
const r = await testContract.ln.callAsync(toFixed(x));
assertFixedRoughlyEquals(r, ln(x), LN_PRECISION);
});
blockchainTests.optional('fuzzing', () => {
const inputs = _.times(FUZZ_COUNT, () => getRandomDecimal(0, 1));
for (const x of inputs) {
it(`ln(${x.toString(10)})`, async () => {
const r = await testContract.ln.callAsync(toFixed(x));
assertFixedRoughlyEquals(r, ln(x), LN_PRECISION);
});
}
});
});
describe('exp()', () => {
const EXP_PRECISION = 18;
function exp(x: Numberish): BigNumber {
return new BigNumber(
toDecimal(x)
.exp()
.toFixed(128),
);
}
it('exp(x = 0) == 1', async () => {
const x = toFixed(0);
const r = await testContract.exp.callAsync(x);
assertFixedEquals(r, 1);
});
it('exp(x > EXP_MAX_VAL) throws', async () => {
const x = toFixed(MAX_EXP_NUMBER).plus(1);
const expectedError = new FixedMathRevertErrors.SignedValueError(
FixedMathRevertErrors.ValueErrorCodes.TooLarge,
x,
);
const tx = testContract.exp.callAsync(x);
return expect(tx).to.revertWith(expectedError);
});
it('exp(x < EXP_MIN_VAL) == 0', async () => {
const x = toFixed(MIN_EXP_NUMBER).minus(1);
const r = await testContract.exp.callAsync(x);
assertFixedEquals(r, 0);
});
it('exp(x < 0), where x is close to 0', async () => {
const x = new BigNumber('-1e-18');
const r = await testContract.exp.callAsync(toFixed(x));
assertFixedRoughlyEquals(r, exp(x), EXP_PRECISION);
});
it('exp(x), where x is close to EXP_MIN_VAL', async () => {
const x = MIN_EXP_NUMBER.plus('1e-18');
const r = await testContract.exp.callAsync(toFixed(x));
assertFixedRoughlyEquals(r, exp(x), EXP_PRECISION);
});
it('exp(x = -0.85)', async () => {
const x = -0.85;
const r = await testContract.exp.callAsync(toFixed(x));
assertFixedRoughlyEquals(r, exp(x), EXP_PRECISION);
});
blockchainTests.optional('fuzzing', () => {
const inputs = _.times(FUZZ_COUNT, () => getRandomDecimal(MIN_EXP_NUMBER, MAX_EXP_NUMBER));
for (const x of inputs) {
it(`exp(${x.toString(10)})`, async () => {
const r = await testContract.exp.callAsync(toFixed(x));
assertFixedRoughlyEquals(r, exp(x), EXP_PRECISION);
});
}
});
});
});
// tslint:disable-next-line: max-file-line-count