diff --git a/src/libraries/FullMath.sol b/src/libraries/FullMath.sol new file mode 100644 index 0000000..05d656e --- /dev/null +++ b/src/libraries/FullMath.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.36; + +/// @title 512 bit multiply then divide without intermediate overflow. +library FullMath { + function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) { + unchecked { + uint256 prod0; + uint256 prod1; + assembly { + let mm := mulmod(a, b, not(0)) + prod0 := mul(a, b) + prod1 := sub(sub(mm, prod0), lt(mm, prod0)) + } + + if (prod1 == 0) { + require(denominator > 0, "FullMath: DIV_ZERO"); + assembly { + result := div(prod0, denominator) + } + return result; + } + + require(denominator > prod1, "FullMath: OVERFLOW"); + + uint256 remainder; + assembly { + remainder := mulmod(a, b, denominator) + } + assembly { + prod1 := sub(prod1, gt(remainder, prod0)) + prod0 := sub(prod0, remainder) + } + + uint256 twos = (0 - denominator) & denominator; + assembly { + denominator := div(denominator, twos) + } + assembly { + prod0 := div(prod0, twos) + } + assembly { + twos := add(div(sub(0, twos), twos), 1) + } + prod0 |= prod1 * twos; + + uint256 inv = (3 * denominator) ^ 2; + inv *= 2 - denominator * inv; + inv *= 2 - denominator * inv; + inv *= 2 - denominator * inv; + inv *= 2 - denominator * inv; + inv *= 2 - denominator * inv; + inv *= 2 - denominator * inv; + + result = prod0 * inv; + return result; + } + } + + function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) { + unchecked { + result = mulDiv(a, b, denominator); + if (mulmod(a, b, denominator) > 0) { + require(result < type(uint256).max, "FullMath: ROUND_OVERFLOW"); + result++; + } + } + } +}