diff --git a/jvm/src/test/scala/org/scalacheck/GenSpecification.scala b/jvm/src/test/scala/org/scalacheck/GenSpecification.scala index 907b9fd7e..5a0308734 100644 --- a/jvm/src/test/scala/org/scalacheck/GenSpecification.scala +++ b/jvm/src/test/scala/org/scalacheck/GenSpecification.scala @@ -124,6 +124,25 @@ object GenSpecification extends Properties("Gen") with GenSpecificationVersionSp } } + property("choose-big-int") = forAll( + nonEmptyContainerOf[Array, Byte](arbitrary[Byte]).map(BigInt(_)), + nonEmptyContainerOf[Array, Byte](arbitrary[Byte]).map(BigInt(_)) + ) { (l: BigInt, h: BigInt) => + Try(choose(l, h)) match { + case Success(g) => forAll(g) { x => l <= x && x <= h } + case Failure(e: Choose.IllegalBoundsError[_]) => Prop(l > h) + case Failure(e) => throw e + } + } + + property("Gen.choose(BigInt( 2^(2^18 - 1)), BigInt(-2^(2^18 - 1)))") = { + val (l, h) = (BigInt(-2).pow(262143), + BigInt( 2).pow(262143)) + Prop.forAllNoShrink(Gen.choose(l, h)) { x => + l <= x && x <= h + } + } + property("choose-xmap") = { implicit val chooseDate: Choose[Date] = Choose.xmap[Long, Date](new Date(_), _.getTime) diff --git a/src/main/scala/org/scalacheck/Gen.scala b/src/main/scala/org/scalacheck/Gen.scala index eea9757f4..f5126184f 100644 --- a/src/main/scala/org/scalacheck/Gen.scala +++ b/src/main/scala/org/scalacheck/Gen.scala @@ -23,6 +23,7 @@ import scala.collection.mutable.ArrayBuffer import scala.concurrent.duration.{Duration, FiniteDuration} import java.util.{ Calendar, UUID } +import java.nio.ByteBuffer sealed abstract class Gen[+T] extends Serializable { self => @@ -454,6 +455,24 @@ object Gen extends GenArities with GenVersionSpecific { implicit val chooseFiniteDuration: Choose[FiniteDuration] = Choose.xmap[Long, FiniteDuration](Duration.fromNanos, _.toNanos) + implicit object chooseBigInt extends Choose[BigInt] { + def choose(low: BigInt, high: BigInt): Gen[BigInt] = + if (low > high) throw new IllegalBoundsError(low, high) + else if (low == high) low + else { + val range = high - low + Gen.containerOfN[Array, Long]( + (range.bitLength + 64 - 1) / 64, + Gen.choose(Long.MinValue, Long.MaxValue) + ).map(longs => { + longs(0) = longs(0) & (-1L >>> (64 - range.bitLength % 64)) + val bb = ByteBuffer.allocate(longs.length * 8) + longs.foreach(bb.putLong) + BigInt(1, bb.array()) + low + }).filter(n => high >= n) + } + } + /** Transform a Choose[T] to a Choose[U] where T and U are two isomorphic * types whose relationship is described by the provided transformation * functions. (exponential functor map) */