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() } } }