Skip to content

Commit 2189f6f

Browse files
authored
✨ Add exponentialWad to LibPRNG (ethereum#836)
1 parent 5a7f17a commit 2189f6f

File tree

3 files changed

+96
-30
lines changed

3 files changed

+96
-30
lines changed

.gas-snapshot

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -649,16 +649,17 @@ LibMapTest:testUint8MapSetAndGet() (gas: 883454)
649649
LibMapTest:testUint8MapSetAndGet(uint256) (runs: 256, μ: 59152, ~: 59103)
650650
LibMapTest:testUint8MapSetAndGet2(uint256) (runs: 256, μ: 67845, ~: 69101)
651651
LibMapTest:test__codesize() (gas: 13187)
652-
LibPRNGTest:testLCGGas() (gas: 20802)
652+
LibPRNGTest:testExponentialWad() (gas: 4268529)
653+
LibPRNGTest:testLCGGas() (gas: 20736)
653654
LibPRNGTest:testPRNGGas() (gas: 25645)
654-
LibPRNGTest:testPRNGNext() (gas: 16162)
655-
LibPRNGTest:testPRNGShuffle() (gas: 504485)
655+
LibPRNGTest:testPRNGNext() (gas: 16184)
656+
LibPRNGTest:testPRNGShuffle() (gas: 504507)
656657
LibPRNGTest:testPRNGShuffleBytes() (gas: 222095)
657658
LibPRNGTest:testPRNGShuffleBytesGas() (gas: 1322452)
658659
LibPRNGTest:testPRNGShuffleGas() (gas: 1610949)
659660
LibPRNGTest:testPRNGUniform() (gas: 559341)
660-
LibPRNGTest:testStandardNormalWad() (gas: 4254566)
661-
LibPRNGTest:test__codesize() (gas: 5879)
661+
LibPRNGTest:testStandardNormalWad() (gas: 4260605)
662+
LibPRNGTest:test__codesize() (gas: 6473)
662663
LibRLPTest:testComputeAddressDifferential(address,uint256) (runs: 256, μ: 1913, ~: 1633)
663664
LibRLPTest:testComputeAddressForLargeNonces() (gas: 1771)
664665
LibRLPTest:testComputeAddressForSmallNonces() (gas: 967)
@@ -858,29 +859,29 @@ MetadataReaderLibTest:testReadStringTruncated(uint256) (runs: 256, μ: 799541, ~
858859
MetadataReaderLibTest:testReadUint() (gas: 911216)
859860
MetadataReaderLibTest:testReadUint(uint256) (runs: 256, μ: 46033, ~: 47093)
860861
MetadataReaderLibTest:test__codesize() (gas: 9186)
861-
MinHeapLibTest:testHeapEnqueue(uint256) (runs: 256, μ: 188312, ~: 192351)
862-
MinHeapLibTest:testHeapEnqueue2(uint256) (runs: 256, μ: 628359, ~: 566381)
862+
MinHeapLibTest:testHeapEnqueue(uint256) (runs: 256, μ: 185713, ~: 187563)
863+
MinHeapLibTest:testHeapEnqueue2(uint256) (runs: 256, μ: 648386, ~: 573268)
863864
MinHeapLibTest:testHeapEnqueueGas() (gas: 854801)
864-
MinHeapLibTest:testHeapEnqueueZeroMaxLengthReverts(uint256) (runs: 256, μ: 47693, ~: 28615)
865-
MinHeapLibTest:testHeapPSiftTrick(uint256,uint256,uint256) (runs: 256, μ: 716, ~: 877)
866-
MinHeapLibTest:testHeapPushAndPop(uint256) (runs: 256, μ: 107245, ~: 99564)
867-
MinHeapLibTest:testHeapPushPop(uint256) (runs: 256, μ: 245886, ~: 244732)
868-
MinHeapLibTest:testHeapReplace(uint256) (runs: 256, μ: 293870, ~: 302883)
869-
MinHeapLibTest:testHeapReplaceOrPopEmptyHeapReverts(uint256) (runs: 256, μ: 47585, ~: 50916)
870-
MinHeapLibTest:testHeapRoot(uint256) (runs: 256, μ: 48520, ~: 47811)
871-
MinHeapLibTest:testHeapSmallest(uint256) (runs: 256, μ: 1703864, ~: 1271598)
865+
MinHeapLibTest:testHeapEnqueueZeroMaxLengthReverts(uint256) (runs: 256, μ: 48157, ~: 48176)
866+
MinHeapLibTest:testHeapPSiftTrick(uint256,uint256,uint256) (runs: 256, μ: 711, ~: 708)
867+
MinHeapLibTest:testHeapPushAndPop(uint256) (runs: 256, μ: 103034, ~: 99564)
868+
MinHeapLibTest:testHeapPushPop(uint256) (runs: 256, μ: 246900, ~: 245280)
869+
MinHeapLibTest:testHeapReplace(uint256) (runs: 256, μ: 298263, ~: 315174)
870+
MinHeapLibTest:testHeapReplaceOrPopEmptyHeapReverts(uint256) (runs: 256, μ: 46295, ~: 50906)
871+
MinHeapLibTest:testHeapRoot(uint256) (runs: 256, μ: 49603, ~: 67523)
872+
MinHeapLibTest:testHeapSmallest(uint256) (runs: 256, μ: 1666715, ~: 1271598)
872873
MinHeapLibTest:testHeapSmallestGas() (gas: 50027269)
873-
MinHeapLibTest:testMemHeapEnqueue(uint256) (runs: 256, μ: 857848, ~: 823530)
874-
MinHeapLibTest:testMemHeapEnqueue2(uint256) (runs: 256, μ: 793762, ~: 741115)
874+
MinHeapLibTest:testMemHeapEnqueue(uint256) (runs: 256, μ: 897229, ~: 824239)
875+
MinHeapLibTest:testMemHeapEnqueue2(uint256) (runs: 256, μ: 803087, ~: 735967)
875876
MinHeapLibTest:testMemHeapEnqueueGas() (gas: 233981)
876-
MinHeapLibTest:testMemHeapEnqueueZeroMaxLengthReverts(uint256) (runs: 256, μ: 25191, ~: 24159)
877-
MinHeapLibTest:testMemHeapPushAndPop(uint256) (runs: 256, μ: 824109, ~: 748166)
877+
MinHeapLibTest:testMemHeapEnqueueZeroMaxLengthReverts(uint256) (runs: 256, μ: 25211, ~: 24170)
878+
MinHeapLibTest:testMemHeapPushAndPop(uint256) (runs: 256, μ: 795803, ~: 743874)
878879
MinHeapLibTest:testMemHeapPushGas() (gas: 31982)
879-
MinHeapLibTest:testMemHeapPushPop(uint256) (runs: 256, μ: 1028744, ~: 775102)
880-
MinHeapLibTest:testMemHeapReplace(uint256) (runs: 256, μ: 879282, ~: 824296)
881-
MinHeapLibTest:testMemHeapReplaceOrPopEmptyHeapReverts(uint256) (runs: 256, μ: 25909, ~: 25517)
882-
MinHeapLibTest:testMemHeapRoot(uint256) (runs: 256, μ: 694847, ~: 644571)
883-
MinHeapLibTest:testMemHeapSmallest(uint256) (runs: 256, μ: 835597, ~: 744766)
880+
MinHeapLibTest:testMemHeapPushPop(uint256) (runs: 256, μ: 1061534, ~: 775073)
881+
MinHeapLibTest:testMemHeapReplace(uint256) (runs: 256, μ: 895879, ~: 845419)
882+
MinHeapLibTest:testMemHeapReplaceOrPopEmptyHeapReverts(uint256) (runs: 256, μ: 25898, ~: 25371)
883+
MinHeapLibTest:testMemHeapRoot(uint256) (runs: 256, μ: 720351, ~: 644424)
884+
MinHeapLibTest:testMemHeapSmallest(uint256) (runs: 256, μ: 807207, ~: 744317)
884885
MinHeapLibTest:testMemHeapSmallestGas() (gas: 2871518)
885886
MinHeapLibTest:test__codesize() (gas: 13708)
886887
MulticallableTest:testMulticallableBenchmark() (gas: 29588)
@@ -948,8 +949,8 @@ ReceiverTest:testOnERC721Received() (gas: 64127)
948949
ReceiverTest:test__codesize() (gas: 3310)
949950
RedBlackTreeLibTest:testRedBlackTreeBenchUint160() (gas: 3429423)
950951
RedBlackTreeLibTest:testRedBlackTreeBenchUint256() (gas: 5843130)
951-
RedBlackTreeLibTest:testRedBlackTreeInsertAndRemove(uint256) (runs: 256, μ: 627514, ~: 487609)
952-
RedBlackTreeLibTest:testRedBlackTreeInsertAndRemove2(uint256) (runs: 256, μ: 420920, ~: 390160)
952+
RedBlackTreeLibTest:testRedBlackTreeInsertAndRemove(uint256) (runs: 256, μ: 624653, ~: 478269)
953+
RedBlackTreeLibTest:testRedBlackTreeInsertAndRemove2(uint256) (runs: 256, μ: 407187, ~: 348916)
953954
RedBlackTreeLibTest:testRedBlackTreeInsertAndRemove3() (gas: 21546528)
954955
RedBlackTreeLibTest:testRedBlackTreeInsertBenchStep() (gas: 3703676)
955956
RedBlackTreeLibTest:testRedBlackTreeInsertBenchUint160() (gas: 3469294)
@@ -958,16 +959,16 @@ RedBlackTreeLibTest:testRedBlackTreeInsertOneGas() (gas: 45564)
958959
RedBlackTreeLibTest:testRedBlackTreeInsertTenGas() (gas: 282751)
959960
RedBlackTreeLibTest:testRedBlackTreeInsertThreeGas() (gas: 96193)
960961
RedBlackTreeLibTest:testRedBlackTreeInsertTwoGas() (gas: 69797)
961-
RedBlackTreeLibTest:testRedBlackTreeNearest(uint256) (runs: 256, μ: 228165, ~: 224911)
962-
RedBlackTreeLibTest:testRedBlackTreeNearestAfter(uint256) (runs: 256, μ: 251011, ~: 248814)
963-
RedBlackTreeLibTest:testRedBlackTreeNearestBefore(uint256) (runs: 256, μ: 232084, ~: 195909)
962+
RedBlackTreeLibTest:testRedBlackTreeNearest(uint256) (runs: 256, μ: 234562, ~: 224911)
963+
RedBlackTreeLibTest:testRedBlackTreeNearestAfter(uint256) (runs: 256, μ: 239070, ~: 225794)
964+
RedBlackTreeLibTest:testRedBlackTreeNearestBefore(uint256) (runs: 256, μ: 224829, ~: 195447)
964965
RedBlackTreeLibTest:testRedBlackTreePointers() (gas: 91897)
965966
RedBlackTreeLibTest:testRedBlackTreeRejectsEmptyValue() (gas: 3238)
966967
RedBlackTreeLibTest:testRedBlackTreeRemoveViaPointer() (gas: 58172)
967968
RedBlackTreeLibTest:testRedBlackTreeTreeFullReverts() (gas: 50335)
968969
RedBlackTreeLibTest:testRedBlackTreeTryInsertAndRemove() (gas: 56139)
969970
RedBlackTreeLibTest:testRedBlackTreeValues() (gas: 191599)
970-
RedBlackTreeLibTest:testRedBlackTreeValues(uint256) (runs: 256, μ: 383999, ~: 267070)
971+
RedBlackTreeLibTest:testRedBlackTreeValues(uint256) (runs: 256, μ: 392283, ~: 267070)
971972
RedBlackTreeLibTest:test__codesize() (gas: 14087)
972973
ReentrancyGuardTest:testRecursiveDirectUnguardedCall() (gas: 34254)
973974
ReentrancyGuardTest:testRecursiveIndirectUnguardedCall() (gas: 47771)

src/utils/LibPRNG.sol

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,37 @@ library LibPRNG {
175175
shr(192, mul(s, and(m, mulmod(r3, a, n))))))), 7745966692414833770)
176176
}
177177
}
178+
179+
/// @dev Returns a sample from the unit exponential distribution denominated in `WAD`.
180+
function exponentialWad(PRNG memory prng) internal pure returns (uint256 result) {
181+
/// @solidity memory-safe-assembly
182+
assembly {
183+
let n := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff43 // Prime.
184+
let a := 0x100000000000000000000000000000051 // Prime and a primitive root of `n`.
185+
let m := 0x7fffffffffffffffffffffffffffffff
186+
let r := keccak256(prng, 0x20)
187+
mstore(prng, r)
188+
let p := and(m, r)
189+
let w := and(m, shr(127, r))
190+
if iszero(gt(w, p)) {
191+
// Passes the Kolmogorov-Smirnov test for 200k samples.
192+
// Gas usage varies, starting from about 199+ gas.
193+
for {} 1 {} {
194+
r := mulmod(r, a, n)
195+
let v := and(m, r)
196+
if iszero(lt(v, w)) {
197+
r := mulmod(r, a, n)
198+
result := add(result, 1000000000000000000)
199+
w := and(m, shr(127, r))
200+
p := and(m, r)
201+
if iszero(lt(w, p)) { break }
202+
continue
203+
}
204+
w := and(m, shr(127, r))
205+
if iszero(lt(w, v)) { break }
206+
}
207+
}
208+
result := add(result, div(p, 170141183460469231732))
209+
}
210+
}
178211
}

test/LibPRNG.t.sol

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,4 +201,36 @@ contract LibPRNGTest is SoladyTest {
201201
assertLt(FixedPointMathLib.abs(sd - wad), uint256(wad / 8));
202202
}
203203
}
204+
205+
function testExponentialWad() public {
206+
LibPRNG.PRNG memory prng;
207+
unchecked {
208+
uint256 n = 1000;
209+
int256 oldM;
210+
int256 newM;
211+
int256 oldS;
212+
int256 newS;
213+
for (uint256 i; i != n;) {
214+
uint256 gasBefore = gasleft();
215+
int256 x = int256(prng.exponentialWad());
216+
uint256 gasUsed = gasBefore - gasleft();
217+
if (++i == 1) {
218+
oldM = (newM = x);
219+
} else {
220+
newM = oldM + (x - oldM) / int256(i);
221+
newS = oldS + (x - oldM) * (x - newM);
222+
oldM = newM;
223+
oldS = newS;
224+
}
225+
emit LogInt("exponentialWad", x);
226+
emit LogUint("gasUsed", gasUsed);
227+
}
228+
int256 wad = int256(FixedPointMathLib.WAD);
229+
emit LogInt("mean", newM);
230+
int256 sd = int256(FixedPointMathLib.sqrt(uint256(newS / int256(n - 1))));
231+
assertLt(FixedPointMathLib.abs(newM - wad), uint256(wad / 8));
232+
emit LogInt("standard deviation", sd);
233+
assertLt(FixedPointMathLib.abs(sd - wad), uint256(wad / 8));
234+
}
235+
}
204236
}

0 commit comments

Comments
 (0)