95 lines
2.4 KiB
Kotlin
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() }
|
|
}
|
|
}
|