pragma solidity >=0.0; /// @title Math library - Allows calculation of logarithmic and exponential functions /// @author Alan Lu - /// @author Stefan George - library Math { /* * Constants */ // This is equal to 1 in our calculations uint public constant ONE = 0x10000000000000000; uint public constant LN2 = 0xb17217f7d1cf79ac; uint public constant LOG2_E = 0x171547652b82fe177; /* * Public functions */ /// @dev Returns natural exponential function value of given x /// @param x x /// @return e**x function exp(int x) public pure returns (uint) { // revert if x is > MAX_POWER, where // MAX_POWER = int(mp.floor(mp.log(mpf(2**256 - 1) / ONE) * ONE)) require(x <= 2454971259878909886679); // return 0 if exp(x) is tiny, using // MIN_POWER = int(mp.floor(mp.log(mpf(1) / ONE) * ONE)) if (x < -818323753292969962227) return 0; // Transform so that e^x -> 2^x x = x * int(ONE) / int(LN2); // 2^x = 2^whole(x) * 2^frac(x) // ^^^^^^^^^^ is a bit shift // so Taylor expand on z = frac(x) int shift; uint z; if (x >= 0) { shift = x / int(ONE); z = uint(x % int(ONE)); } else { shift = x / int(ONE) - 1; z = ONE - uint(-x % int(ONE)); } // 2^x = 1 + (ln 2) x + (ln 2)^2/2! x^2 + ... // // Can generate the z coefficients using mpmath and the following lines // >>> from mpmath import mp // >>> mp.dps = 100 // >>> ONE = 0x10000000000000000 // >>> print('\n'.join(hex(int(mp.log(2)**i / mp.factorial(i) * ONE)) for i in range(1, 7))) // 0xb17217f7d1cf79ab // 0x3d7f7bff058b1d50 // 0xe35846b82505fc5 // 0x276556df749cee5 // 0x5761ff9e299cc4 // 0xa184897c363c3 uint zpow = z; uint result = ONE; result += 0xb17217f7d1cf79ab * zpow / ONE; zpow = zpow * z / ONE; result += 0x3d7f7bff058b1d50 * zpow / ONE; zpow = zpow * z / ONE; result += 0xe35846b82505fc5 * zpow / ONE; zpow = zpow * z / ONE; result += 0x276556df749cee5 * zpow / ONE; zpow = zpow * z / ONE; result += 0x5761ff9e299cc4 * zpow / ONE; zpow = zpow * z / ONE; result += 0xa184897c363c3 * zpow / ONE; zpow = zpow * z / ONE; result += 0xffe5fe2c4586 * zpow / ONE; zpow = zpow * z / ONE; result += 0x162c0223a5c8 * zpow / ONE; zpow = zpow * z / ONE; result += 0x1b5253d395e * zpow / ONE; zpow = zpow * z / ONE; result += 0x1e4cf5158b * zpow / ONE; zpow = zpow * z / ONE; result += 0x1e8cac735 * zpow / ONE; zpow = zpow * z / ONE; result += 0x1c3bd650 * zpow / ONE; zpow = zpow * z / ONE; result += 0x1816193 * zpow / ONE; zpow = zpow * z / ONE; result += 0x131496 * zpow / ONE; zpow = zpow * z / ONE; result += 0xe1b7 * zpow / ONE; zpow = zpow * z / ONE; result += 0x9c7 * zpow / ONE; if (shift >= 0) { if (result >> uint(256 - shift) > 0) return (2 ** 256 - 1); return result << uint(shift); } else return result >> uint(-shift); } /// @dev Returns natural logarithm value of given x /// @param x x /// @return ln(x) function ln(uint x) public pure returns (int) { require(x > 0); // binary search for floor(log2(x)) int ilog2 = floorLog2(x); int z; if (ilog2 < 0) z = int(x << uint(-ilog2)); else z = int(x >> uint(ilog2)); // z = x * 2^-⌊log₂x⌋ // so 1 <= z < 2 // and ln z = ln x - ⌊log₂x⌋/log₂e // so just compute ln z using artanh series // and calculate ln x from that int term = (z - int(ONE)) * int(ONE) / (z + int(ONE)); int halflnz = term; int termpow = term * term / int(ONE) * term / int(ONE); halflnz += termpow / 3; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 5; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 7; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 9; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 11; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 13; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 15; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 17; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 19; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 21; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 23; termpow = termpow * term / int(ONE) * term / int(ONE); halflnz += termpow / 25; return (ilog2 * int(ONE)) * int(ONE) / int(LOG2_E) + 2 * halflnz; } /// @dev Returns base 2 logarithm value of given x /// @param x x /// @return lo logarithmic value function floorLog2(uint x) public pure returns (int lo) { lo = -64; int hi = 193; // I use a shift here instead of / 2 because it floors instead of rounding towards 0 int mid = (hi + lo) >> 1; while((lo + 1) < hi) { if (mid < 0 && x << uint(-mid) < ONE || mid >= 0 && x >> uint(mid) < ONE) hi = mid; else lo = mid; mid = (hi + lo) >> 1; } } /// @dev Returns maximum of an array /// @param nums Numbers to look through /// @return max Maximum number function max(int[] memory nums) public pure returns (int max) { require(nums.length > 0); max = -2**255; for (uint i = 0; i < nums.length; i++) if (nums[i] > max) max = nums[i]; } /// @dev Returns whether an add operation causes an overflow /// @param a First addend /// @param b Second addend /// @return Did no overflow occur? function safeToAdd(uint a, uint b) public pure returns (bool) { return a + b >= a; } /// @dev Returns whether a subtraction operation causes an underflow /// @param a Minuend /// @param b Subtrahend /// @return Did no underflow occur? function safeToSub(uint a, uint b) public pure returns (bool) { return a >= b; } /// @dev Returns whether a multiply operation causes an overflow /// @param a First factor /// @param b Second factor /// @return Did no overflow occur? function safeToMul(uint a, uint b) public pure returns (bool) { return b == 0 || a * b / b == a; } /// @dev Returns sum if no overflow occurred /// @param a First addend /// @param b Second addend /// @return Sum function add(uint a, uint b) public pure returns (uint) { require(safeToAdd(a, b)); return a + b; } /// @dev Returns difference if no overflow occurred /// @param a Minuend /// @param b Subtrahend /// @return Difference function sub(uint a, uint b) public pure returns (uint) { require(safeToSub(a, b)); return a - b; } /// @dev Returns product if no overflow occurred /// @param a First factor /// @param b Second factor /// @return Product function mul(uint a, uint b) public pure returns (uint) { require(safeToMul(a, b)); return a * b; } /// @dev Returns whether an add operation causes an overflow /// @param a First addend /// @param b Second addend /// @return Did no overflow occur? function safeToAdd(int a, int b) public pure returns (bool) { return (b >= 0 && a + b >= a) || (b < 0 && a + b < a); } /// @dev Returns whether a subtraction operation causes an underflow /// @param a Minuend /// @param b Subtrahend /// @return Did no underflow occur? function safeToSub(int a, int b) public pure returns (bool) { return (b >= 0 && a - b <= a) || (b < 0 && a - b > a); } /// @dev Returns whether a multiply operation causes an overflow /// @param a First factor /// @param b Second factor /// @return Did no overflow occur? function safeToMul(int a, int b) public pure returns (bool) { return (b == 0) || (a * b / b == a); } /// @dev Returns sum if no overflow occurred /// @param a First addend /// @param b Second addend /// @return Sum function add(int a, int b) public pure returns (int) { require(safeToAdd(a, b)); return a + b; } /// @dev Returns difference if no overflow occurred /// @param a Minuend /// @param b Subtrahend /// @return Difference function sub(int a, int b) public pure returns (int) { require(safeToSub(a, b)); return a - b; } /// @dev Returns product if no overflow occurred /// @param a First factor /// @param b Second factor /// @return Product function mul(int a, int b) public pure returns (int) { require(safeToMul(a, b)); return a * b; } }