Implement LC random generator and Collection.shuffle

This commit is contained in:
Pavel Punegov
2018-06-22 14:04:27 +03:00
committed by Pavel Punegov
parent b6c7d2d7c3
commit 96b0d10746
6 changed files with 191 additions and 10 deletions
+9
View File
@@ -493,6 +493,15 @@ task runtime_basic_exit(type: RunStandaloneKonanTest) {
expectedExitStatus = 42
}
task runtime_random(type: RunKonanTest) {
source = "runtime/basic/random.kt"
}
task runtime_worker_random(type: RunKonanTest) {
disabled = (project.testTarget == 'wasm32')
source = "runtime/basic/worker_random.kt"
}
task hello0(type: RunKonanTest) {
goldValue = "Hello, world!\n"
source = "runtime/basic/hello0.kt"
@@ -0,0 +1,65 @@
package runtime.basic.random
import kotlin.collections.*
import kotlin.random.*
import kotlin.system.*
import kotlin.test.*
/**
* Tests that setting the same seed make random generate the same sequence
*/
private inline fun <reified T> testReproducibility(seed: Long, generator: () -> T) {
// Reset seed. This will make Random to start a new sequence
Random.seed = seed
val first = Array<T>(50, { i -> generator() }).toList()
// Reset seed and try again
Random.seed = seed
val second = Array<T>(50, { i -> generator() }).toList()
assertTrue(first == second, "FAIL: got different sequences of generated values " +
"first: $first, second: $second")
}
/**
* Tests that setting seed makes random generate different sequence.
*/
private inline fun <reified T> testDifference(generator: () -> T) {
Random.seed = 12345678L
val first = Array<T>(100, { i -> generator() }).toList()
Random.seed = 87654321L
val second = Array<T>(100, { i -> generator() }).toList()
assertTrue(first != second, "FAIL: got the same sequence of generated values " +
"first: $first, second: $second")
}
@Test
fun testInts() {
testReproducibility(getTimeMillis(), { Random.nextInt() })
testReproducibility(Long.MAX_VALUE, { Random.nextInt() })
}
@Test
fun testLong() {
testReproducibility(getTimeMillis(), { Random.nextLong() })
testReproducibility(Long.MAX_VALUE, { Random.nextLong() })
}
@Test
fun testDiffInt() = testDifference { Random.nextInt() }
@Test
fun testDiffLong() = testDifference { Random.nextLong() }
@Test
fun testBoundInt() {
testReproducibility(1000L, { Random.nextInt(1000) })
// test bounds
val bound = 5000
val a = Array<Int>(100, { Random.nextInt(bound) })
a.forEach {
assertTrue(it >= 0, "Should be: $it >= 0")
assertTrue(it < bound, "Should be: $it < $bound")
}
}
@@ -0,0 +1,35 @@
package runtime.basic.worker_random
import konan.worker.*
import kotlin.collections.*
import kotlin.random.*
import kotlin.system.*
import kotlin.test.*
@Test
fun testRandomWorkers() {
val seed = getTimeMillis()
val workers = Array(5, { _ -> startWorker()})
val attempts = 3
val results = Array(attempts, { ArrayList<Int>() } )
for (attempt in 0 until attempts) {
Random.seed = seed
// Produce a list of random numbers in each worker
val futures = Array(workers.size, { workerIndex ->
workers[workerIndex].schedule(TransferMode.CHECKED, { workerIndex }) { input ->
Array(10, { Random.nextInt() }).toList()
}
})
// Now collect all results into current attempt's list
val futureSet = futures.toSet()
for (i in 0 until futureSet.size) {
val ready = futureSet.waitForMultipleFutures(10000)
ready.forEach { results[attempt].addAll(it.result()) }
}
}
workers.forEach {
it.requestTermination().consume { _ -> }
}
}
@@ -94,17 +94,12 @@ class MutableCollectionTest {
}
}
/*
// Kotlin/JVM only: java.util.Collections.fill(this, value)
@Test fun listFill() {
val list = MutableList(3) { it }
list.fill(42)
assertEquals(listOf(42, 42, 42), list)
}
*/
/*
// shuffle is Kotlin/JVM only method
@Test fun shuffled() {
val list = MutableList(100) { it }
val shuffled = list.shuffled()
@@ -113,5 +108,4 @@ class MutableCollectionTest {
assertEquals(list.toSet(), shuffled.toSet())
assertEquals(list.size, shuffled.distinct().size)
}
*/
}
@@ -17,6 +17,7 @@
package kotlin.collections
import kotlin.comparisons.*
import kotlin.random.*
// Copies typed varargs array to an array of objects
internal actual fun <T> Array<out T>.copyToArrayOfAny(isVarargs: Boolean): Array<out Any?> =
@@ -229,13 +230,10 @@ public actual fun <T> MutableList<T>.fill(value: T): Unit {
*
* See: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
*/
@FixmeRandom
@SinceKotlin("1.2")
public actual fun <T> MutableList<T>.shuffle(): Unit {
for (i in lastIndex downTo 1) {
// FIXME: implement a good random
//val j = rand(i + 1)
val j = i / 2
val j = Random.nextInt(i + 1)
val copy = this[i]
this[i] = this[j]
this[j] = copy
@@ -0,0 +1,80 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin.random
import konan.internal.*
import konan.worker.AtomicLong
import kotlin.random.Random.Companion.MULTIPLIER
import kotlin.system.getTimeNanos
abstract class Random {
abstract fun nextInt(): Int
abstract fun nextInt(bound: Int): Int
abstract fun nextLong(): Long
/**
* A default pseudo-random linear congruential generator.
*/
companion object : Random() {
private const val MULTIPLIER = 0x5deece66dL
private val _seed = AtomicLong(mult(getTimeNanos()))
/**
* Random generator seed value.
*/
var seed: Long
get() = _seed.get()
set(value) = update(mult(value))
private fun mult(value: Long) = (value xor MULTIPLIER) and ((1L shl 48) - 1)
private fun update(seed: Long) {
_seed.compareAndSwap(_seed.get(), seed)
}
/**
* Returns a pseudo-random Int number.
*/
override fun nextInt(): Int {
update((seed * MULTIPLIER + 0xbL) and ((1L shl 48) - 1));
return (seed ushr 16).toInt();
}
/**
* Returns a pseudo-random Int value between 0 and specified value (exclusive)
*/
override fun nextInt(bound: Int): Int {
if (bound <= 0) throw IllegalArgumentException("Incorrect bound: $bound")
if (bound and (bound - 1) == 0) {
return ((bound * nextInt().toLong()) shr 31).toInt();
}
var m: Int
do {
var r = nextInt()
m = r % bound
} while (r - m + (bound - 1) < 0)
return m
}
/**
* Returns a pseudo-random Long number.
*/
override fun nextLong(): Long = (nextInt().toLong() shl 32) + nextInt().toLong()
}
}