mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			341 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Solidity
		
	
	
	
	
	
			
		
		
	
	
			341 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Solidity
		
	
	
	
	
	
| pragma solidity >=0.0;
 | |
| 
 | |
| 
 | |
| /// @title Math library - Allows calculation of logarithmic and exponential functions
 | |
| /// @author Alan Lu - <alan.lu@gnosis.pm>
 | |
| /// @author Stefan George - <stefan@gnosis.pm>
 | |
| 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;
 | |
|     }
 | |
| }
 |