@@ -125,6 +125,17 @@ object Test {
125125    def  withLegacyShrinking (b : Boolean ):  Parameters  = 
126126      cpy(useLegacyShrinking0 =  b)
127127
128+     /**  Maximum number of spins of the RNG to perform between checks. 
129+      *  Greater values will reduce reuse of values (with dimimishing returns) 
130+      *  for a given number of arguments to Prop.forAll tests.  Greater values 
131+      *  will also generally lead to slower tests, so be careful. 
132+      */  
133+     val  maxRNGSpins :  Int  =  1 
134+ 
135+     /**  Set maximum RNG spins between checks */  
136+     def  withMaxRNGSpins (n : Int ):  Parameters  = 
137+       cpy(maxRNGSpins0 =  n)
138+ 
128139    override  def  toString :  String  =  {
129140      val  sb  =  new  StringBuilder 
130141      sb.append(" Parameters(" 
@@ -137,7 +148,8 @@ object Test {
137148      sb.append(s " customClassLoader= $customClassLoader,  " )
138149      sb.append(s " propFilter= $propFilter,  " )
139150      sb.append(s " initialSeed= $initialSeed,  " )
140-       sb.append(s " useLegacyShrinking= $useLegacyShrinking) " )
151+       sb.append(s " useLegacyShrinking= $useLegacyShrinking,  " )
152+       sb.append(s " maxRNGSpins= $maxRNGSpins) " )
141153      sb.toString
142154    }
143155
@@ -152,7 +164,8 @@ object Test {
152164      customClassLoader0 : Option [ClassLoader ] =  outer.customClassLoader,
153165      propFilter0 : Option [String ] =  outer.propFilter,
154166      initialSeed0 : Option [rng.Seed ] =  outer.initialSeed,
155-       useLegacyShrinking0 : Boolean  =  outer.useLegacyShrinking
167+       useLegacyShrinking0 : Boolean  =  outer.useLegacyShrinking,
168+       maxRNGSpins0 : Int  =  outer.maxRNGSpins
156169    ):  Parameters  = 
157170      new  Parameters  {
158171        val  minSuccessfulTests :  Int  =  minSuccessfulTests0
@@ -165,6 +178,7 @@ object Test {
165178        val  propFilter :  Option [String ] =  propFilter0
166179        val  initialSeed :  Option [rng.Seed ] =  initialSeed0
167180        override  val  useLegacyShrinking :  Boolean  =  useLegacyShrinking0
181+         override  val  maxRNGSpins :  Int  =  maxRNGSpins0
168182      }
169183
170184    //  no longer used, but preserved for binary compatibility
@@ -342,10 +356,17 @@ object Test {
342356      val  help  =  " Disable legacy shrinking using Shrink instances" 
343357    }
344358
359+     object  OptMaxRNGSpins  extends  IntOpt  {
360+       val  default  =  1 
361+       val  names  =  Set (" maxRNGSpins" 
362+       val  help  =  " Maximum number of RNG spins to perform between checks" 
363+     }
364+ 
345365    val  opts  =  Set [Opt [_]](
346366      OptMinSuccess , OptMaxDiscardRatio , OptMinSize ,
347367      OptMaxSize , OptWorkers , OptVerbosity ,
348-       OptPropFilter , OptInitialSeed , OptDisableLegacyShrinking 
368+       OptPropFilter , OptInitialSeed , OptDisableLegacyShrinking ,
369+       OptMaxRNGSpins 
349370    )
350371
351372    def  parseParams (args : Array [String ]):  (Parameters  =>  Parameters , List [String ]) =  {
@@ -369,6 +390,7 @@ object Test {
369390        }
370391
371392      val  useLegacyShrinking0 :  Boolean  =  ! optMap(OptDisableLegacyShrinking )
393+       val  maxRNGSpins :  Int  =  optMap(OptMaxRNGSpins )
372394      val  params  =  { (p : Parameters ) => 
373395        p.withMinSuccessfulTests(minSuccess0)
374396          .withMinSize(minSize0)
@@ -379,6 +401,7 @@ object Test {
379401          .withPropFilter(propFilter0)
380402          .withInitialSeed(initialSeed0)
381403          .withLegacyShrinking(useLegacyShrinking0)
404+           .withMaxRNGSpins(maxRNGSpins)
382405      }
383406      (params, us)
384407    }
@@ -405,6 +428,7 @@ object Test {
405428
406429    val  iterations  =  Math .ceil(params.minSuccessfulTests /  params.workers.toDouble)
407430    val  sizeStep  =  (params.maxSize -  params.minSize) /  (iterations *  params.workers)
431+     val  maxSpinsBetween  =  params.maxRNGSpins.max(1 )
408432    var  stop  =  false 
409433
410434    def  workerFun (workerIdx : Int ):  Result  =  {
@@ -420,6 +444,22 @@ object Test {
420444        if  (workerIdx ==  0 ) seed0 else  seed0.reseed(workerIdx.toLong)
421445      }
422446
447+       val  spinner :  () =>  Unit  = 
448+         if  (maxSpinsBetween >  1 ) {
449+           () =>  {
450+             var  slides  =  1  +  ((n +  d) %  maxSpinsBetween)
451+ 
452+             while  (slides >  0 ) {
453+               seed =  seed.slide
454+               slides -=  1 
455+             }
456+           }
457+         } else  {
458+           () =>  {
459+             seed =  seed.slide
460+           }
461+         }
462+ 
423463      while (! stop &&  res ==  null  &&  n <  iterations) {
424464
425465        val  count  =  workerIdx +  (params.workers *  (n +  d))
@@ -429,7 +469,7 @@ object Test {
429469          .withInitialSeed(Some (seed))
430470          .withSize(size.round.toInt)
431471
432-         seed  =  seed.slide 
472+         spinner() 
433473
434474        val  propRes  =  p(genPrms)
435475        if  (propRes.collected.nonEmpty) {
0 commit comments