Update docs

This commit is contained in:
github-actions
2025-04-22 16:39:54 +00:00
parent 0dda004024
commit da7fd0d3e5
230 changed files with 11375 additions and 1714 deletions

View File

@ -7,18 +7,60 @@ import {Test, stdError} from "forge-std/Test.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
contract MathTest is Test {
function testSymbolicTernary(bool f, uint256 a, uint256 b) public {
function testSymbolicTernary(bool f, uint256 a, uint256 b) public pure {
assertEq(Math.ternary(f, a, b), f ? a : b);
}
// ADD512 & MUL512
function testAdd512(uint256 a, uint256 b) public pure {
(uint256 high, uint256 low) = Math.add512(a, b);
// test against tryAdd
(bool success, uint256 result) = Math.tryAdd(a, b);
if (success) {
assertEq(high, 0);
assertEq(low, result);
} else {
assertEq(high, 1);
}
// test against unchecked
unchecked {
assertEq(low, a + b); // unchecked allow overflow
}
}
function testMul512(uint256 a, uint256 b) public pure {
(uint256 high, uint256 low) = Math.mul512(a, b);
// test against tryMul
(bool success, uint256 result) = Math.tryMul(a, b);
if (success) {
assertEq(high, 0);
assertEq(low, result);
} else {
assertGt(high, 0);
}
// test against unchecked
unchecked {
assertEq(low, a * b); // unchecked allow overflow
}
// test against alternative method
(uint256 _high, uint256 _low) = _mulKaratsuba(a, b);
assertEq(high, _high);
assertEq(low, _low);
}
// MIN & MAX
function testSymbolicMinMax(uint256 a, uint256 b) public {
function testSymbolicMinMax(uint256 a, uint256 b) public pure {
assertEq(Math.min(a, b), a < b ? a : b);
assertEq(Math.max(a, b), a > b ? a : b);
}
// CEILDIV
function testCeilDiv(uint256 a, uint256 b) public {
function testCeilDiv(uint256 a, uint256 b) public pure {
vm.assume(b > 0);
uint256 result = Math.ceilDiv(a, b);
@ -35,7 +77,7 @@ contract MathTest is Test {
}
// SQRT
function testSqrt(uint256 input, uint8 r) public {
function testSqrt(uint256 input, uint8 r) public pure {
Math.Rounding rounding = _asRounding(r);
uint256 result = Math.sqrt(input, rounding);
@ -66,31 +108,31 @@ contract MathTest is Test {
}
// INV
function testInvMod(uint256 value, uint256 p) public {
function testInvMod(uint256 value, uint256 p) public pure {
_testInvMod(value, p, true);
}
function testInvMod2(uint256 seed) public {
function testInvMod2(uint256 seed) public pure {
uint256 p = 2; // prime
_testInvMod(bound(seed, 1, p - 1), p, false);
}
function testInvMod17(uint256 seed) public {
function testInvMod17(uint256 seed) public pure {
uint256 p = 17; // prime
_testInvMod(bound(seed, 1, p - 1), p, false);
}
function testInvMod65537(uint256 seed) public {
function testInvMod65537(uint256 seed) public pure {
uint256 p = 65537; // prime
_testInvMod(bound(seed, 1, p - 1), p, false);
}
function testInvModP256(uint256 seed) public {
function testInvModP256(uint256 seed) public pure {
uint256 p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff; // prime
_testInvMod(bound(seed, 1, p - 1), p, false);
}
function _testInvMod(uint256 value, uint256 p, bool allowZero) private {
function _testInvMod(uint256 value, uint256 p, bool allowZero) private pure {
uint256 inverse = Math.invMod(value, p);
if (inverse != 0) {
assertEq(mulmod(value, inverse, p), 1);
@ -101,7 +143,7 @@ contract MathTest is Test {
}
// LOG2
function testLog2(uint256 input, uint8 r) public {
function testLog2(uint256 input, uint8 r) public pure {
Math.Rounding rounding = _asRounding(r);
uint256 result = Math.log2(input, rounding);
@ -128,7 +170,7 @@ contract MathTest is Test {
}
// LOG10
function testLog10(uint256 input, uint8 r) public {
function testLog10(uint256 input, uint8 r) public pure {
Math.Rounding rounding = _asRounding(r);
uint256 result = Math.log10(input, rounding);
@ -155,7 +197,7 @@ contract MathTest is Test {
}
// LOG256
function testLog256(uint256 input, uint8 r) public {
function testLog256(uint256 input, uint8 r) public pure {
Math.Rounding rounding = _asRounding(r);
uint256 result = Math.log256(input, rounding);
@ -182,9 +224,9 @@ contract MathTest is Test {
}
// MULDIV
function testMulDiv(uint256 x, uint256 y, uint256 d) public {
function testMulDiv(uint256 x, uint256 y, uint256 d) public pure {
// Full precision for x * y
(uint256 xyHi, uint256 xyLo) = _mulHighLow(x, y);
(uint256 xyHi, uint256 xyLo) = Math.mul512(x, y);
// Assume result won't overflow (see {testMulDivDomain})
// This also checks that `d` is positive
@ -194,9 +236,9 @@ contract MathTest is Test {
uint256 q = Math.mulDiv(x, y, d);
// Full precision for q * d
(uint256 qdHi, uint256 qdLo) = _mulHighLow(q, d);
(uint256 qdHi, uint256 qdLo) = Math.mul512(q, d);
// Add remainder of x * y / d (computed as rem = (x * y % d))
(uint256 qdRemLo, uint256 c) = _addCarry(qdLo, mulmod(x, y, d));
(uint256 c, uint256 qdRemLo) = Math.add512(qdLo, mulmod(x, y, d));
uint256 qdRemHi = qdHi + c;
// Full precision check that x * y = q * d + rem
@ -204,8 +246,9 @@ contract MathTest is Test {
assertEq(xyLo, qdRemLo);
}
/// forge-config: default.allow_internal_expect_revert = true
function testMulDivDomain(uint256 x, uint256 y, uint256 d) public {
(uint256 xyHi, ) = _mulHighLow(x, y);
(uint256 xyHi, ) = Math.mul512(x, y);
// Violate {testMulDiv} assumption (covers d is 0 and result overflow)
vm.assume(xyHi >= d);
@ -216,6 +259,7 @@ contract MathTest is Test {
}
// MOD EXP
/// forge-config: default.allow_internal_expect_revert = true
function testModExp(uint256 b, uint256 e, uint256 m) public {
if (m == 0) {
vm.expectRevert(stdError.divisionError);
@ -225,7 +269,7 @@ contract MathTest is Test {
assertEq(result, _nativeModExp(b, e, m));
}
function testTryModExp(uint256 b, uint256 e, uint256 m) public {
function testTryModExp(uint256 b, uint256 e, uint256 m) public view {
(bool success, uint256 result) = Math.tryModExp(b, e, m);
assertEq(success, m != 0);
if (success) {
@ -236,6 +280,7 @@ contract MathTest is Test {
}
}
/// forge-config: default.allow_internal_expect_revert = true
function testModExpMemory(uint256 b, uint256 e, uint256 m) public {
if (m == 0) {
vm.expectRevert(stdError.divisionError);
@ -247,7 +292,7 @@ contract MathTest is Test {
assertEq(res, _nativeModExp(b, e, m));
}
function testTryModExpMemory(uint256 b, uint256 e, uint256 m) public {
function testTryModExpMemory(uint256 b, uint256 e, uint256 m) public view {
(bool success, bytes memory result) = Math.tryModExp(
abi.encodePacked(b),
abi.encodePacked(e),
@ -263,26 +308,13 @@ contract MathTest is Test {
}
}
function _nativeModExp(uint256 b, uint256 e, uint256 m) private pure returns (uint256) {
if (m == 1) return 0;
uint256 r = 1;
while (e > 0) {
if (e % 2 > 0) {
r = mulmod(r, b, m);
}
b = mulmod(b, b, m);
e >>= 1;
}
return r;
}
// Helpers
function _asRounding(uint8 r) private pure returns (Math.Rounding) {
vm.assume(r < uint8(type(Math.Rounding).max));
return Math.Rounding(r);
}
function _mulHighLow(uint256 x, uint256 y) private pure returns (uint256 high, uint256 low) {
function _mulKaratsuba(uint256 x, uint256 y) private pure returns (uint256 high, uint256 low) {
(uint256 x0, uint256 x1) = (x & type(uint128).max, x >> 128);
(uint256 y0, uint256 y1) = (y & type(uint128).max, y >> 128);
@ -302,10 +334,16 @@ contract MathTest is Test {
}
}
function _addCarry(uint256 x, uint256 y) private pure returns (uint256 res, uint256 carry) {
unchecked {
res = x + y;
function _nativeModExp(uint256 b, uint256 e, uint256 m) private pure returns (uint256) {
if (m == 1) return 0;
uint256 r = 1;
while (e > 0) {
if (e % 2 > 0) {
r = mulmod(r, b, m);
}
b = mulmod(b, b, m);
e >>= 1;
}
carry = res < x ? 1 : 0;
return r;
}
}