// SPDX-License-Identifier: BUSL-1.1 // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . pragma solidity ^0.8.28; /* solhint-disable private-vars-leading-underscore, reason-string, func-name-mixedcase, gas-custom-errors */ library PMath { uint256 internal constant ONE = 1e18; // 18 decimal places int256 internal constant IONE = 1e18; // 18 decimal places uint256 internal constant ONE_YEAR = 365 days; int256 internal constant IONE_YEAR = 365 days; uint256 internal constant ONE_MUL_YEAR = 1e18 * 365 days; int256 internal constant IONE_MUL_YEAR = 1e18 * 365 days; function inc(uint256 x) internal pure returns (uint256) { unchecked { return x + 1; } } function dec(uint256 x) internal pure returns (uint256) { unchecked { return x - 1; } } function mulUp(uint256 x, uint256 y) internal pure returns (uint256 z) { assembly ("memory-safe") { z := mul(x, y) // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`. if iszero(eq(div(z, y), x)) { if y { mstore(0x00, 0xbac65e5b) // `MulWadFailed()`. revert(0x1c, 0x04) } } z := add(iszero(iszero(mod(z, ONE))), div(z, ONE)) } } function mulDown(uint256 x, uint256 y) internal pure returns (uint256 z) { assembly ("memory-safe") { // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`. if gt(x, div(not(0), y)) { if y { mstore(0x00, 0xbac65e5b) // `MulWadFailed()`. revert(0x1c, 0x04) } } z := div(mul(x, y), ONE) } } function mulDown(int256 x, int256 y) internal pure returns (int256 z) { assembly ("memory-safe") { z := mul(x, y) // Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`. if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) { mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`. revert(0x1c, 0x04) } z := sdiv(z, IONE) } } function divDown(uint256 x, uint256 y) internal pure returns (uint256 z) { assembly ("memory-safe") { // Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`. if iszero(mul(y, lt(x, add(1, div(not(0), ONE))))) { mstore(0x00, 0x7c5f487d) // `DivWadFailed()`. revert(0x1c, 0x04) } z := div(mul(x, ONE), y) } } function divDown(int256 x, int256 y) internal pure returns (int256 z) { assembly ("memory-safe") { z := mul(x, ONE) // Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`. if iszero(mul(y, eq(sdiv(z, ONE), x))) { mstore(0x00, 0x5c43740d) // `SDivWadFailed()`. revert(0x1c, 0x04) } z := sdiv(z, y) } } function rawDivUp(uint256 x, uint256 d) internal pure returns (uint256 z) { assembly ("memory-safe") { if iszero(d) { mstore(0x00, 0x65244e4e) // `DivFailed()`. revert(0x1c, 0x04) } z := add(iszero(iszero(mod(x, d))), div(x, d)) } } function rawDivCeil(int256 x, int256 d) internal pure returns (int256 z) { assembly ("memory-safe") { if iszero(d) { mstore(0x00, 0x65244e4e) // `DivFailed()`. revert(0x1c, 0x04) } z := sdiv(x, d) if iszero(xor(shr(255, x), shr(255, d))) { z := add(z, iszero(iszero(smod(x, d)))) } } } function rawDivFloor(int256 x, int256 d) internal pure returns (int256 z) { assembly ("memory-safe") { if iszero(d) { mstore(0x00, 0x65244e4e) // `DivFailed()`. revert(0x1c, 0x04) } z := sdiv(x, d) if xor(shr(255, x), shr(255, d)) { z := sub(z, iszero(iszero(smod(x, d)))) } } } function mulCeil(int256 x, int256 y) internal pure returns (int256) { return rawDivCeil(x * y, IONE); } function mulFloor(int256 x, int256 y) internal pure returns (int256) { return rawDivFloor(x * y, IONE); } function tweakUp(uint256 a, uint256 factor) internal pure returns (uint256) { return mulUp(a, ONE + factor); } function tweakDown(uint256 a, uint256 factor) internal pure returns (uint256) { return mulDown(a, ONE - factor); } function abs(int256 x) internal pure returns (uint256 z) { unchecked { z = (uint256(x) + uint256(x >> 255)) ^ uint256(x >> 255); } } function neg(uint256 x) internal pure returns (int256) { return Int(x) * (-1); } function max(uint256 x, uint256 y) internal pure returns (uint256 z) { assembly { z := xor(x, mul(xor(x, y), gt(y, x))) } } function max(int256 x, int256 y) internal pure returns (int256 z) { assembly { z := xor(x, mul(xor(x, y), sgt(y, x))) } } function max32(uint32 x, uint32 y) internal pure returns (uint32 z) { return (x > y ? x : y); } function max40(uint40 x, uint40 y) internal pure returns (uint40 z) { return (x > y ? x : y); } function min(uint256 x, uint256 y) internal pure returns (uint256 z) { assembly { z := xor(x, mul(xor(x, y), lt(y, x))) } } function min(int256 x, int256 y) internal pure returns (int256 z) { assembly { z := xor(x, mul(xor(x, y), slt(y, x))) } } function sign(int256 x) internal pure returns (int256) { if (x > 0) return 1; if (x < 0) return -1; return 0; } function avg(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = (x & y) + ((x ^ y) >> 1); } } function avg(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = (x >> 1) + (y >> 1) + (x & y & 1); } } /*/////////////////////////////////////////////////////////////// CASTS //////////////////////////////////////////////////////////////*/ function Int(uint256 x) internal pure returns (int256) { if (int256(x) >= 0) return int256(x); _revertOverflow(); } function Int128(uint256 x) internal pure returns (int128) { if ((x >> 127) == 0) return int128(int256(x)); _revertOverflow(); } function Int112(int256 x) internal pure returns (int112) { unchecked { if (((1 << 111) + uint256(x)) >> 112 == uint256(0)) return int112(x); _revertOverflow(); } } function Int128(int256 x) internal pure returns (int128) { unchecked { if (((1 << 127) + uint256(x)) >> 128 == uint256(0)) return int128(x); _revertOverflow(); } } function Uint(int256 x) internal pure returns (uint256) { if (x >= 0) return uint256(x); _revertOverflow(); } function Uint128(int256 x) internal pure returns (uint128) { if (x >= 0 && x < 1 << 128) return uint128(uint256(x)); _revertOverflow(); } function Uint8(bool x) internal pure returns (uint8) { return x ? 1 : 0; } function Uint8(uint256 x) internal pure returns (uint8) { if (x >= 1 << 8) _revertOverflow(); return uint8(x); } function Uint16(uint256 x) internal pure returns (uint16) { if (x >= 1 << 16) _revertOverflow(); return uint16(x); } function Uint32(uint256 x) internal pure returns (uint32) { if (x >= 1 << 32) _revertOverflow(); return uint32(x); } function Uint40(uint256 x) internal pure returns (uint40) { if (x >= 1 << 40) _revertOverflow(); return uint40(x); } function Uint64(uint256 x) internal pure returns (uint64) { if (x >= 1 << 64) _revertOverflow(); return uint64(x); } function Uint128(uint256 x) internal pure returns (uint128) { if (x >= 1 << 128) _revertOverflow(); return uint128(x); } function Uint224(uint256 x) internal pure returns (uint224) { if (x >= 1 << 224) _revertOverflow(); return uint224(x); } /*/////////////////////////////////////////////////////////////// SOLADY FUNCTIONS //////////////////////////////////////////////////////////////*/ function sqrt(uint256 x) internal pure returns (uint256 z) { assembly { // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`. z := 181 // The "correct" value is 1, but this saves a multiplication later. // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. // Let `y = x / 2**r`. We check `y >= 2**(k + 8)` // but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`. let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x)))) r := or(r, shl(5, lt(0xffffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffffff, shr(r, x)))) z := shl(shr(1, r), z) // Goal was to get `z*z*y` within a small factor of `x`. More iterations could // get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`. // We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small. // That's not possible if `x < 256` but we can just verify those cases exhaustively. // Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`. // Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`. // Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps. // For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)` // is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`, // with largest error when `s = 1` and when `s = 256` or `1/256`. // Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`. // Then we can estimate `sqrt(y)` using // `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`. // There is no overflow risk here since `y < 2**136` after the first branch above. z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181. // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // If `x+1` is a perfect square, the Babylonian method cycles between // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor. // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division z := sub(z, lt(div(x, z), z)) } } /*/////////////////////////////////////////////////////////////// MISCELLANEOUS FUNCTIONS //////////////////////////////////////////////////////////////*/ function isAApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) { return mulDown(b, ONE - eps) <= a && a <= mulDown(b, ONE + eps); } function isAGreaterApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) { return a >= b && a <= mulDown(b, ONE + eps); } function isASmallerApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) { return a <= b && a >= mulDown(b, ONE - eps); } function _revertOverflow() private pure { assembly ("memory-safe") { // Store the function selector of `Overflow()`. mstore(0x00, 0x35278d12) // Revert with (offset, size). revert(0x1c, 0x04) } } }