@@ -571,11 +571,14 @@ contract LibPRNGTest is SoladyTest {
571571 function testLazyShufflerRevertsOnGrowWithInvalidLength (uint256 n , uint256 nGrow ) public {
572572 n = _bound (n, 1 , 2 ** 32 - 2 );
573573 this .lazyShufflerInitialize (n);
574- nGrow = _bound (n, 0 , 2 ** 32 - 2 );
575- if (nGrow < n) {
574+ nGrow = _bound (nGrow, 0 , 2 ** 32 - 2 );
575+ uint256 limit = n > 65534 ? 2 ** 32 - 2 : 65534 ;
576+ bool reverts = nGrow < n || nGrow > limit;
577+ if (reverts) {
576578 vm.expectRevert (LibPRNG.InvalidNewLazyShufflerLength.selector );
577579 }
578- this .lazyShufflerGrow (n);
580+ this .lazyShufflerGrow (nGrow);
581+ assertEq (_lazyShuffler0.length (), reverts ? n : nGrow);
579582 }
580583
581584 function testLazyShufflerRevertsOnDoubleInit () public {
@@ -620,4 +623,63 @@ contract LibPRNGTest is SoladyTest {
620623 function lazyShuffler1Get (uint256 i ) public view returns (uint256 ) {
621624 return _lazyShuffler1.get (i);
622625 }
626+
627+ function testLazyShufflerRevertsOnGrowAcrossWidthBoundary () public {
628+ _lazyShuffler0.initialize (2 );
629+ _lazyShuffler0.next (0 );
630+ vm.expectRevert (LibPRNG.InvalidNewLazyShufflerLength.selector );
631+ this .lazyShufflerGrow (65535 );
632+ }
633+
634+ function testLazyShufflerRevertsOnGrowAcrossWidthBoundaryUndrawn () public {
635+ _lazyShuffler0.initialize (2 );
636+ vm.expectRevert (LibPRNG.InvalidNewLazyShufflerLength.selector );
637+ this .lazyShufflerGrow (65535 );
638+ }
639+
640+ // `grow` had no upper bound check, so the length silently truncated to zero.
641+ function testLazyShufflerRevertsOnGrowOutOfRange (uint256 n ) public {
642+ _lazyShuffler0.initialize (10 );
643+ n = _bound (n, 2 ** 32 - 1 , type (uint256 ).max);
644+ vm.expectRevert (LibPRNG.InvalidNewLazyShufflerLength.selector );
645+ this .lazyShufflerGrow (n);
646+ assertEq (_lazyShuffler0.length (), 10 );
647+ }
648+
649+ function testLazyShufflerGrowWithinSameWidth () public {
650+ _lazyShuffler0.initialize (2 );
651+ uint256 first = _lazyShuffler0.next (0 );
652+ _lazyShuffler0.grow (1000 );
653+ assertEq (_lazyShuffler0.get (0 ), first);
654+ assertLt (_lazyShuffler0.get (0 ), 1000 );
655+ }
656+
657+ function testLazyShufflerGrowWithinWideWidth () public {
658+ _lazyShuffler0.initialize (70000 );
659+ uint256 first = _lazyShuffler0.next (0 );
660+ _lazyShuffler0.grow (200000 );
661+ assertEq (_lazyShuffler0.get (0 ), first);
662+ assertLt (_lazyShuffler0.get (0 ), 200000 );
663+ }
664+
665+ // A 32-bit shuffler still draws each value at most once across a grow.
666+ function testLazyShufflerWideProducesNoDuplicatesAcrossGrow () public {
667+ _lazyShuffler0.initialize (65535 );
668+ uint256 [] memory seen = new uint256 [](8 );
669+ unchecked {
670+ for (uint256 i; i != 4 ; ++ i) {
671+ seen[i] = _lazyShuffler0.next (_random ());
672+ }
673+ _lazyShuffler0.grow (65600 );
674+ for (uint256 i = 4 ; i != 8 ; ++ i) {
675+ seen[i] = _lazyShuffler0.next (_random ());
676+ }
677+ LibSort.sort (seen);
678+ LibSort.uniquifySorted (seen);
679+ assertEq (seen.length , 8 );
680+ for (uint256 i; i != 8 ; ++ i) {
681+ assertLt (seen[i], 65600 );
682+ }
683+ }
684+ }
623685}
0 commit comments