alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Scala example source code file (Gen.scala)

This example Scala source code file (Gen.scala) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Scala tags/keywords

choose, choose, gen, gen, int, option, option, some, t, t, u, v, w, x

The Scala Gen.scala source code

/*-------------------------------------------------------------------------*\
**  ScalaCheck                                                             **
**  Copyright (c) 2007-2010 Rickard Nilsson. All rights reserved.          **
**  http://www.scalacheck.org                                              **
**                                                                         **
**  This software is released under the terms of the Revised BSD License.  **
**  There is NO WARRANTY. See the file LICENSE for the full text.          **
\*-------------------------------------------------------------------------*/

package org.scalacheck

import scala.collection.mutable.ListBuffer
import util.Buildable
import Prop._
import Arbitrary._

trait Choose[T] {
  def choose(min: T, max: T): Gen[T]
}

object Choose {
  import Gen.{fail, parameterized, value}

  implicit val chooseLong: Choose[Long] = new Choose[Long] {
    def choose(low: Long, high: Long) =
      if(low > high || (high-low < 0)) fail
      else parameterized(prms => value(prms.choose(low,high)))
  }

  implicit val chooseDouble: Choose[Double] = new Choose[Double] {
    def choose(low: Double, high: Double) =
      if (low > high || (high-low > Double.MaxValue)) fail
      else parameterized(prms => value(prms.choose(low,high)))
  }

  implicit val chooseInt: Choose[Int] = new Choose[Int] {
    def choose(low: Int, high: Int) =
      chooseLong.choose(low, high).map(_.toInt)
  }

  implicit val chooseByte: Choose[Byte] = new Choose[Byte] {
    def choose(low: Byte, high: Byte) =
      chooseLong.choose(low, high).map(_.toByte)
  }

  implicit val chooseShort: Choose[Short] = new Choose[Short] {
    def choose(low: Short, high: Short) =
      chooseLong.choose(low, high).map(_.toShort)
  }

  implicit val chooseChar: Choose[Char] = new Choose[Char] {
    def choose(low: Char, high: Char) =
      chooseLong.choose(low, high).map(_.toChar)
  }

  implicit val chooseFloat: Choose[Float] = new Choose[Float] {
    def choose(low: Float, high: Float) =
      chooseDouble.choose(low, high).map(_.toFloat)
  }
}


/** Class that represents a generator. */
sealed trait Gen[+T] {

  import Gen.choose

  var label = "" // TODO: Ugly mutable field

  /** Put a label on the generator to make test reports clearer */
  def label(l: String): Gen[T] = {
    label = l
    this
  }

  /** Put a label on the generator to make test reports clearer */
  def :|(l: String) = label(l)

  /** Put a label on the generator to make test reports clearer */
  def |:(l: String) = label(l)

  /** Put a label on the generator to make test reports clearer */
  def :|(l: Symbol) = label(l.toString.drop(1))

  /** Put a label on the generator to make test reports clearer */
  def |:(l: Symbol) = label(l.toString.drop(1))

  def apply(prms: Gen.Params): Option[T]

  def map[U](f: T => U): Gen[U] = Gen(prms => this(prms).map(f)).label(label)

  def map2[U, V](g: Gen[U])(f: (T, U) => V) =
    combine(g)((t, u) => t.flatMap(t => u.flatMap(u => Some(f(t, u)))))

  def map3[U, V, W](gu: Gen[U], gv: Gen[V])(f: (T, U, V) => W) =
    combine3(gu, gv)((t, u, v) => t.flatMap(t => u.flatMap(u => v.flatMap(v => Some(f(t, u, v))))))

  def map4[U, V, W, X](gu: Gen[U], gv: Gen[V], gw: Gen[W])(f: (T, U, V, W) => X) =
    combine4(gu, gv, gw)((t, u, v, w) => t.flatMap(t => u.flatMap(u => v.flatMap(v => w.flatMap(w => Some(f(t, u, v, w)))))))

  def map5[U, V, W, X, Y](gu: Gen[U], gv: Gen[V], gw: Gen[W], gx: Gen[X])(f: (T, U, V, W, X) => Y) =
    combine5(gu, gv, gw, gx)((t, u, v, w, x) => t.flatMap(t => u.flatMap(u => v.flatMap(v => w.flatMap(w => x.flatMap(x => Some(f(t, u, v, w, x))))))))

  def map6[U, V, W, X, Y, Z](gu: Gen[U], gv: Gen[V], gw: Gen[W], gx: Gen[X], gy: Gen[Y])(f: (T, U, V, W, X, Y) => Z) =
    combine6(gu, gv, gw, gx, gy)((t, u, v, w, x, y) => t.flatMap(t => u.flatMap(u => v.flatMap(v => w.flatMap(w => x.flatMap(x => y.flatMap(y => Some(f(t, u, v, w, x, y)))))))))

  def flatMap[U](f: T => Gen[U]): Gen[U] = Gen(prms => for {
    t <- this(prms)
    u <- f(t)(prms)
  } yield u)

  def filter(p: T => Boolean): Gen[T] = Gen(prms => for {
    t <- this(prms)
    u <- if (p(t)) Some(t) else None
  } yield u).label(label)

  def withFilter(p: T => Boolean) = new GenWithFilter[T](this, p)

  final class GenWithFilter[+A](self: Gen[A], p: A => Boolean) {
    def map[B](f: A => B): Gen[B] = self filter p map f
    def flatMap[B](f: A => Gen[B]): Gen[B] = self filter p flatMap f
    def withFilter(q: A => Boolean): GenWithFilter[A] = new GenWithFilter[A](self, x => p(x) && q(x))
  }

  def suchThat(p: T => Boolean): Gen[T] = filter(p)

  def combine[U,V](g: Gen[U])(f: (Option[T],Option[U]) => Option[V]): Gen[V] =
    Gen(prms => f(this(prms), g(prms)))

  def combine3[U, V, W](gu: Gen[U], gv: Gen[V])
      (f: (Option[T], Option[U], Option[V]) => Option[W]) =
    Gen(prms => f(this(prms), gu(prms), gv(prms)))

  def combine4[U, V, W, X](gu: Gen[U], gv: Gen[V], gw: Gen[W])
      (f: (Option[T], Option[U], Option[V], Option[W]) => Option[X]) =
    Gen(prms => f(this(prms), gu(prms), gv(prms), gw(prms)))

  def combine5[U, V, W, X, Y](gu: Gen[U], gv: Gen[V], gw: Gen[W], gx: Gen[X])
      (f: (Option[T], Option[U], Option[V], Option[W], Option[X]) => Option[Y]) =
    Gen(prms => f(this(prms), gu(prms), gv(prms), gw(prms), gx(prms)))

  def combine6[U, V, W, X, Y, Z](gu: Gen[U], gv: Gen[V], gw: Gen[W], gx: Gen[X], gy: Gen[Y])
      (f: (Option[T], Option[U], Option[V], Option[W], Option[X], Option[Y]) => Option[Z]) =
        Gen(prms => f(this(prms), gu(prms), gv(prms), gw(prms), gx(prms), gy(prms)))

  def ap[U](g: Gen[T => U]) = flatMap(t => g.flatMap(u => Gen(p => Some(u(t)))))

  override def toString =
    if(label.length == 0) "Gen()" else "Gen(\"" + label + "\")"

  /** Returns a new property that holds if and only if both this
   *  and the given generator generates the same result, or both
   *  generators generate no result.
   *  @deprecated Use <code>== instead */
  @deprecated("Use == instead")
  def ===[U](g: Gen[U]): Prop = this == g

  /** Returns a new property that holds if and only if both this
   *  and the given generator generates the same result, or both
   *  generators generate no result.  */
  def ==[U](g: Gen[U]) = Prop(prms =>
    (this(prms.genPrms), g(prms.genPrms)) match {
      case (None,None) => proved(prms)
      case (Some(r1),Some(r2)) if r1 == r2 => proved(prms)
      case _ => falsified(prms)
    }
  )

  def !=[U](g: Gen[U]) = forAll(this)(r => forAll(g)(_ != r))

  def !==[U](g: Gen[U]) = Prop(prms =>
    (this(prms.genPrms), g(prms.genPrms)) match {
      case (None,None) => falsified(prms)
      case (Some(r1),Some(r2)) if r1 == r2 => falsified(prms)
      case _ => proved(prms)
    }
  )

  private var freq = 1
  def |[U >: T](g: Gen[U]): Gen[U] = {
    val h = Gen.frequency((freq, this), (1, g))
    h.freq = freq+1
    h
  }

  /** Generates a sample value by using default parameters */
  def sample: Option[T] = apply(Gen.Params())

}


/** Contains combinators for building generators. */
object Gen {

  import Arbitrary._
  import Shrink._

  /** Record that encapsulates all parameters required for data generation */
  case class Params(
    size: Int = 100,
    rng: java.util.Random = util.StdRand
  ) {
    def resize(newSize: Int) = this.copy(size = newSize)

    /** @throws IllegalArgumentException if l is greater than h, or if
     *  the range between l and h doesn't fit in a Long. */
    def choose(l: Long, h: Long): Long = {
      val d = h-l
      if (d < 0) throw new IllegalArgumentException("Invalid range")
      else if (d == 0) l
      else {
        val r = math.abs(rng.nextLong)
        val a = if(r == Long.MinValue) 1 else 0
        l + (math.abs(r+a) % d) + a
      }
    }

    /** @throws IllegalArgumentException if l is greater than h, or if
     *  the range between l and h doesn't fit in a Double. */
    def choose(l: Double, h: Double) = {
      val d = h-l
      if (d < 0 || d > Double.MaxValue)
        throw new IllegalArgumentException("Invalid range")
      else if (d == 0) l
      else rng.nextDouble * (h-l) + l
    }
  }

  /* Default generator parameters
   *  @deprecated Use <code>Gen.Params() instead */
  @deprecated("Use Gen.Params() instead")
  val defaultParams = Params()

  /* Generator factory method */
  def apply[T](g: Gen.Params => Option[T]) = new Gen[T] {
    def apply(p: Gen.Params) = g(p)
  }

  /* Convenience method for using the <code>frequency method like this:
   * <code>frequency((1, "foo"), (3, "bar")) */
  implicit def freqTuple[T](t: (Int, T)): (Int, Gen[T]) = (t._1, value(t._2))


  //// Various Generator Combinators ////

  /** Sequences generators. If any of the given generators fails, the
   *  resulting generator will also fail. */
  def sequence[C[_],T](gs: Iterable[Gen[T]])(implicit b: Buildable[T,C]): Gen[C[T]] = Gen(prms => {
    val builder = b.builder
    var none = false
    val xs = gs.iterator
    while(xs.hasNext && !none) xs.next.apply(prms) match {
      case None => none = true
      case Some(x) => builder += x
    }
    if(none) None else Some(builder.result())
  })

  /** Wraps a generator lazily. The given parameter is only evalutated once,
   *  and not until the wrapper generator is evaluated. */
  def lzy[T](g: => Gen[T]) = new Gen[T] {
    lazy val h = g
    def apply(prms: Params) = h(prms)
  }

  /** Wraps a generator for later evaluation. The given parameter is
   *  evaluated each time the wrapper generator is evaluated. */
  def wrap[T](g: => Gen[T]) = Gen(p => g(p))

  /** A generator that always generates the given value */
  implicit def value[T](x: T) = Gen(p => Some(x))

  /** A generator that never generates a value */
  def fail[T]: Gen[T] = Gen(p => None)

  /** A generator that generates a random value in the given (inclusive)
   *  range. If the range is invalid, the generator will not generate any value.
   */
  def choose[T](min: T, max: T)(implicit c: Choose[T]): Gen[T] = {
    c.choose(min, max)
  }

  /** Creates a generator that can access its generation parameters */
  def parameterized[T](f: Params => Gen[T]): Gen[T] = Gen(prms => f(prms)(prms))

  /** Creates a generator that can access its generation size */
  def sized[T](f: Int => Gen[T]) = parameterized(prms => f(prms.size))

  /** Creates a resized version of a generator */
  def resize[T](s: Int, g: Gen[T]) = Gen(prms => g(prms.resize(s)))

  /** Chooses one of the given generators with a weighted random distribution */
  def frequency[T](gs: (Int,Gen[T])*): Gen[T] = {
    lazy val tot = (gs.map(_._1) :\ 0) (_+_)

    def pick(n: Int, l: List[(Int,Gen[T])]): Gen[T] = l match {
      case Nil => fail
      case (k,g)::gs => if(n <= k) g else pick(n-k, gs)
    }

    for {
      n <- choose(1,tot)
      x <- pick(n,gs.toList)
    } yield x
  }

  /** Picks a random value from a list */
  def oneOf[T](xs: Seq[T]): Gen[T] = if(xs.isEmpty) fail else for {
    i <- choose(0, xs.size-1)
  } yield xs(i)

  /** Picks a random generator from a list */
  def oneOf[T](g1: Gen[T], g2: Gen[T], gs: Gen[T]*) = for {
    i <- choose(0, gs.length+1)
    x <- if(i == 0) g1 else if(i == 1) g2 else gs(i-2)
  } yield x

  /** Chooses one of the given values, with a weighted random distribution.
   *  @deprecated Use <code>frequency with constant generators
   *  instead. */
  @deprecated("Use 'frequency' with constant generators instead.")
  def elementsFreq[T](vs: (Int, T)*): Gen[T] =
    frequency(vs.map { case (w,v) => (w, value(v)) } : _*)

  /** A generator that returns a random element from a list
   *  @deprecated Use <code>oneOf with constant generators instead. */
  @deprecated("Use 'oneOf' with constant generators instead.")
  def elements[T](xs: T*): Gen[T] = if(xs.isEmpty) fail else for {
    i <- choose(0,xs.length-1)
  } yield xs(i)


  //// List Generators ////

  /** Generates a container of any type for which there exists an implicit
   *  <code>Buildable instance. The elements in the container will
   *  be generated by the given generator. The size of the generated container
   *  is given by <code>n. */
  def containerOfN[C[_],T](n: Int, g: Gen[T])(implicit b: Buildable[T,C]
  ): Gen[C[T]] = sequence[C,T](new Iterable[Gen[T]] {
    def iterator = new Iterator[Gen[T]] {
      var i = 0
      def hasNext = i < n
      def next = { i += 1; g }
    }
  })

  /** Generates a container of any type for which there exists an implicit
   *  <code>Buildable instance. The elements in the container will
   *  be generated by the given generator. The size of the container is
   *  bounded by the size parameter used when generating values. */
  def containerOf[C[_],T](g: Gen[T])(implicit b: Buildable[T,C]): Gen[C[T]] =
    sized(size => for(n <- choose(0,size); c <- containerOfN[C,T](n,g)) yield c)

  /** Generates a non-empty container of any type for which there exists an
   *  implicit <code>Buildable instance. The elements in the container
   *  will be generated by the given generator. The size of the container is
   *  bounded by the size parameter used when generating values. */
  def containerOf1[C[_],T](g: Gen[T])(implicit b: Buildable[T,C]): Gen[C[T]] =
    sized(size => for(n <- choose(1,size); c <- containerOfN[C,T](n,g)) yield c)

  /** Generates a list of random length. The maximum length depends on the
   *  size parameter. This method is equal to calling
   *  <code>containerOf[List,T](g). */
  def listOf[T](g: => Gen[T]) = containerOf[List,T](g)

  /** Generates a non-empty list of random length. The maximum length depends
   *  on the size parameter. This method is equal to calling
   *  <code>containerOf1[List,T](g). */
  def listOf1[T](g: => Gen[T]) = containerOf1[List,T](g)

  /** Generates a list of the given length. This method is equal to calling
   *  <code>containerOfN[List,T](n,g). */
  def listOfN[T](n: Int, g: Gen[T]) = containerOfN[List,T](n,g)

  /** Generates a list of the given length. This method is equal to calling
   *  <code>containerOfN[List,T](n,g).
   *  @deprecated Use the method <code>listOfN instead. */
  @deprecated("Use 'listOfN' instead.")
  def vectorOf[T](n: Int, g: Gen[T]) = containerOfN[List,T](n,g)

  /** A generator that picks a random number of elements from a list */
  def someOf[T](l: Iterable[T]) = choose(0,l.size) flatMap (pick(_,l))

  /** A generator that picks a random number of elements from a list */
  def someOf[T](g1: Gen[T], g2: Gen[T], gs: Gen[T]*) = for {
    n <- choose(0, gs.length+2)
    x <- pick(n, g1, g2, gs: _*)
  } yield x

  /** A generator that picks a given number of elements from a list, randomly */
  def pick[T](n: Int, l: Iterable[T]): Gen[Seq[T]] =
    if(n > l.size || n < 0) fail
    else Gen(prms => {
      val buf = new ListBuffer[T]
      buf ++= l
      while(buf.length > n) {
        val g = choose(0, buf.length-1)
        buf.remove(g(prms).get)
      }
      Some(buf)
    })

  /** A generator that picks a given number of elements from a list, randomly */
  def pick[T](n: Int, g1: Gen[T], g2: Gen[T], gs: Gen[T]*): Gen[Seq[T]] = for {
    is <- pick(n, 0 until (gs.size+2))
    allGs = gs ++ (g1::g2::Nil)
    xs <- sequence[List,T](is.toList.map(allGs(_)))
  } yield xs


  //// Character Generators ////

  /* Generates a numerical character */
  def numChar: Gen[Char] = choose(48,57) map (_.toChar)

  /* Generates an upper-case alpha character */
  def alphaUpperChar: Gen[Char] = choose(65,90) map (_.toChar)

  /* Generates a lower-case alpha character */
  def alphaLowerChar: Gen[Char] = choose(97,122) map (_.toChar)

  /* Generates an alpha character */
  def alphaChar = frequency((1,alphaUpperChar), (9,alphaLowerChar))

  /* Generates an alphanumerical character */
  def alphaNumChar = frequency((1,numChar), (9,alphaChar))

  //// String Generators ////

  /* Generates a string that starts with a lower-case alpha character,
   * and only contains alphanumerical characters */
  def identifier: Gen[String] = for {
    c <- alphaLowerChar
    cs <- listOf(alphaNumChar)
  } yield (c::cs).mkString

  /* Generates a string of alpha characters */
  def alphaStr: Gen[String] = for(cs <- listOf(Gen.alphaChar)) yield cs.mkString

  /* Generates a string of digits */
  def numStr: Gen[String] = for(cs <- listOf(Gen.numChar)) yield cs.mkString

  //// Number Generators ////

  /* Generates positive integers
   * @deprecated Use <code>posNum[Int]code> instead */
  @deprecated("Use posNum[Int] instead")
  def posInt: Gen[Int] = sized(max => choose(1, max))

  /* Generates negative integers
   * @deprecated Use <code>negNum[Int]code> instead */
  @deprecated("Use negNum[Int] instead")
  def negInt: Gen[Int] = sized(max => choose(-max, -1))

  /** Generates positive numbers of uniform distribution, with an
   *  upper bound of the generation size parameter. */
  def posNum[T](implicit num: Numeric[T], c: Choose[T]): Gen[T] = {
    import num._
    sized(max => c.choose(one, fromInt(max)))
  }

  /** Generates negative numbers of uniform distribution, with an
   *  lower bound of the negated generation size parameter. */
  def negNum[T](implicit num: Numeric[T], c: Choose[T]): Gen[T] = {
    import num._
    sized(max => c.choose(-fromInt(max), -one))
  }

  /** Generates numbers within the given inclusive range, with
   *  extra weight on zero, +/- unity, both extremities, and any special
   *  numbers provided. The special numbers must lie within the given range,
   *  otherwise they won't be included. */
  def chooseNum[T](minT: T, maxT: T, specials: T*)(
    implicit num: Numeric[T], c: Choose[T]
  ): Gen[T] = {
    import num._
    val basics = List(minT, maxT, zero, one, -one)
    val basicsAndSpecials = for {
      t <- specials ++ basics if t >= minT && t <= maxT
    } yield (1, value(t))
    val allGens = basicsAndSpecials ++ List(
      (basicsAndSpecials.length, c.choose(minT, maxT))
    )
    frequency(allGens: _*)
  }
}

Other Scala examples (source code examples)

Here is a short list of links related to this Scala Gen.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.