val cfactor = 2 abstract class Value(val t: Type) { def valstr: String override def toString = "(" + t.getName + f"$valstr)" } type Weights = Map[Type, Int] trait Type { me => def rnd(path: Weights): Value// call this method def getName: String = getClass.getSimpleName } def iRnd(rangeLen: Int, from: Int = 0) = (Math.random * rangeLen).toInt + from case class Enum(alternatives: Type*) extends Type { class Val(ref: Value) extends Value(this) { def valstr = ref.toString} def rnd(weights: Weights) = { // val picked = iRnd(alternatives.length) // simply random val roulette = alternatives.map {weights get _ match { case Some(count) => Math.pow(cfactor, -count) case None => 1 }}.toArray val ball = Math.random * roulette.sum def drop(n: Int, sum: Double): Int = if (sum > 0) drop(n+1, sum-roulette(n)) else n-1 val picked = alternatives(drop(0, ball)) val uw = weights updated (picked, weights.getOrElse(picked, 0) + 1) new Val(picked.rnd(uw)) } } class Num(from: Int, len: Int) extends Type() { class Val(n: Int) extends Value(this) { def valstr = n.toString} override def getName = f"[$from,${from + len}] " def rnd(weights: Weights) = new Val(iRnd(len, from)) } object Byte extends Num(-128, 256) val ByteExpr: Enum = new Enum(Byte, IntSum(ByteExpr)) case class IntSum(expr: => Type) extends Type() { // need call by name to "tie the knot" val ByteExpr => Sum(ByteExpr) class Val(left: Value, right: Value) extends Value(this) { def valstr = left + "," + right} def rnd(weights: Weights): Value = new Val(expr.rnd(weights), expr.rnd(weights)) } (1 to 70) foreach {i=> println(ByteExpr.rnd(Map.empty))}