All Downloads are FREE. Search and download functionalities are using the official Maven repository.

spire.random.Uniform.scala Maven / Gradle / Ivy

The newest version!
package spire
package random

import spire.math.{Rational, UInt, ULong}

trait Uniform[@sp A] extends Any { self =>
  /**
   * Return an `A` that is uniformly distributed between `min` and `max` inclusive.
   */
  def apply(min: A, max: A): Dist[A]
}

object Uniform {
  @inline final def apply[@sp A](implicit u: Uniform[A]): Uniform[A] = u

  def apply[@sp A](min: A, max: A)(implicit u: Uniform[A]): Dist[A] = u(min, max)

  implicit val UniformInt: Uniform[Int] =
    new Uniform[Int] {
      def apply(min: Int, max: Int): Dist[Int] =
        new DistFromGen[Int](_.nextInt(min, max))
    }

  implicit val UniformLong: Uniform[Long] =
    new Uniform[Long] {
      def apply(min: Long, max: Long): Dist[Long] =
        new DistFromGen[Long](_.nextLong(min, max))
    }

  implicit val UniformUInt: Uniform[UInt] =
    new Uniform[UInt] {
      def apply(min: UInt, max: UInt): Dist[UInt] =
        new DistFromGen[UInt](g => UInt(g.nextInt(min.signed, max.signed)))
    }

  implicit val UniformULong: Uniform[ULong] =
    new Uniform[ULong] {
      def apply(min: ULong, max: ULong): Dist[ULong] =
        new DistFromGen[ULong](g => ULong(g.nextLong(min.signed, max.signed)))
    }

  implicit val UniformFloat: Uniform[Float] =
    new Uniform[Float] {
      def apply(min: Float, max: Float): Dist[Float] =
        new DistFromGen[Float](_.nextFloat(min, max))
    }

  implicit val UniformDouble: Uniform[Double] =
    new Uniform[Double] {
      def apply(min: Double, max: Double): Dist[Double] =
        new DistFromGen[Double](_.nextDouble(min, max))
    }

  implicit val UniformBigInt: Uniform[BigInt] =
    new Uniform[BigInt] {
      def apply(min: BigInt, max: BigInt): Dist[BigInt] = {
        val range = max - min
        val width = range.bitLength
        if (width < 64) {
          val range0 = range.toLong
          new DistFromGen[BigInt]({ gen =>
            min + BigInt(gen.nextLong(0, range0))
          })
        } else {
          val mask0 = (1 << (width % 8)) - 1
          val mask = if (mask0 == 0) 255 else mask0
          new DistFromGen[BigInt]({ gen =>
            val bytes = new Array[Byte]((width + 7) / 8)

            def loop(): BigInt = {
              gen.fillBytes(bytes)
              bytes(0) = (bytes(0) & mask).toByte
              val n = BigInt(1, bytes)
              if (n > range) loop() else n
            }

            min + loop()
          })
        }
      }
    }

  implicit val UniformBigDecimal: Uniform[BigDecimal] =
    new Uniform[BigDecimal] {
      def apply(min: BigDecimal, max: BigDecimal): Dist[BigDecimal] = {
        val precision = spire.math.max(min.mc.getPrecision, max.mc.getPrecision)
        if (precision == 0) {
          throw new IllegalArgumentException("Both min and max provided to UniformBigDecimal have unlimited precision. Cannot produce uniform distributions with unlimited precision.")
        }
        val range = max - min
        val dist = UniformBigInt(0, BigInt(10) pow precision)
        new DistFromGen[BigDecimal]({ gen =>
          min + range * BigDecimal(dist(gen), precision)
        })
      }
    }

  def uniformRational(eps: Rational): Uniform[Rational] =
    new Uniform[Rational] {
      def apply(min: Rational, max: Rational): Dist[Rational] = {
        val num = ((max - min) / eps).toBigInt
        Uniform[BigInt].apply(0, num).map(n => min + Rational(n) * eps)
      }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy