spire.random.Generator.scala Maven / Gradle / Ivy
The newest version!
package spire
package random
import spire.math.{UInt, ULong}
abstract class Generator {
protected var extra: Boolean = false
protected var value: Double = 0.0
def copy: Generator = {
val gen = copyInit
gen.extra = extra
gen.value = value
gen
}
protected[this] def copyInit: Generator
def sync: rng.SyncGenerator = new rng.SyncGenerator(copy)
def getSeedBytes(): Array[Byte]
def setSeedBytes(bytes: Array[Byte]): Unit
/**
* Generate an equally-distributed random Int.
*/
def nextInt(): Int
/**
* Generates a random long. All 64-bit long values are equally likely.
*/
def nextLong(): Long
/**
* Generate a random value using a Dist[A] type class instance.
*
* Implicit Dist[A] instances are provided for the AnyVal types as well as
* UByte through ULong. More complex Dist instances can be created from
* these.
*/
def next[A](implicit next: Dist[A]): A = next(this)
/**
* Generate an infinite iterator of random values using Dist[A].
*/
def iterator[A](implicit next: Dist[A]): Iterator[A] =
new DistIterator(next, this)
/**
* Generates a random integer using n bits of state (0 <= n <= 32).
*/
def nextBits(n: Int): Int = nextInt() >>> (32 - n)
/**
* Generates a random int between 0 (inclusive) and n (exclusive).
*/
def nextInt(n: Int): Int = {
@tailrec def loop(b: Int): Int = {
val v = b % n
if (b - v + (n - 1) < 0) loop(nextInt() >>> 1) else v
}
if (n < 1)
throw new IllegalArgumentException("argument must be positive %d" format n)
else if ((n & -n) == n)
((n * ((nextInt() >>> 1).toLong)) >>> 31).toInt
else
loop(nextInt() >>> 1)
}
private final def retryCap(width: UInt): UInt = {
// Simulate 1 << 32 long division.
val q = UInt(Int.MinValue) / width
val r = UInt(Int.MinValue) % width
val n = (q << 1) + (r << 1) / width
n * width
}
/**
* Return an Int in [from, to].
*/
def nextInt(from: Int, to: Int): Int = {
val width = UInt(to - from + 1)
if (width == UInt(0)) {
nextInt()
} else {
val cap = if (width > UInt(Int.MinValue)) width else retryCap(width)
if (cap == UInt(0)) {
val x = UInt(nextInt())
from + (x % width).signed
} else {
@tailrec def loop(): Int = {
val x = UInt(nextInt())
if (x <= cap) (x % width).signed + from else loop()
}
loop()
}
}
}
/**
* Generates a random int between 0 (inclusive) and n (exclusive).
*/
def nextLong(n: Long): Long = {
@tailrec def loop(b: Long): Long = {
val v = b % n
if (b - v + (n - 1) < 0) loop(nextLong() >>> 1) else v
}
if (n < 1)
throw new IllegalArgumentException("argument must be positive %d" format n)
else if ((n & -n) == n)
nextLong() & (n - 1)
else
loop(nextLong() >>> 1)
}
private final def retryCap(width: ULong): ULong = {
// Simulate 1 << 32 long division.
val q = ULong(Long.MinValue) / width
val r = ULong(Long.MinValue) % width
val n = (q << 1) + (r << 1) / width
n * width
}
/**
* Return an Long in [from, to].
*/
def nextLong(from: Long, to: Long): Long = {
val width = ULong(to - from + 1)
if (width == ULong(0)) {
nextLong()
} else {
val cap = if (width > ULong(Long.MinValue)) width else retryCap(width)
if (cap == ULong(0)) {
val x = ULong(nextLong())
from + (x % width).signed
} else {
@tailrec def loop(): Long = {
val x = ULong(nextLong())
if (x <= cap) (x % width).signed + from else loop()
}
loop()
}
}
}
/**
* Generates a random Boolean.
*/
def nextBoolean(): Boolean = (nextInt() & 1) != 0
/**
* Generates a random float in [0.0, 1.0).
*/
def nextFloat(): Float = (nextInt() >>> 8) * 5.9604645e-8f
/**
* Generates a random float in [0.0, n).
*/
def nextFloat(n: Float): Float = nextFloat() * n
/**
* Generates a random float in [from, until).
*/
def nextFloat(from: Float, until: Float): Float =
from + (until - from) * nextFloat()
/**
* Generates a random double in [0.0, 1.0).
*/
def nextDouble(): Double =
(nextLong() >>> 11) * 1.1102230246251565e-16
/**
* Generates a random double in [0.0, n).
*/
def nextDouble(n: Double): Double = nextDouble() * n
/**
* Generates a random double in [from, until).
*/
def nextDouble(from: Double, until: Double): Double =
from + (until - from) * nextDouble()
/**
* Generate an array of n random Longs.
*/
def generateLongs(n: Int): Array[Long] = {
val arr = new Array[Long](n)
fillLongs(arr)
arr
}
/**
* Fill an array with random Longs.
*/
def fillLongs(arr: Array[Long]): Unit = {
var i = 0
val len = arr.length
while (i < len) {
arr(i) = nextLong()
i += 1
}
}
/**
* Generate an array of n random Ints.
*/
def generateInts(n: Int): Array[Int] = {
val arr = new Array[Int](n)
fillInts(arr)
arr
}
/**
* Fill an array with random Ints.
*/
def fillInts(arr: Array[Int]): Unit = {
var i = 0
val len = arr.length
while (i < len) {
arr(i) = nextInt()
i += 1
}
}
/**
* Generate an array of n random Shorts.
*/
def generateShorts(n: Int): Array[Short] = {
val arr = new Array[Short](n)
fillShorts(arr)
arr
}
/**
* Fill an array with random Shorts.
*/
def fillShorts(arr: Array[Short]): Unit = {
var i = 0
val len = arr.length
val llen = len & 0xfffffffe
while (i < llen) {
val n = nextInt()
arr(i) = (n & 0xffff).toShort
arr(i + 1) = ((n >>> 16) & 0xffff).toShort
i += 2
}
if (len != llen) arr(i) = (nextInt() & 0xffff).toShort
}
/**
* Generate an array of n random Bytes.
*/
def generateBytes(n: Int): Array[Byte] = {
val arr = new Array[Byte](n)
fillBytes(arr)
arr
}
/**
* Fill an array with random Bytes.
*/
def fillBytes(arr: Array[Byte]): Unit = {
var i = 0
val len = arr.length
val llen = len & 0xfffffffc
while (i < llen) {
val n = nextInt()
arr(i) = (n & 0xff).toByte
arr(i + 1) = ((n >>> 8) & 0xff).toByte
arr(i + 2) = ((n >>> 16) & 0xff).toByte
arr(i + 3) = ((n >>> 24) & 0xff).toByte
i += 4
}
if (i < len) {
var n = nextInt()
while (i < len) {
arr(i) = (n & 0xff).toByte
n = n >>> 8
i += 1
}
}
}
/**
* Generate an Array[A] using the given Dist[A] instance.
*/
def generateArray[@sp A: Dist: ClassTag](n: Int): Array[A] = {
val arr = new Array[A](n)
fillArray(arr)
arr
}
/**
* Fill an Array[A] using the given Dist[A] instance.
*/
def fillArray[@sp A: Dist](arr: Array[A]): Unit = {
var i = 0
val len = arr.length
while (i < len) {
arr(i) = next[A]
i += 1
}
}
def oneOf[A](as: A*): A = chooseFromSeq(as)(this)
def chooseFromArray[@sp A](arr: Array[A])(implicit gen: Generator): A =
arr(gen.nextInt(arr.length))
def chooseFromSeq[A](seq: Seq[A])(implicit gen: Generator): A =
seq(gen.nextInt(seq.length))
def chooseFromIterable[A](as: Iterable[A])(implicit gen: Generator): A =
as.iterator.drop(gen.nextInt(as.size)).next()
def sampleFromArray[@sp A: ClassTag](as: Array[A], size: Int)(implicit gen: Generator): Array[A] = {
val chosen: Array[A] = new Array[A](size)
if (size < 1) {
throw new IllegalArgumentException("illegal sample size (%d)" format size)
} else if (size < as.length) {
var i = 0
while (i < as.length) {
if (i < size) {
chosen(i) = as(i)
} else {
val n: Int = gen.nextInt(i + 1)
if (n < size) chosen(n) = as(i)
}
i += 1
}
} else if (size == as.length) {
System.arraycopy(as, 0, chosen, 0, as.length)
shuffle(chosen)
} else {
throw new IllegalArgumentException("sample size (%d) exceeds input size (%d)" format (size, as.length))
}
chosen
}
def sampleFromTraversable[@sp A: ClassTag](as: Traversable[A], size: Int)(implicit gen: Generator): Array[A] = {
val chosen: Array[A] = new Array[A](size)
var i: Int = 0
as.foreach { a =>
if (i < size) {
chosen(i) = a
} else {
val n: Int = gen.nextInt(i + 1)
if (n < size) chosen(n) = a
}
i += 1
}
if (i < size) {
throw new IllegalArgumentException("sample size (%d) exceeds input size (%d)" format (size, i))
}
chosen
}
def shuffle[@sp A](as: Array[A])(implicit gen: Generator): Unit = {
var i: Int = as.length - 1
while (i > 0) {
val n: Int = gen.nextInt(i)
val tmp: A = as(i)
as(i) = as(n)
as(n) = tmp
i -= 1
}
}
def nextGaussian(): Double = if (extra) {
extra = false
value
} else {
@tailrec def loop(x: Double, y: Double): Double = {
val s = x * x + y * y
if (s >= 1.0 || s == 0.0) {
loop(nextDouble() * 2 - 1, nextDouble() * 2 - 1)
} else {
val scale = Math.sqrt(-2.0 * Math.log(s) / s)
extra = true
value = y * scale
x * scale
}
}
loop(nextDouble() * 2 - 1, nextDouble() * 2 - 1)
}
def nextGaussian(mean: Double, stddev: Double): Double =
nextGaussian() * stddev + mean
def fillGaussians(arr: Array[Double]): Unit =
fillGaussians(arr, 0.0, 1.0)
def fillGaussians(arr: Array[Double], mean: Double, stddev: Double): Unit = {
var i = 0
val len = arr.length & 0xfffffffe
@tailrec def loop(i: Int, x: Double, y: Double): Unit = {
val s = x * x + y * y
if (s >= 1.0 || s == 0.0) {
loop(i, nextDouble() * 2 - 1, nextDouble() * 2 - 1)
} else {
val scale = Math.sqrt(-2.0 * Math.log(s) / s)
arr(i) = x * scale * stddev + mean
arr(i + 1) = y * scale * stddev + mean
}
}
while (i < len) {
loop(i, nextDouble() * 2 - 1, nextDouble() * 2 - 1)
i += 2
}
if (len < arr.length) arr(len) = nextGaussian() * stddev + mean
}
def generateGaussians(n: Int): Array[Double] = {
val arr = new Array[Double](n)
fillGaussians(arr)
arr
}
def generateGaussians(n: Int, mean: Double, stddev: Double): Array[Double] = {
val arr = new Array[Double](n)
fillGaussians(arr, mean, stddev)
arr
}
}
abstract class IntBasedGenerator extends Generator { self =>
def nextLong(): Long =
((nextInt() & 0xffffffffL) << 32) | (nextInt() & 0xffffffffL)
}
abstract class LongBasedGenerator extends Generator { self =>
def nextInt(): Int =
(nextLong() >>> 32).toInt
override def fillInts(arr: Array[Int]): Unit = {
var i = 0
val len = arr.length
val llen = len & 0xfffffffe
while (i < llen) {
val n = nextLong()
arr(i) = (n & 0xffffffff).toInt
arr(i + 1) = ((n >>> 32) & 0xffffffff).toInt
i += 2
}
if (len != llen) arr(i) = nextInt()
}
override def fillShorts(arr: Array[Short]): Unit = {
var i = 0
val len = arr.length
val llen = len & 0xfffffffc
while (i < llen) {
val n = nextLong()
arr(i) = (n & 0xffff).toShort
arr(i + 1) = ((n >>> 16) & 0xffff).toShort
arr(i + 2) = ((n >>> 32) & 0xffff).toShort
arr(i + 3) = ((n >>> 48) & 0xffff).toShort
i += 4
}
if (i < len) {
var n = nextLong()
while (i < len) {
arr(i) = (n & 0xffff).toShort
n = n >>> 16
i += 1
}
}
}
override def fillBytes(arr: Array[Byte]): Unit = {
var i = 0
val len = arr.length
val llen = len & 0xfffffff8
while (i < llen) {
val n = nextLong()
arr(i) = (n & 0xff).toByte
arr(i + 1) = ((n >>> 8) & 0xff).toByte
arr(i + 2) = ((n >>> 16) & 0xff).toByte
arr(i + 3) = ((n >>> 24) & 0xff).toByte
arr(i + 4) = ((n >>> 32) & 0xff).toByte
arr(i + 5) = ((n >>> 40) & 0xff).toByte
arr(i + 6) = ((n >>> 48) & 0xff).toByte
arr(i + 7) = ((n >>> 56) & 0xff).toByte
i += 8
}
if (i < len) {
var n = nextLong()
while (i < len) {
arr(i) = (n & 0xff).toByte
n = n >>> 8
i += 1
}
}
}
}
trait GeneratorCompanion[G, @sp(Int, Long) S] {
def randomSeed(): S
def fromBytes(bytes: Array[Byte]): G
def fromSeed(seed: S): G
def fromTime(time: Long = System.nanoTime): G
final def apply(): G = fromTime()
final def apply(seed: S): G = fromSeed(seed)
}
object Generator {
implicit val rng = GlobalRng
}
object GlobalRng extends LongBasedGenerator {
private val rng = spire.random.rng.Cmwc5.fromTime().sync
override def sync: spire.random.rng.SyncGenerator = rng
def copyInit: Generator = rng.copyInit
def getSeedBytes: Array[Byte] = rng.getSeedBytes
def setSeedBytes(bytes: Array[Byte]): Unit = rng.setSeedBytes(bytes)
def nextLong(): Long = rng.nextLong()
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy