| 1 | // SPDX-License-Identifier: MIT |
| 2 | pragma solidity >=0.8.0 <0.9.0; |
| 3 | |
| 4 | import {stdMath} from "../src/StdMath.sol"; |
| 5 | import {Test, stdError} from "../src/Test.sol"; |
| 6 | |
| 7 | contract StdMathMock is Test { |
| 8 | function exposed_percentDelta(uint256 a, uint256 b) public pure returns (uint256) { |
| 9 | return stdMath.percentDelta(a, b); |
| 10 | } |
| 11 | |
| 12 | function exposed_percentDelta(int256 a, int256 b) public pure returns (uint256) { |
| 13 | return stdMath.percentDelta(a, b); |
| 14 | } |
| 15 | } |
| 16 | |
| 17 | contract StdMathTest is Test { |
| 18 | function test_GetAbs() external pure { |
| 19 | assertEq(stdMath.abs(-50), 50); |
| 20 | assertEq(stdMath.abs(50), 50); |
| 21 | assertEq(stdMath.abs(-1337), 1337); |
| 22 | assertEq(stdMath.abs(0), 0); |
| 23 | |
| 24 | assertEq(stdMath.abs(type(int256).min), (type(uint256).max >> 1) + 1); |
| 25 | assertEq(stdMath.abs(type(int256).max), (type(uint256).max >> 1)); |
| 26 | } |
| 27 | |
| 28 | function testFuzz_GetAbs(int256 a) external pure { |
| 29 | uint256 manualAbs = getAbs(a); |
| 30 | |
| 31 | uint256 abs = stdMath.abs(a); |
| 32 | |
| 33 | assertEq(abs, manualAbs); |
| 34 | } |
| 35 | |
| 36 | function test_GetDelta_Uint() external pure { |
| 37 | assertEq(stdMath.delta(uint256(0), uint256(0)), 0); |
| 38 | assertEq(stdMath.delta(uint256(0), uint256(1337)), 1337); |
| 39 | assertEq(stdMath.delta(uint256(0), type(uint64).max), type(uint64).max); |
| 40 | assertEq(stdMath.delta(uint256(0), type(uint128).max), type(uint128).max); |
| 41 | assertEq(stdMath.delta(uint256(0), type(uint256).max), type(uint256).max); |
| 42 | |
| 43 | assertEq(stdMath.delta(0, uint256(0)), 0); |
| 44 | assertEq(stdMath.delta(1337, uint256(0)), 1337); |
| 45 | assertEq(stdMath.delta(type(uint64).max, uint256(0)), type(uint64).max); |
| 46 | assertEq(stdMath.delta(type(uint128).max, uint256(0)), type(uint128).max); |
| 47 | assertEq(stdMath.delta(type(uint256).max, uint256(0)), type(uint256).max); |
| 48 | |
| 49 | assertEq(stdMath.delta(1337, uint256(1337)), 0); |
| 50 | assertEq(stdMath.delta(type(uint256).max, type(uint256).max), 0); |
| 51 | assertEq(stdMath.delta(5000, uint256(1250)), 3750); |
| 52 | } |
| 53 | |
| 54 | function testFuzz_GetDelta_Uint(uint256 a, uint256 b) external pure { |
| 55 | uint256 manualDelta = a > b ? a - b : b - a; |
| 56 | |
| 57 | uint256 delta = stdMath.delta(a, b); |
| 58 | |
| 59 | assertEq(delta, manualDelta); |
| 60 | } |
| 61 | |
| 62 | function test_GetDelta_Int() external pure { |
| 63 | assertEq(stdMath.delta(int256(0), int256(0)), 0); |
| 64 | assertEq(stdMath.delta(int256(0), int256(1337)), 1337); |
| 65 | assertEq(stdMath.delta(int256(0), type(int64).max), type(uint64).max >> 1); |
| 66 | assertEq(stdMath.delta(int256(0), type(int128).max), type(uint128).max >> 1); |
| 67 | assertEq(stdMath.delta(int256(0), type(int256).max), type(uint256).max >> 1); |
| 68 | |
| 69 | assertEq(stdMath.delta(0, int256(0)), 0); |
| 70 | assertEq(stdMath.delta(1337, int256(0)), 1337); |
| 71 | assertEq(stdMath.delta(type(int64).max, int256(0)), type(uint64).max >> 1); |
| 72 | assertEq(stdMath.delta(type(int128).max, int256(0)), type(uint128).max >> 1); |
| 73 | assertEq(stdMath.delta(type(int256).max, int256(0)), type(uint256).max >> 1); |
| 74 | |
| 75 | assertEq(stdMath.delta(-0, int256(0)), 0); |
| 76 | assertEq(stdMath.delta(-1337, int256(0)), 1337); |
| 77 | assertEq(stdMath.delta(type(int64).min, int256(0)), (type(uint64).max >> 1) + 1); |
| 78 | assertEq(stdMath.delta(type(int128).min, int256(0)), (type(uint128).max >> 1) + 1); |
| 79 | assertEq(stdMath.delta(type(int256).min, int256(0)), (type(uint256).max >> 1) + 1); |
| 80 | |
| 81 | assertEq(stdMath.delta(int256(0), -0), 0); |
| 82 | assertEq(stdMath.delta(int256(0), -1337), 1337); |
| 83 | assertEq(stdMath.delta(int256(0), type(int64).min), (type(uint64).max >> 1) + 1); |
| 84 | assertEq(stdMath.delta(int256(0), type(int128).min), (type(uint128).max >> 1) + 1); |
| 85 | assertEq(stdMath.delta(int256(0), type(int256).min), (type(uint256).max >> 1) + 1); |
| 86 | |
| 87 | assertEq(stdMath.delta(1337, int256(1337)), 0); |
| 88 | assertEq(stdMath.delta(type(int256).max, type(int256).max), 0); |
| 89 | assertEq(stdMath.delta(type(int256).min, type(int256).min), 0); |
| 90 | assertEq(stdMath.delta(type(int256).min, type(int256).max), type(uint256).max); |
| 91 | assertEq(stdMath.delta(5000, int256(1250)), 3750); |
| 92 | } |
| 93 | |
| 94 | function testFuzz_GetDelta_Int(int256 a, int256 b) external pure { |
| 95 | uint256 absA = getAbs(a); |
| 96 | uint256 absB = getAbs(b); |
| 97 | uint256 absDelta = absA > absB ? absA - absB : absB - absA; |
| 98 | |
| 99 | uint256 manualDelta; |
| 100 | if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) { |
| 101 | manualDelta = absDelta; |
| 102 | } |
| 103 | // (a < 0 && b >= 0) || (a >= 0 && b < 0) |
| 104 | else { |
| 105 | manualDelta = absA + absB; |
| 106 | } |
| 107 | |
| 108 | uint256 delta = stdMath.delta(a, b); |
| 109 | |
| 110 | assertEq(delta, manualDelta); |
| 111 | } |
| 112 | |
| 113 | function test_GetPercentDelta_Uint() external { |
| 114 | StdMathMock stdMathMock = new StdMathMock(); |
| 115 | |
| 116 | assertEq(stdMath.percentDelta(uint256(0), uint256(1337)), 1e18); |
| 117 | assertEq(stdMath.percentDelta(uint256(0), type(uint64).max), 1e18); |
| 118 | assertEq(stdMath.percentDelta(uint256(0), type(uint128).max), 1e18); |
| 119 | assertEq(stdMath.percentDelta(uint256(0), type(uint192).max), 1e18); |
| 120 | |
| 121 | assertEq(stdMath.percentDelta(1337, uint256(1337)), 0); |
| 122 | assertEq(stdMath.percentDelta(type(uint192).max, type(uint192).max), 0); |
| 123 | assertEq(stdMath.percentDelta(0, uint256(2500)), 1e18); |
| 124 | assertEq(stdMath.percentDelta(2500, uint256(2500)), 0); |
| 125 | assertEq(stdMath.percentDelta(5000, uint256(2500)), 1e18); |
| 126 | assertEq(stdMath.percentDelta(7500, uint256(2500)), 2e18); |
| 127 | |
| 128 | vm.expectRevert(stdError.divisionError); |
| 129 | stdMathMock.exposed_percentDelta(uint256(1), 0); |
| 130 | } |
| 131 | |
| 132 | function testFuzz_GetPercentDelta_Uint(uint192 a, uint192 b) external pure { |
| 133 | vm.assume(b != 0); |
| 134 | uint256 manualDelta = a > b ? a - b : b - a; |
| 135 | |
| 136 | uint256 manualPercentDelta = manualDelta * 1e18 / b; |
| 137 | uint256 percentDelta = stdMath.percentDelta(a, b); |
| 138 | |
| 139 | assertEq(percentDelta, manualPercentDelta); |
| 140 | } |
| 141 | |
| 142 | function test_GetPercentDelta_Int() external { |
| 143 | // We deploy a mock version so we can properly test the revert. |
| 144 | StdMathMock stdMathMock = new StdMathMock(); |
| 145 | |
| 146 | assertEq(stdMath.percentDelta(int256(0), int256(1337)), 1e18); |
| 147 | assertEq(stdMath.percentDelta(int256(0), -1337), 1e18); |
| 148 | assertEq(stdMath.percentDelta(int256(0), type(int64).min), 1e18); |
| 149 | assertEq(stdMath.percentDelta(int256(0), type(int128).min), 1e18); |
| 150 | assertEq(stdMath.percentDelta(int256(0), type(int192).min), 1e18); |
| 151 | assertEq(stdMath.percentDelta(int256(0), type(int64).max), 1e18); |
| 152 | assertEq(stdMath.percentDelta(int256(0), type(int128).max), 1e18); |
| 153 | assertEq(stdMath.percentDelta(int256(0), type(int192).max), 1e18); |
| 154 | |
| 155 | assertEq(stdMath.percentDelta(1337, int256(1337)), 0); |
| 156 | assertEq(stdMath.percentDelta(type(int192).max, type(int192).max), 0); |
| 157 | assertEq(stdMath.percentDelta(type(int192).min, type(int192).min), 0); |
| 158 | |
| 159 | assertEq(stdMath.percentDelta(type(int192).min, type(int192).max), 2e18); // rounds the 1 wei diff down |
| 160 | assertEq(stdMath.percentDelta(type(int192).max, type(int192).min), 2e18 - 1); // rounds the 1 wei diff down |
| 161 | assertEq(stdMath.percentDelta(0, int256(2500)), 1e18); |
| 162 | assertEq(stdMath.percentDelta(2500, int256(2500)), 0); |
| 163 | assertEq(stdMath.percentDelta(5000, int256(2500)), 1e18); |
| 164 | assertEq(stdMath.percentDelta(7500, int256(2500)), 2e18); |
| 165 | |
| 166 | vm.expectRevert(stdError.divisionError); |
| 167 | stdMathMock.exposed_percentDelta(int256(1), 0); |
| 168 | } |
| 169 | |
| 170 | function testFuzz_GetPercentDelta_Int(int192 a, int192 b) external pure { |
| 171 | vm.assume(b != 0); |
| 172 | uint256 absA = getAbs(a); |
| 173 | uint256 absB = getAbs(b); |
| 174 | uint256 absDelta = absA > absB ? absA - absB : absB - absA; |
| 175 | |
| 176 | uint256 manualDelta; |
| 177 | if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) { |
| 178 | manualDelta = absDelta; |
| 179 | } |
| 180 | // (a < 0 && b >= 0) || (a >= 0 && b < 0) |
| 181 | else { |
| 182 | manualDelta = absA + absB; |
| 183 | } |
| 184 | |
| 185 | uint256 manualPercentDelta = manualDelta * 1e18 / absB; |
| 186 | uint256 percentDelta = stdMath.percentDelta(a, b); |
| 187 | |
| 188 | assertEq(percentDelta, manualPercentDelta); |
| 189 | } |
| 190 | |
| 191 | /*////////////////////////////////////////////////////////////////////////// |
| 192 | HELPERS |
| 193 | //////////////////////////////////////////////////////////////////////////*/ |
| 194 | |
| 195 | function getAbs(int256 a) private pure returns (uint256) { |
| 196 | if (a < 0) { |
| 197 | return a == type(int256).min ? uint256(type(int256).max) + 1 : uint256(-a); |
| 198 | } |
| 199 | |
| 200 | return uint256(a); |
| 201 | } |
| 202 | } |