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

commonMain.io.kotest.properties.gens.kt Maven / Gradle / Ivy

package io.kotest.properties

import io.kotest.properties.shrinking.ChooseShrinker
import io.kotest.properties.shrinking.DoubleShrinker
import io.kotest.properties.shrinking.FloatShrinker
import io.kotest.properties.shrinking.IntShrinker
import io.kotest.properties.shrinking.ListShrinker
import io.kotest.properties.shrinking.Shrinker
import io.kotest.properties.shrinking.StringShrinker
import kotlin.jvm.JvmOverloads
import kotlin.math.abs
import kotlin.random.Random
import kotlin.random.nextInt
import kotlin.random.nextLong

/**
 * Returns a stream of values where each value is a random
 * printed string.
 *
 * The constant values are:
 * The empty string
 * A line separator
 * Multi-line string
 * a UTF8 string.
 */
@JvmOverloads
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.string(minSize: Int = 0, maxSize: Int = 100): Gen = object : Gen {
   val literals = listOf("",
      "\n",
      "\nabc\n123\n",
      "\u006c\u0069b/\u0062\u002f\u006d\u0069nd/m\u0061x\u002e\u0070h\u0070")

   override fun constants(): Iterable = literals.filter { it.length in minSize..maxSize }
   override fun random(seed: Long?): Sequence {
      val r = getRandomFor(seed)
      return generateSequence {
         r.nextPrintableString(minSize + r.nextInt(maxSize - minSize + 1))
      }
   }
   override fun shrinker(): Shrinker? = StringShrinker
}

/**
 * Returns a stream of values where each value is a randomly
 * chosen [Int]. The values always returned include
 * the following edge cases: [[Int.MIN_VALUE], [Int.MAX_VALUE], 0, 1, -1]
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.int() = object : Gen {
   val literals = listOf(Int.MIN_VALUE, Int.MAX_VALUE, 0, 1, -1)
   override fun constants(): Iterable = literals
   override fun random(seed: Long?): Sequence {
      val r = getRandomFor(seed)
      return generateSequence { r.nextInt() }
   }

   override fun shrinker() = IntShrinker
}

/**
 * Returns a stream of values where each value is a randomly
 * chosen [UInt]. The values always returned include
 * the following edge cases: [[UInt.MIN_VALUE], [UInt.MAX_VALUE]]
 */
@ExperimentalUnsignedTypes
fun Gen.Companion.uint() = object : Gen {
   val literals = listOf(UInt.MIN_VALUE, UInt.MAX_VALUE)
   override fun constants(): Iterable = literals
   override fun random(seed: Long?): Sequence {
      val r = getRandomFor(seed)
      return generateSequence { r.nextInt().toUInt() }
   }
}

/**
 * Returns a stream of values where each value is a randomly
 * chosen [Short]. The values always returned include
 * the following edge cases: [[Short.MIN_VALUE], [Short.MAX_VALUE], 0]
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.short() = int().map { it.ushr(Int.SIZE_BITS - Short.SIZE_BITS).toShort() }

/**
 * Returns a stream of values where each value is a randomly
 * chosen [UShort]. The values always returned include
 * the following edge cases: [[UShort.MIN_VALUE], [UShort.MAX_VALUE]]
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
@ExperimentalUnsignedTypes
fun Gen.Companion.ushort() = uint().map { it.shr(UInt.SIZE_BITS - UShort.SIZE_BITS).toUShort() }

/**
 * Returns a stream of values where each value is a randomly
 * chosen [Byte]. The values always returned include
 * the following edge cases: [[Byte.MIN_VALUE], [Byte.MAX_VALUE], 0]
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.byte() = int().map { it.ushr(Int.SIZE_BITS - Byte.SIZE_BITS).toByte() }

/**
 * Returns a stream of values where each value is a randomly
 * chosen [UByte]. The values always returned include
 * the following edge cases: [[UByte.MIN_VALUE], [UByte.MAX_VALUE]]
 */
@ExperimentalUnsignedTypes
fun Gen.Companion.ubyte() = uint().map { it.shr(UInt.SIZE_BITS - UByte.SIZE_BITS).toByte() }

/**
 * Returns a stream of values where each value is a randomly
 * chosen positive value. The values returned always include
 * the following edge cases: [Int.MAX_VALUE]
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.positiveIntegers(): Gen = int().filter { it > 0 }

/**
 * Returns a stream of values where each value is a randomly
 * chosen natural number. The values returned always include
 * the following edge cases: [Int.MAX_VALUE]
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.nats(): Gen = int().filter { it >= 0 }

/**
 * Returns a stream of values where each value is a randomly
 * chosen negative value. The values returned always include
 * the following edge cases: [Int.MIN_VALUE]
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.negativeIntegers(): Gen = int().filter { it < 0 }

/**
 * Returns a stream of values where each value is a randomly
 * chosen Double.
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.double(): Gen = object : Gen {
  val literals = listOf(0.0,
    1.0,
    -1.0,
    1e300,
    Double.MIN_VALUE,
    Double.MAX_VALUE,
    Double.NEGATIVE_INFINITY,
    Double.NaN,
    Double.POSITIVE_INFINITY)

  override fun constants(): Iterable = literals
   override fun random(seed: Long?): Sequence {
      val r = getRandomFor(seed)
      return generateSequence { r.nextDouble() }
   }
  override fun shrinker(): Shrinker? = DoubleShrinker
}

/**
 * Returns a [Gen] which is the same as [Gen.double] but does not include +INFINITY, -INFINITY or NaN.
 *
 * This will only generate numbers ranging from [from] (inclusive) to [to] (inclusive)
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.numericDoubles(from: Double = Double.MIN_VALUE,
                                 to: Double = Double.MAX_VALUE
): Gen = object : Gen {
   val literals = listOf(0.0, 1.0, -1.0, 1e300, Double.MIN_VALUE, Double.MAX_VALUE).filter { it in (from..to) }
   override fun constants(): Iterable = literals
   override fun random(seed: Long?): Sequence {
      val r = getRandomFor(seed)
      return generateSequence { r.nextDouble(from, to) }
   }

   override fun shrinker(): Shrinker? = DoubleShrinker
}

@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.positiveDoubles(): Gen = double().filter { it > 0.0 }

@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.negativeDoubles(): Gen = double().filter { it < 0.0 }


/**
 * Returns a stream of values where each value is a randomly
 * chosen Float.
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.float(): Gen = object : Gen {
  val literals = listOf(0F,
    Float.MIN_VALUE,
    Float.MAX_VALUE,
    Float.NEGATIVE_INFINITY,
    Float.NaN,
    Float.POSITIVE_INFINITY)

  override fun constants(): Iterable = literals
   override fun random(seed: Long?): Sequence {
      val r = getRandomFor(seed)
      return generateSequence { r.nextFloat() }
   }
  override fun shrinker() = FloatShrinker
}

/**
 * Returns a [Gen] which is the same as [Gen.float] but does not include +INFINITY, -INFINITY or NaN.
 *
 * This will only generate numbers ranging from [from] (inclusive) to [to] (inclusive)
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.numericFloats(
   from: Float = Float.MIN_VALUE,
   to: Float = Float.MAX_VALUE
): Gen = object : Gen {
   val literals = listOf(0.0F, 1.0F, -1.0F, Float.MIN_VALUE, Float.MAX_VALUE).filter { it in (from..to) }
   override fun constants(): Iterable = literals

   // There's no nextFloat(from, to) method, so borrowing it from Double
   override fun random(seed: Long?): Sequence {
      val r = getRandomFor(seed)
      return generateSequence {
         r.nextDouble(from.toDouble(), to.toDouble()).toFloat()
      }
   }

   override fun shrinker(): Shrinker? = FloatShrinker
}

/**
 * Returns a stream of values where each value is a randomly
 * chosen long. The values returned always include
 * the following edge cases: [[Long.MIN_VALUE], [Long.MAX_VALUE], 0, 1, -1]
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.long(): Gen = object : Gen {
  val literals = listOf(Long.MIN_VALUE, Long.MAX_VALUE, 0L, 1L, -1L)
  override fun constants(): Iterable = literals
   override fun random(seed: Long?): Sequence {
      val r = getRandomFor(seed)
      return generateSequence { abs(r.nextLong()) }
   }
}

/**
 * Returns a stream of values where each value is a randomly
 * chosen [ULong]. The values returned always include
 * the following edge cases: [[ULong.MIN_VALUE], [ULong.MAX_VALUE]]
 */
@ExperimentalUnsignedTypes
fun Gen.Companion.ulong(): Gen = object : Gen {
  val literals = listOf(ULong.MIN_VALUE, ULong.MAX_VALUE)
  override fun constants(): Iterable = literals
   override fun random(seed: Long?): Sequence {
      val r = getRandomFor(seed)
      return generateSequence { r.nextLong().toULong() }
   }
}

/**
 * Returns both boolean values
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.bool(): Gen = object : Gen {
  override fun constants(): Iterable = listOf(true, false)
   override fun random(seed: Long?): Sequence {
      val r = getRandomFor(seed)
      return generateSequence { r.nextBoolean() }
   }
}

private object CharSets {
  val CONTROL     = listOf('\u0000'..'\u001F', '\u007F'..'\u007F')
  val WHITESPACE  = listOf('\u0020'..'\u0020', '\u0009'..'\u0009', '\u000A'..'\u000A')
  val BASIC_LATIN = listOf('\u0021'..'\u007E')
}

/**
 * Returns a stream of randomly-chosen Chars. Custom characters can be generated by
 * providing CharRanges. Distribution will be even across the ranges of Chars.
 * For example:
 * Gen.char('A'..'C', 'D'..'E')
 * Ths will choose A, B, C, D, and E each 20% of the time.
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.char(range: CharRange, vararg ranges: CharRange): Gen {
  return Gen.char(listOf(range) + ranges)
}

/**
 * Returns a stream of randomly-chosen Chars. Custom characters can be generated by
 * providing a list of CharRanges. Distribution will be even across the ranges of Chars.
 * For example:
 * Gen.char(listOf('A'..'C', 'D'..'E')
 * Ths will choose A, B, C, D, and E each 20% of the time.
 *
 * If no parameter is given, ASCII characters will be generated.
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.char(ranges: List = CharSets.BASIC_LATIN): Gen = object : Gen {
  init {
    require(ranges.all { !it.isEmpty() }) { "Ranges cannot be empty" }
    require(ranges.isNotEmpty()) { "List of ranges must have at least one range" }
  }
  override fun constants(): Iterable = emptyList()
  override fun random(seed: Long?): Sequence {
    val r = getRandomFor(seed)
    val genRange =
      if (ranges.size == 1) Gen.constant(ranges.first())
      else makeRangeWeightedGen()
    return generateSequence { genRange.next(seed = seed).random(r) }
  }
  // Convert the list of CharRanges into a weighted Gen in which
  // the ranges are chosen from the list using the length of the
  // range as the weight.
  private fun makeRangeWeightedGen(): Gen {
     val weightPairs = ranges.map { range ->
        val weight = range.last.toInt() - range.first.toInt() + 1
        Pair(weight, range)
     }
     return Gen.choose(weightPairs[0], weightPairs[1], *weightPairs.drop(2).toTypedArray())
  }
}

/**
 * Returns a stream of values, where each value is
 * a set of values generated by the given generator.
 */
@JvmOverloads
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.set(gen: Gen, maxSize: Int = 100): Gen> = object : Gen> {
   init {
      require(maxSize >= 0) { "maxSize must be positive" }
   }

   override fun constants(): Iterable> = listOf(gen.constants().take(maxSize).toSet())
   override fun random(seed: Long?): Sequence> {
      val r = getRandomFor(seed)
      return generateSequence {
         val size = r.nextInt(maxSize)
         gen.random().take(size).toSet()
      }
   }
}

/**
 * Returns a stream of values, where each value is
 * a list of values generated by the underlying generator.
 */
@JvmOverloads
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.list(gen: Gen, maxSize: Int = 100): Gen> = object : Gen> {
   init {
      require(maxSize >= 0) { "maxSize must be positive" }
   }

   override fun constants(): Iterable> = listOf(gen.constants().take(maxSize).toList())
   override fun random(seed: Long?): Sequence> {
      val r = getRandomFor(seed)
      return generateSequence {
         val size = r.nextInt(maxSize)
         gen.random().take(size).toList()
      }
   }

   override fun shrinker() = ListShrinker()
}

/**
 * Returns a [[Gen]] where each value is a [[Triple]] generated
 * by a value from each of three supplied generators.
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.triple(genA: Gen,
                                   genB: Gen,
                                   genC: Gen): Gen> = object : Gen> {
  override fun constants(): Iterable> {
    return genA.constants().zip(genB.constants()).zip(genC.constants()).map {
      Triple(it.first.first,
        it.first.second,
        it.second)
    }
  }

   override fun random(seed: Long?): Sequence> = genA.random(seed).zip(genB.random(seed)).zip(
      genC.random(
         seed)).map {
    Triple(it.first.first,
      it.first.second,
      it.second)
  }
}

@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.bind(gena: Gen, createFn: (A) -> T): Gen = object : Gen {
  override fun constants(): Iterable = emptyList()
   override fun random(seed: Long?): Sequence = gena.random().map { createFn(it) }
}

@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.bind(gena: Gen, genb: Gen, createFn: (A, B) -> T): Gen = object : Gen {
  override fun constants(): Iterable = emptyList()
   override fun random(seed: Long?): Sequence =
      gena.random().zip(genb.random(seed)).map { createFn(it.first, it.second) }
}

@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.bind(gena: Gen,
                                    genb: Gen,
                                    genc: Gen,
                                    createFn: (A, B, C) -> T): Gen = object : Gen {
  override fun constants(): Iterable = emptyList()
   override fun random(seed: Long?): Sequence =
      gena.random().zip(genb.random(seed)).zip(genc.random()).map {
         createFn(it.first.first,
            it.first.second,
            it.second)
      }
}

@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.bind(gena: Gen, genb: Gen, genc: Gen, gend: Gen,
                                       createFn: (A, B, C, D) -> T): Gen = object : Gen {
  override fun constants(): Iterable = emptyList()
   override fun random(seed: Long?): Sequence =
    gena.random()
       .zip(genb.random(seed))
       .zip(genc.random(seed))
       .zip(gend.random(seed))
      .map { createFn(it.first.first.first, it.first.first.second, it.first.second, it.second) }
}

@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.bind(gena: Gen, genb: Gen, genc: Gen, gend: Gen, gene: Gen,
                                          createFn: (A, B, C, D, E) -> T): Gen = object : Gen {
  override fun constants(): Iterable = emptyList()
   override fun random(seed: Long?): Sequence =
    gena.random()
       .zip(genb.random(seed))
       .zip(genc.random(seed))
       .zip(gend.random(seed))
       .zip(gene.random(seed))
      .map {
        createFn(it.first.first.first.first,
          it.first.first.first.second,
          it.first.first.second,
          it.first.second,
          it.second)
      }
}

@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.bind(gena: Gen,
                                             genb: Gen,
                                             genc: Gen,
                                             gend: Gen,
                                             gene: Gen,
                                             genf: Gen,
                                             createFn: (A, B, C, D, E, F) -> T): Gen = object : Gen {
  override fun constants(): Iterable = emptyList()
   override fun random(seed: Long?): Sequence =
    gena.random()
       .zip(genb.random(seed))
       .zip(genc.random(seed))
       .zip(gend.random(seed))
       .zip(gene.random(seed))
       .zip(genf.random(seed))
      .map {
        createFn(
          it.first.first.first.first.first,
          it.first.first.first.first.second,
          it.first.first.first.second,
          it.first.first.second,
          it.first.second,
          it.second)
      }
}

@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.bind(gena: Gen,
                                                genb: Gen,
                                                genc: Gen,
                                                gend: Gen,
                                                gene: Gen,
                                                genf: Gen,
                                                geng: Gen,
                                                createFn: (A, B, C, D, E, F, G) -> T): Gen = object : Gen {
  override fun constants(): Iterable = emptyList()
   override fun random(seed: Long?): Sequence =
    gena.random()
       .zip(genb.random(seed))
       .zip(genc.random(seed))
       .zip(gend.random(seed))
       .zip(gene.random(seed))
       .zip(genf.random(seed))
       .zip(geng.random(seed))
      .map {
        createFn(
          it.first.first.first.first.first.first,
          it.first.first.first.first.first.second,
          it.first.first.first.first.second,
          it.first.first.first.second,
          it.first.first.second,
          it.first.second,
          it.second)
      }
}

fun  Gen.Companion.oneOf(vararg gens: Gen): Gen = object : Gen {
   override fun constants(): Iterable = gens.flatMap { it.constants() }

   override fun random(seed: Long?): Sequence {
      require(gens.isNotEmpty()) { "List of generators cannot be empty" }

      val iterators = gens.map { it.random(seed).iterator() }
      val r = getRandomFor(seed)

      return generateInfiniteSequence {
         val iteratorLocation = r.nextInt(0, iterators.size)
         val iterator = iterators[iteratorLocation]
         iterator.next()
      }

   }
}

/**
 * Returns a stream of values, where each
 * value is generated from the given function
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
inline fun  Gen.Companion.create(crossinline fn: () -> T): Gen = object : Gen {
  override fun constants(): Iterable = emptyList()
   override fun random(seed: Long?): Sequence = generateInfiniteSequence { fn() }
}

/**
 * Adapts a list into a generator, where random
 * values will be picked. May not choose every
 * item in the list.
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.from(values: List): Gen = object : Gen {
  override fun constants(): Iterable = emptyList()
   override fun random(seed: Long?): Sequence {
      val r = getRandomFor(seed)
      return generateInfiniteSequence { values[r.nextInt(0, values.size)] }
   }
}

/**
 * @return a new [Gen] created from the given [values] (see [from] List for more details)
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.from(values: Array): Gen = from(values.toList())

/**
 * Returns a stream of values, where each value is
 * a random Int between the given min and max.
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0. Use Arb.ints(min, max)")
fun Gen.Companion.choose(min: Int, max: Int): Gen {
  require(min < max) { "min must be < max" }
  return object : Gen {
    override fun constants(): Iterable = emptyList()
     override fun random(seed: Long?): Sequence {
        val r = getRandomFor(seed)
        return generateSequence { r.nextInt(min..max) }
     }

    override fun shrinker() = ChooseShrinker(min, max)
  }
}

/**
 * Returns a stream of values, where each value is a
 * Long between the given min and max.
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0. Use Arb.longs(min, max)")
fun Gen.Companion.choose(min: Long, max: Long): Gen {
  require(min < max) { "min must be < max" }
  return object : Gen {
     override fun constants(): Iterable = emptyList()
     override fun random(seed: Long?): Sequence {
        val r = getRandomFor(seed)
        return generateSequence { r.nextLong(min..max) }
     }
  }
}

/**
 * Returns a stream of values based on weights:
 *
 * Gen.choose(1 to 'A', 2 to 'B') will generate 'A' 33% of the time
 * and 'B' 66% of the time.
 *
 * @throws IllegalArgumentException If any negative weight is given or only
 * weights of zero are given.
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0. Use Arb.choose(a, b, c, ...)")
fun  Gen.Companion.choose(a: Pair, b: Pair, vararg cs: Pair): Gen {
  val allPairs = listOf(a, b) + cs
  val weights = allPairs.map { it.first }
  require(weights.all { it >=0 }) { "Negative weights not allowed" }
  require(weights.any { it > 0 }) { "At least one weight must be greater than zero"}
  return object : Gen {
    // The algorithm for pick is a migration of
    // the algorithm from Haskell QuickCheck
    // http://hackage.haskell.org/package/QuickCheck
    // See function frequency in the package Test.QuickCheck
    private tailrec fun pick(n: Int, l: Sequence>): T {
      val (w, e) = l.first()
      return if (n <= w) e
      else pick(n - w, l.drop(1))
    }
    override fun constants(): Iterable = emptyList()
    override fun random(seed: Long?): Sequence {
      val r = getRandomFor(seed)
      val total = weights.sum()
      return generateSequence {
        val n = r.nextInt(1, total + 1)
        pick(n, allPairs.asSequence())
      }
    }
  }
}

/**
 * Returns a stream of values, where each value is
 * a pair generated by the underlying generators.
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.pair(genK: Gen, genV: Gen): Gen> = object : Gen> {
  override fun constants(): Iterable> {
    val keys = genK.constants().toList()
    return keys.zip(genV.random().take(keys.size).toList())
  }

   override fun random(seed: Long?): Sequence> = genK.random(seed).zip(genV.random(seed))
}

/**
 * Returns a stream of values, where each value is
 * a Map, which contains keys and values generated
 * from the underlying generators.
 */
@JvmOverloads
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.map(genK: Gen, genV: Gen, maxSize: Int = 100): Gen> = object : Gen> {
  init {
    require(maxSize >= 0) { "maxSize must be positive" }
  }

  override fun constants(): Iterable> = emptyList()
   override fun random(seed: Long?): Sequence> {
      val r = getRandomFor(seed)
      return generateSequence {
         val size = r.nextInt(maxSize)
         genK.random().take(size).zip(genV.random().take(size)).toMap()
      }
   }
}

/**
 * @return a stream of values, where each value is a [Map] of [Pair]s from the given [gen]
 *   the size of the [Map] is bounded between [0, [maxSize])
 */
@JvmOverloads
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.map(gen: Gen>, maxSize: Int = 100): Gen> = object : Gen> {
  init {
    require(maxSize >= 0) { "maxSize must be positive" }
  }

  override fun constants(): Iterable> = emptyList()
   override fun random(seed: Long?): Sequence> {
      val r = getRandomFor(seed)
      return generateSequence {
         val size = r.nextInt(maxSize)
         gen.random(seed).take(size).toMap()
      }
   }
}

/**
 * Returns the next pseudorandom, uniformly distributed value
 * from the ASCII range 32-126.
 */
fun Random.nextPrintableChar(): Char = nextInt(from = 32, until = 127).toChar()

/**
 * Generates a [String] of [length] by calling [nextPrintableChar]
 *
 * ```kotlin
 * // Examples
 * val r = Random.Default
 * r.nextPrintableString(-10) // ""
 * r.nextPrintableString(0)   // ""
 * r.nextPrintableString(1)   // " ", "a", "Z", etc.
 * r.nextPrintableString(5)   // "Ha Ha", "gens!", "[{-}]", etc.
 * ```
 *
 * @param length of String
 * @return a given length String of random printable Chars
 */
fun Random.nextPrintableString(length: Int): String {
   return (0 until length).map { nextPrintableChar() }.joinToString("")
}

/**
 * Returns a [[Gen]] which always returns the same value.
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.constant(value: T): Gen = object : Gen {
   override fun constants(): Iterable = listOf(value)
   override fun random(seed: Long?): Sequence = generateInfiniteSequence { value }
}

@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.multiples(k: Int, max: Int): Gen = object : Gen {

   // 0 is a multiple of everything
   override fun constants(): Iterable = listOf(0)

   override fun random(seed: Long?): Sequence {
      val r = getRandomFor(seed)
      return generateSequence {
         r.nextInt(max / k) * k
      }.filter { it >= 0 }
   }
}

@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.factors(k: Int): Gen = object : Gen {

   // 1 is a factor of all ints
   override fun constants(): Iterable = listOf(1)

   override fun random(seed: Long?): Sequence {
      val r = getRandomFor(seed)
      return generateSequence { r.nextInt(k) }.filter { it > 0 }
         .filter { k % it == 0 }
   }
}

/**
 * Returns a [Gen] which returns the sample values in the same order as they are passed in, once all sample values are used
 * it repeats elements from start.
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.Companion.samples(vararg sampleValues: T) = object : Gen {
    private fun getNextSampleElementProvider(): () -> T  {
        var currentIndex = 0;
        return {
            val nextIndex = currentIndex % sampleValues.size
            val nextValue = sampleValues[nextIndex]
            currentIndex += 1
            nextValue
        }
    }

    override fun random(seed: Long?): Sequence {
        return if(sampleValues.isEmpty()) {
            emptySequence()
        } else {
            generateInfiniteSequence(getNextSampleElementProvider())
        }
    }

    override fun constants(): Iterable = sampleValues.asIterable()
}