Provide common Random API
#KT-17261
This commit is contained in:
@@ -18,9 +18,13 @@
|
||||
package kotlin.internal.jdk7
|
||||
|
||||
import kotlin.internal.PlatformImplementations
|
||||
import kotlin.random.Random
|
||||
import kotlin.random.jdk7.PlatformThreadLocalRandom
|
||||
|
||||
internal open class JDK7PlatformImplementations : PlatformImplementations() {
|
||||
|
||||
override fun addSuppressed(cause: Throwable, exception: Throwable) = cause.addSuppressed(exception)
|
||||
|
||||
override fun defaultPlatformRandom(): Random = PlatformThreadLocalRandom()
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.random.jdk7
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom
|
||||
|
||||
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "CANNOT_OVERRIDE_INVISIBLE_MEMBER")
|
||||
internal class PlatformThreadLocalRandom : kotlin.random.AbstractPlatformRandom() {
|
||||
override val impl: ThreadLocalRandom get() = ThreadLocalRandom.current()
|
||||
|
||||
override fun nextLong(bound: Long): Long = impl.nextLong(bound)
|
||||
override fun nextLong(origin: Long, bound: Long): Long = impl.nextLong(origin, bound)
|
||||
override fun nextInt(bound: Int): Int = impl.nextInt(bound)
|
||||
override fun nextInt(origin: Int, bound: Int): Int = impl.nextInt(origin, bound)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.random
|
||||
|
||||
import kotlin.math.pow
|
||||
|
||||
internal actual fun defaultPlatformRandom(): Random =
|
||||
XorWowRandom((js("Math").random().unsafeCast<Double>() * (2.0.pow(32.0))).toInt())
|
||||
|
||||
|
||||
internal actual fun fastLog2(value: Int): Int {
|
||||
// TODO: not so fast, make faster
|
||||
require(value > 0)
|
||||
var v = value
|
||||
var log = -1
|
||||
while (v != 0) {
|
||||
v = v.ushr(1)
|
||||
log++
|
||||
}
|
||||
return log
|
||||
}
|
||||
@@ -7,6 +7,8 @@ package kotlin.internal
|
||||
|
||||
import kotlin.*
|
||||
import java.util.regex.MatchResult
|
||||
import kotlin.random.FallbackThreadLocalRandom
|
||||
import kotlin.random.Random
|
||||
|
||||
internal open class PlatformImplementations {
|
||||
|
||||
@@ -17,6 +19,8 @@ internal open class PlatformImplementations {
|
||||
public open fun getMatchResultNamedGroup(matchResult: MatchResult, name: String): MatchGroup? {
|
||||
throw UnsupportedOperationException("Retrieving groups by name is not supported on this platform.")
|
||||
}
|
||||
|
||||
public open fun defaultPlatformRandom(): Random = FallbackThreadLocalRandom()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.random
|
||||
|
||||
import kotlin.UnsupportedOperationException
|
||||
import kotlin.internal.*
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
public fun Random.asJavaRandom(): java.util.Random =
|
||||
(this as? AbstractPlatformRandom)?.impl ?: KotlinRandom(this)
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
public fun java.util.Random.asKotlinRandom(): Random =
|
||||
PlatformRandom(this)
|
||||
|
||||
|
||||
|
||||
@InlineOnly
|
||||
internal actual inline fun defaultPlatformRandom(): Random =
|
||||
IMPLEMENTATIONS.defaultPlatformRandom()
|
||||
|
||||
internal actual fun fastLog2(value: Int): Int =
|
||||
31 - Integer.numberOfLeadingZeros(value)
|
||||
|
||||
|
||||
internal abstract class AbstractPlatformRandom : Random() {
|
||||
abstract val impl: java.util.Random
|
||||
|
||||
override fun nextBits(bitCount: Int): Int = impl.nextInt() ushr (32 - bitCount).coerceAtLeast(0)
|
||||
|
||||
override fun nextInt(): Int = impl.nextInt()
|
||||
override fun nextLong(): Long = impl.nextLong()
|
||||
override fun nextBoolean(): Boolean = impl.nextBoolean()
|
||||
override fun nextDouble(): Double = impl.nextDouble()
|
||||
override fun nextFloat(): Float = impl.nextFloat()
|
||||
|
||||
override fun nextBytes(array: ByteArray): ByteArray = run { impl.nextBytes(array); array }
|
||||
}
|
||||
|
||||
internal class FallbackThreadLocalRandom : AbstractPlatformRandom() {
|
||||
private val implStorage = object : ThreadLocal<java.util.Random>() {
|
||||
override fun initialValue(): java.util.Random {
|
||||
return java.util.Random()
|
||||
}
|
||||
}
|
||||
override val impl: java.util.Random
|
||||
get() = implStorage.get()
|
||||
}
|
||||
|
||||
private class PlatformRandom(override val impl: java.util.Random) : AbstractPlatformRandom()
|
||||
|
||||
private class KotlinRandom(val impl: Random) : java.util.Random() {
|
||||
override fun next(bits: Int): Int = impl.nextBits(bits)
|
||||
override fun nextInt(): Int = impl.nextInt()
|
||||
override fun nextInt(bound: Int): Int = impl.nextInt(bound)
|
||||
override fun nextBoolean(): Boolean = impl.nextBoolean()
|
||||
override fun nextLong(): Long = impl.nextLong()
|
||||
override fun nextFloat(): Float = impl.nextFloat()
|
||||
override fun nextDouble(): Double = impl.nextDouble()
|
||||
|
||||
override fun setSeed(seed: Long) {
|
||||
throw UnsupportedOperationException("Setting seed is not supported")
|
||||
}
|
||||
|
||||
override fun nextBytes(bytes: ByteArray) {
|
||||
impl.nextBytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package samples.random
|
||||
|
||||
import samples.*
|
||||
import kotlin.random.Random
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class Randoms {
|
||||
@Sample
|
||||
fun defaultRandom() {
|
||||
val randomValues = List(10) { Random.nextInt(0, 100) }
|
||||
// prints new sequence every time
|
||||
println(randomValues)
|
||||
|
||||
val nextValues = List(10) { Random.nextInt(0, 100) }
|
||||
println(nextValues)
|
||||
assertTrue(randomValues != nextValues)
|
||||
}
|
||||
|
||||
@Sample
|
||||
fun seededRandom() {
|
||||
fun getRandomList(random: Random): List<Int> =
|
||||
List(10) { random.nextInt(0, 100) }
|
||||
|
||||
val randomValues1 = getRandomList(Random(42))
|
||||
// prints the same sequence every time
|
||||
assertPrints(randomValues1, "[44, 34, 69, 67, 22, 16, 67, 45, 95, 10]")
|
||||
|
||||
val randomValues2 = getRandomList(Random(42))
|
||||
// random with the same seed produce the same sequence
|
||||
assertTrue(randomValues1 == randomValues2)
|
||||
|
||||
val randomValues3 = getRandomList(Random(0))
|
||||
// random with another seed produce another sequence
|
||||
assertPrints(randomValues3, "[20, 63, 41, 28, 0, 99, 35, 42, 72, 13]")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.random
|
||||
|
||||
import kotlin.*
|
||||
|
||||
/**
|
||||
* An abstract class that is implemented by random number generator algorithms.
|
||||
*
|
||||
* The companion object [Random.Companion] is the default instance of [Random].
|
||||
*
|
||||
* To get a seeded instance of random generator use [Random] function.
|
||||
*
|
||||
* @sample samples.random.Randoms.defaultRandom
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
public abstract class Random {
|
||||
public abstract fun nextBits(bitCount: Int): Int
|
||||
|
||||
public open fun nextInt(): Int = nextBits(32)
|
||||
public open fun nextInt(bound: Int): Int = nextInt(0, bound)
|
||||
public open fun nextInt(origin: Int, bound: Int): Int {
|
||||
checkRangeBounds(origin.toLong(), bound.toLong())
|
||||
val n = bound - origin
|
||||
if (n > 0 || n == Int.MIN_VALUE) {
|
||||
val rnd = if (n and -n == n) {
|
||||
val bitCount = fastLog2(n)
|
||||
nextBits(bitCount)
|
||||
} else {
|
||||
var v: Int
|
||||
do {
|
||||
val bits = nextInt().ushr(1)
|
||||
v = bits % n
|
||||
} while (bits - v + (n - 1) < 0)
|
||||
v
|
||||
}
|
||||
return origin + rnd
|
||||
} else {
|
||||
while (true) {
|
||||
val rnd = nextInt()
|
||||
if (rnd in origin until bound) return rnd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public open fun nextInt(range: IntRange): Int = when {
|
||||
range.last < Int.MAX_VALUE -> nextInt(range.first, range.last + 1)
|
||||
range.first > Int.MIN_VALUE -> nextInt(range.first - 1, range.last) + 1
|
||||
else -> nextInt()
|
||||
}
|
||||
|
||||
|
||||
public open fun nextLong(): Long = nextInt().toLong().shl(32) + nextInt()
|
||||
|
||||
public open fun nextLong(bound: Long): Long = nextLong(0, bound)
|
||||
|
||||
public open fun nextLong(origin: Long, bound: Long): Long {
|
||||
checkRangeBounds(origin, bound)
|
||||
val n = bound - origin
|
||||
if (n > 0) {
|
||||
val rnd: Long
|
||||
if (n and -n == n) {
|
||||
val bitCount = fastLog2((n ushr 32).toInt())
|
||||
rnd = when {
|
||||
bitCount < 0 ->
|
||||
// toUInt().toLong()
|
||||
nextBits(fastLog2(n.toInt())).toLong() and 0xFFFFFFFF
|
||||
bitCount == 0 ->
|
||||
nextBits(32).toLong() and 0xFFFFFFFF
|
||||
else ->
|
||||
nextBits(bitCount).toLong().shl(32) + nextBits(32)
|
||||
}
|
||||
} else {
|
||||
var v: Long
|
||||
do {
|
||||
val bits = nextLong().ushr(1)
|
||||
v = bits % n
|
||||
} while (bits - v + (n - 1) < 0)
|
||||
rnd = v
|
||||
}
|
||||
return origin + rnd
|
||||
} else {
|
||||
while (true) {
|
||||
val rnd = nextLong()
|
||||
if (rnd in origin until bound) return rnd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public open fun nextLong(range: LongRange): Long = when {
|
||||
// range.isEmpty() -> throw IllegalArgumentException("Cannot get random in empty range: $range")
|
||||
range.last < Long.MAX_VALUE -> nextLong(range.start, range.endInclusive + 1)
|
||||
range.start > Long.MIN_VALUE -> nextLong(range.start - 1, range.endInclusive) + 1
|
||||
else -> nextLong()
|
||||
}
|
||||
|
||||
public open fun nextBoolean(): Boolean = nextBits(1) != 0
|
||||
|
||||
public open fun nextDouble(): Double = (nextBits(26).toLong().shl(27) + nextBits(27)) / (1L shl 53).toDouble()
|
||||
public open fun nextFloat(): Float = nextBits(24) / (1 shl 24).toFloat()
|
||||
|
||||
public open fun nextBytes(array: ByteArray, startIndex: Int = 0, endIndex: Int = array.size): ByteArray {
|
||||
// TODO: check range
|
||||
|
||||
var v: Int = 0
|
||||
var bits: Int = 0
|
||||
for (i in startIndex until endIndex) {
|
||||
if (bits == 0) {
|
||||
bits = if (endIndex - i >= 4) 32 else (endIndex - i) * 8
|
||||
v = nextBits(bits)
|
||||
}
|
||||
array[i] = v.toByte()
|
||||
v = v ushr 8
|
||||
bits -= 8
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
public open fun nextBytes(array: ByteArray): ByteArray = nextBytes(array, 0, array.size)
|
||||
|
||||
public open fun nextBytes(size: Int): ByteArray = nextBytes(ByteArray(size))
|
||||
|
||||
// TODO: UInt, ULong, UByteArray
|
||||
|
||||
/**
|
||||
* The default random number generator.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @sample samples.random.Randoms.defaultRandom
|
||||
*/
|
||||
companion object : Random() {
|
||||
|
||||
private var _defaultRandom: Random? = null
|
||||
private val defaultRandom: Random
|
||||
get() = _defaultRandom ?: defaultPlatformRandom().also { _defaultRandom = it }
|
||||
|
||||
|
||||
override fun nextBits(bitCount: Int): Int = defaultRandom.nextBits(bitCount)
|
||||
|
||||
// TODO: Override others
|
||||
|
||||
internal fun checkRangeBounds(origin: Long, bound: Long) {
|
||||
if (origin >= bound) throw IllegalArgumentException("Random range is empty: [$origin, $bound)")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a repeatable random number generator seeded with the given [seed] `Int` value.
|
||||
*
|
||||
* Two generators with the same seed produce the same sequence of values.
|
||||
*
|
||||
* @sample samples.random.Randoms.seededRandom
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
public fun Random(seed: Int): Random = XorWowRandom(seed)
|
||||
|
||||
/**
|
||||
* Returns a repeatable random number generator seeded with the given [seed] `Long` value.
|
||||
*
|
||||
* Two generators with the same seed produce the same sequence of values.
|
||||
*
|
||||
* @sample samples.random.Randoms.seededRandom
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
public fun Random(seed: Long): Random = XorWowRandom(seed)
|
||||
|
||||
|
||||
internal expect fun defaultPlatformRandom(): Random
|
||||
internal expect fun fastLog2(value: Int): Int // 31 - Integer.numberOfLeadingZeros(value)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.random
|
||||
|
||||
internal class XorWowRandom internal constructor(
|
||||
private var s0: Int,
|
||||
private var s1: Int,
|
||||
private var s2: Int,
|
||||
private var s3: Int,
|
||||
private var addent: Int
|
||||
) : Random() {
|
||||
|
||||
public constructor(seed: Long) : this(seed.toInt(), (seed ushr 32).toInt(), 0, if (seed == 0L) 1 else 0, 1)
|
||||
public constructor(seed: Int) : this(seed, 0, 0, if (seed == 0) 1 else 0, 1)
|
||||
|
||||
init {
|
||||
require((s0 or s1 or s2 or s3) != 0) { "Initial state must have at least one non-zero element" }
|
||||
}
|
||||
|
||||
/* Algorithm "xorwow" from p. 5 of Marsaglia, "Xorshift RNGs" */
|
||||
override fun nextInt(): Int {
|
||||
var t = s3
|
||||
t = t xor (t shr 2)
|
||||
t = t xor (t shl 1)
|
||||
s3 = s2
|
||||
s2 = s1
|
||||
val s = s0
|
||||
s1 = s
|
||||
t = t xor s
|
||||
t = t xor (s shl 4)
|
||||
s0 = t
|
||||
addent += 362437
|
||||
return t + addent
|
||||
}
|
||||
|
||||
override fun nextBits(bitCount: Int): Int = nextInt() ushr (32 - bitCount)
|
||||
}
|
||||
+35
@@ -3864,6 +3864,41 @@ public abstract interface class kotlin/properties/ReadWriteProperty {
|
||||
public abstract fun setValue (Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V
|
||||
}
|
||||
|
||||
public final class kotlin/random/PlatformRandomKt {
|
||||
public static final fun asJavaRandom (Lkotlin/random/Random;)Ljava/util/Random;
|
||||
public static final fun asKotlinRandom (Ljava/util/Random;)Lkotlin/random/Random;
|
||||
}
|
||||
|
||||
public abstract class kotlin/random/Random {
|
||||
public static final field Companion Lkotlin/random/Random$Companion;
|
||||
public fun <init> ()V
|
||||
public abstract fun nextBits (I)I
|
||||
public fun nextBoolean ()Z
|
||||
public fun nextBytes (I)[B
|
||||
public fun nextBytes ([B)[B
|
||||
public fun nextBytes ([BII)[B
|
||||
public static synthetic fun nextBytes$default (Lkotlin/random/Random;[BIIILjava/lang/Object;)[B
|
||||
public fun nextDouble ()D
|
||||
public fun nextFloat ()F
|
||||
public fun nextInt ()I
|
||||
public fun nextInt (I)I
|
||||
public fun nextInt (II)I
|
||||
public fun nextInt (Lkotlin/ranges/IntRange;)I
|
||||
public fun nextLong ()J
|
||||
public fun nextLong (J)J
|
||||
public fun nextLong (JJ)J
|
||||
public fun nextLong (Lkotlin/ranges/LongRange;)J
|
||||
}
|
||||
|
||||
public final class kotlin/random/Random$Companion : kotlin/random/Random {
|
||||
public fun nextBits (I)I
|
||||
}
|
||||
|
||||
public final class kotlin/random/RandomKt {
|
||||
public static final fun Random (I)Lkotlin/random/Random;
|
||||
public static final fun Random (J)Lkotlin/random/Random;
|
||||
}
|
||||
|
||||
public class kotlin/ranges/CharProgression : java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker {
|
||||
public static final field Companion Lkotlin/ranges/CharProgression$Companion;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
|
||||
Reference in New Issue
Block a user