// SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.15; import {DSTestPlus} from "./utils/DSTestPlus.sol"; import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol"; contract FixedPointMathLibTest is DSTestPlus { function testMulWadDown() public { assertEq(FixedPointMathLib.mulWadDown(2.5e18, 0.5e18), 1.25e18); assertEq(FixedPointMathLib.mulWadDown(3e18, 1e18), 3e18); assertEq(FixedPointMathLib.mulWadDown(369, 271), 0); } function testMulWadDownEdgeCases() public { assertEq(FixedPointMathLib.mulWadDown(0, 1e18), 0); assertEq(FixedPointMathLib.mulWadDown(1e18, 0), 0); assertEq(FixedPointMathLib.mulWadDown(0, 0), 0); } function testMulWadUp() public { assertEq(FixedPointMathLib.mulWadUp(2.5e18, 0.5e18), 1.25e18); assertEq(FixedPointMathLib.mulWadUp(3e18, 1e18), 3e18); assertEq(FixedPointMathLib.mulWadUp(369, 271), 1); } function testMulWadUpEdgeCases() public { assertEq(FixedPointMathLib.mulWadUp(0, 1e18), 0); assertEq(FixedPointMathLib.mulWadUp(1e18, 0), 0); assertEq(FixedPointMathLib.mulWadUp(0, 0), 0); } function testDivWadDown() public { assertEq(FixedPointMathLib.divWadDown(1.25e18, 0.5e18), 2.5e18); assertEq(FixedPointMathLib.divWadDown(3e18, 1e18), 3e18); assertEq(FixedPointMathLib.divWadDown(2, 100000000000000e18), 0); } function testDivWadDownEdgeCases() public { assertEq(FixedPointMathLib.divWadDown(0, 1e18), 0); } function testFailDivWadDownZeroDenominator() public pure { FixedPointMathLib.divWadDown(1e18, 0); } function testDivWadUp() public { assertEq(FixedPointMathLib.divWadUp(1.25e18, 0.5e18), 2.5e18); assertEq(FixedPointMathLib.divWadUp(3e18, 1e18), 3e18); assertEq(FixedPointMathLib.divWadUp(2, 100000000000000e18), 1); } function testDivWadUpEdgeCases() public { assertEq(FixedPointMathLib.divWadUp(0, 1e18), 0); } function testFailDivWadUpZeroDenominator() public pure { FixedPointMathLib.divWadUp(1e18, 0); } function testMulDivDown() public { assertEq(FixedPointMathLib.mulDivDown(2.5e27, 0.5e27, 1e27), 1.25e27); assertEq(FixedPointMathLib.mulDivDown(2.5e18, 0.5e18, 1e18), 1.25e18); assertEq(FixedPointMathLib.mulDivDown(2.5e8, 0.5e8, 1e8), 1.25e8); assertEq(FixedPointMathLib.mulDivDown(369, 271, 1e2), 999); assertEq(FixedPointMathLib.mulDivDown(1e27, 1e27, 2e27), 0.5e27); assertEq(FixedPointMathLib.mulDivDown(1e18, 1e18, 2e18), 0.5e18); assertEq(FixedPointMathLib.mulDivDown(1e8, 1e8, 2e8), 0.5e8); assertEq(FixedPointMathLib.mulDivDown(2e27, 3e27, 2e27), 3e27); assertEq(FixedPointMathLib.mulDivDown(3e18, 2e18, 3e18), 2e18); assertEq(FixedPointMathLib.mulDivDown(2e8, 3e8, 2e8), 3e8); } function testMulDivDownEdgeCases() public { assertEq(FixedPointMathLib.mulDivDown(0, 1e18, 1e18), 0); assertEq(FixedPointMathLib.mulDivDown(1e18, 0, 1e18), 0); assertEq(FixedPointMathLib.mulDivDown(0, 0, 1e18), 0); } function testFailMulDivDownZeroDenominator() public pure { FixedPointMathLib.mulDivDown(1e18, 1e18, 0); } function testMulDivUp() public { assertEq(FixedPointMathLib.mulDivUp(2.5e27, 0.5e27, 1e27), 1.25e27); assertEq(FixedPointMathLib.mulDivUp(2.5e18, 0.5e18, 1e18), 1.25e18); assertEq(FixedPointMathLib.mulDivUp(2.5e8, 0.5e8, 1e8), 1.25e8); assertEq(FixedPointMathLib.mulDivUp(369, 271, 1e2), 1000); assertEq(FixedPointMathLib.mulDivUp(1e27, 1e27, 2e27), 0.5e27); assertEq(FixedPointMathLib.mulDivUp(1e18, 1e18, 2e18), 0.5e18); assertEq(FixedPointMathLib.mulDivUp(1e8, 1e8, 2e8), 0.5e8); assertEq(FixedPointMathLib.mulDivUp(2e27, 3e27, 2e27), 3e27); assertEq(FixedPointMathLib.mulDivUp(3e18, 2e18, 3e18), 2e18); assertEq(FixedPointMathLib.mulDivUp(2e8, 3e8, 2e8), 3e8); } function testMulDivUpEdgeCases() public { assertEq(FixedPointMathLib.mulDivUp(0, 1e18, 1e18), 0); assertEq(FixedPointMathLib.mulDivUp(1e18, 0, 1e18), 0); assertEq(FixedPointMathLib.mulDivUp(0, 0, 1e18), 0); } function testFailMulDivUpZeroDenominator() public pure { FixedPointMathLib.mulDivUp(1e18, 1e18, 0); } function testRPow() public { assertEq(FixedPointMathLib.rpow(2e27, 2, 1e27), 4e27); assertEq(FixedPointMathLib.rpow(2e18, 2, 1e18), 4e18); assertEq(FixedPointMathLib.rpow(2e8, 2, 1e8), 4e8); assertEq(FixedPointMathLib.rpow(8, 3, 1), 512); } function testSqrt() public { assertEq(FixedPointMathLib.sqrt(0), 0); assertEq(FixedPointMathLib.sqrt(1), 1); assertEq(FixedPointMathLib.sqrt(2704), 52); assertEq(FixedPointMathLib.sqrt(110889), 333); assertEq(FixedPointMathLib.sqrt(32239684), 5678); assertEq(FixedPointMathLib.sqrt(type(uint256).max), 340282366920938463463374607431768211455); } function testSqrtBackHashedSingle() public { testSqrtBackHashed(123); } function testMulWadDown(uint256 x, uint256 y) public { // Ignore cases where x * y overflows. unchecked { if (x != 0 && (x * y) / x != y) return; } assertEq(FixedPointMathLib.mulWadDown(x, y), (x * y) / 1e18); } function testFailMulWadDownOverflow(uint256 x, uint256 y) public pure { // Ignore cases where x * y does not overflow. unchecked { if ((x * y) / x == y) revert(); } FixedPointMathLib.mulWadDown(x, y); } function testMulWadUp(uint256 x, uint256 y) public { // Ignore cases where x * y overflows. unchecked { if (x != 0 && (x * y) / x != y) return; } assertEq(FixedPointMathLib.mulWadUp(x, y), x * y == 0 ? 0 : (x * y - 1) / 1e18 + 1); } function testFailMulWadUpOverflow(uint256 x, uint256 y) public pure { // Ignore cases where x * y does not overflow. unchecked { if ((x * y) / x == y) revert(); } FixedPointMathLib.mulWadUp(x, y); } function testDivWadDown(uint256 x, uint256 y) public { // Ignore cases where x * WAD overflows or y is 0. unchecked { if (y == 0 || (x != 0 && (x * 1e18) / 1e18 != x)) return; } assertEq(FixedPointMathLib.divWadDown(x, y), (x * 1e18) / y); } function testFailDivWadDownOverflow(uint256 x, uint256 y) public pure { // Ignore cases where x * WAD does not overflow or y is 0. unchecked { if (y == 0 || (x * 1e18) / 1e18 == x) revert(); } FixedPointMathLib.divWadDown(x, y); } function testFailDivWadDownZeroDenominator(uint256 x) public pure { FixedPointMathLib.divWadDown(x, 0); } function testDivWadUp(uint256 x, uint256 y) public { // Ignore cases where x * WAD overflows or y is 0. unchecked { if (y == 0 || (x != 0 && (x * 1e18) / 1e18 != x)) return; } assertEq(FixedPointMathLib.divWadUp(x, y), x == 0 ? 0 : (x * 1e18 - 1) / y + 1); } function testFailDivWadUpOverflow(uint256 x, uint256 y) public pure { // Ignore cases where x * WAD does not overflow or y is 0. unchecked { if (y == 0 || (x * 1e18) / 1e18 == x) revert(); } FixedPointMathLib.divWadUp(x, y); } function testFailDivWadUpZeroDenominator(uint256 x) public pure { FixedPointMathLib.divWadUp(x, 0); } function testMulDivDown( uint256 x, uint256 y, uint256 denominator ) public { // Ignore cases where x * y overflows or denominator is 0. unchecked { if (denominator == 0 || (x != 0 && (x * y) / x != y)) return; } assertEq(FixedPointMathLib.mulDivDown(x, y, denominator), (x * y) / denominator); } function testFailMulDivDownOverflow( uint256 x, uint256 y, uint256 denominator ) public pure { // Ignore cases where x * y does not overflow or denominator is 0. unchecked { if (denominator == 0 || (x * y) / x == y) revert(); } FixedPointMathLib.mulDivDown(x, y, denominator); } function testFailMulDivDownZeroDenominator(uint256 x, uint256 y) public pure { FixedPointMathLib.mulDivDown(x, y, 0); } function testMulDivUp( uint256 x, uint256 y, uint256 denominator ) public { // Ignore cases where x * y overflows or denominator is 0. unchecked { if (denominator == 0 || (x != 0 && (x * y) / x != y)) return; } assertEq(FixedPointMathLib.mulDivUp(x, y, denominator), x * y == 0 ? 0 : (x * y - 1) / denominator + 1); } function testFailMulDivUpOverflow( uint256 x, uint256 y, uint256 denominator ) public pure { // Ignore cases where x * y does not overflow or denominator is 0. unchecked { if (denominator == 0 || (x * y) / x == y) revert(); } FixedPointMathLib.mulDivUp(x, y, denominator); } function testFailMulDivUpZeroDenominator(uint256 x, uint256 y) public pure { FixedPointMathLib.mulDivUp(x, y, 0); } function testDifferentiallyFuzzSqrt(uint256 x) public { assertEq(FixedPointMathLib.sqrt(x), uniswapSqrt(x)); assertEq(FixedPointMathLib.sqrt(x), abdkSqrt(x)); } function testSqrt(uint256 x) public { uint256 root = FixedPointMathLib.sqrt(x); uint256 next = root + 1; // Ignore cases where next * next overflows. unchecked { if (next * next < next) return; } assertTrue(root * root <= x && next * next > x); } function testSqrtBack(uint256 x) public { unchecked { x >>= 128; while (x != 0) { assertEq(FixedPointMathLib.sqrt(x * x), x); x >>= 1; } } } function testSqrtBackHashed(uint256 x) public { testSqrtBack(uint256(keccak256(abi.encode(x)))); } function uniswapSqrt(uint256 y) internal pure returns (uint256 z) { if (y > 3) { z = y; uint256 x = y / 2 + 1; while (x < z) { z = x; x = (y / x + x) / 2; } } else if (y != 0) { z = 1; } } function abdkSqrt(uint256 x) private pure returns (uint256) { unchecked { if (x == 0) return 0; else { uint256 xx = x; uint256 r = 1; if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; } if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; } if (xx >= 0x100000000) { xx >>= 32; r <<= 16; } if (xx >= 0x10000) { xx >>= 16; r <<= 8; } if (xx >= 0x100) { xx >>= 8; r <<= 4; } if (xx >= 0x10) { xx >>= 4; r <<= 2; } if (xx >= 0x8) { r <<= 1; } r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; // Seven iterations should be enough uint256 r1 = x / r; return r < r1 ? r : r1; } } } }