KStarbound/src/main/kotlin/ru/dbotthepony/kstarbound/util/random/MWCRandom.kt

95 lines
2.4 KiB
Kotlin

package ru.dbotthepony.kstarbound.util.random
import ru.dbotthepony.kstarbound.getValue
import java.util.random.RandomGenerator
// MWC Random (multiply with carry), exact replica from original code
// (for purpose of giving exactly the same results for same seed provided)
@OptIn(ExperimentalUnsignedTypes::class)
class MWCRandom(seed: ULong = System.nanoTime().toULong(), cycle: Int = 256, windupIterations: Int = 32) : RandomGenerator {
private val data = UIntArray(cycle)
private var carry = 0u
private var dataIndex = 0
var seed: ULong = seed
private set
init {
require(windupIterations >= 0) { "Negative amount of windup iterations: $windupIterations" }
init(seed, windupIterations)
}
/**
* re-initialize this MWC generator
*/
fun init(seed: ULong, windupIterations: Int = 0) {
this.seed = seed
carry = (seed % MAGIC).toUInt()
data[0] = seed.toUInt()
data[1] = seed.shr(32).toUInt()
for (i in 2 until data.size) {
data[i] = 69069u * data[i - 2] + 362437u
}
dataIndex = data.size - 1
// initial windup
for (i in 0 until windupIterations) {
nextInt()
}
}
fun addEntropy(seed: ULong = System.nanoTime().toULong()) {
// Same algo as init, but bitwise xor with existing data
carry = ((carry.toULong().xor(seed)) % MAGIC).toUInt()
data[0] = data[0].xor(seed.toUInt())
data[1] = data[1].xor(seed.shr(32).xor(seed).toUInt())
for (i in 2 until data.size) {
data[i] = data[i].xor(69069u * data[i - 2] + 362437u)
}
}
override fun nextInt(): Int {
dataIndex = (dataIndex + 1) % data.size
val t = MAGIC.toULong() * data[dataIndex] + carry
//val t = MAGIC.toLong() * data[dataIndex].toLong() + carry.toLong()
carry = t.shr(32).toUInt()
data[dataIndex] = t.toUInt()
return t.toInt()
}
override fun nextLong(): Long {
val a = nextInt().toLong() and 0xFFFFFFFFL
val b = nextInt().toLong() and 0xFFFFFFFFL
return a.shl(32) or b
}
override fun nextFloat(): Float {
return (nextInt() and 0x7fffffff) / 2.14748365E9f
}
override fun nextDouble(): Double {
return (nextLong() and 0x7fffffffffffffffL) / 9.223372036854776E18
}
override fun nextDouble(origin: Double, bound: Double): Double {
return nextDouble() * (bound - origin) + origin
}
override fun nextFloat(origin: Float, bound: Float): Float {
return nextFloat() * (bound - origin) + origin
}
companion object {
const val MAGIC = 809430660u
val GLOBAL: MWCRandom by ThreadLocal.withInitial { MWCRandom() }
}
}