Created
April 26, 2026 21:57
-
-
Save kyay10/50cdb610f48b3f3f1211efccf51c29c0 to your computer and use it in GitHub Desktop.
Ocaml 3 RNG in Kotlin. Uses org.kotlincrypto.hash for MD5 hashing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // https://github.com/ocaml/stdlib-random/blob/main/random3/random3.ml | |
| // Probably doesn't need Longs, but if it ain't broke, don't fix it | |
| class OcamlRandom3 private constructor(private val state: LongArray, private var index: Int) { | |
| constructor() : this(LongArray(55) { 0 }, 0) | |
| constructor(seed: LongArray) : this() { | |
| fullInit(seed) | |
| } | |
| constructor(seed: Long) : this(longArrayOf(seed)) | |
| private val md5 = MD5() | |
| private fun combine(accu: ByteArray, x: Long): ByteArray = md5.digest((accu + x.toString().encodeToByteArray())) | |
| private fun extract(d: ByteArray) = | |
| (d[0].toUByte().toLong() + (d[1].toUByte().toLong() shl 8) + (d[2].toUByte().toLong() shl 16)) xor | |
| (d[3].toUByte().toLong() shl 22) | |
| internal fun fullInit(seed: LongArray) { | |
| val effectiveSeed = if (seed.isEmpty()) longArrayOf(0) else seed | |
| val seedLength = effectiveSeed.size | |
| for (i in 0..54) { | |
| state[i] = i.toLong() | |
| } | |
| var accu = byteArrayOf('x'.code.toByte()) | |
| for (i in 0..(54 + maxOf(55, seedLength))) { | |
| val j = i % 55 | |
| val k = i % seedLength | |
| accu = combine(accu, effectiveSeed[k]) | |
| state[j] = state[j] xor extract(accu) | |
| } | |
| index = 0 | |
| } | |
| // 30 random bits as the lower part of an integer | |
| private fun bits(): Int { | |
| index = (index + 1) % 55 | |
| val curVal = state[index] | |
| val newVal = state[(index + 24) % 55] + (curVal) | |
| val newVal30 = newVal and 0x3FFFFFFF | |
| state[index] = newVal30 | |
| return newVal30.toInt() | |
| } | |
| fun nextDouble(): Double { | |
| val scale = 1073741824.0 | |
| val r0 = bits().toDouble() | |
| val r1 = bits().toDouble() | |
| val r2 = bits().toDouble() | |
| return ((r0 / scale + r1) / scale + r2) / scale | |
| } | |
| } | |
| // https://github.com/ocaml/stdlib-random/blob/main/random3/random3.ml | |
| // Probably doesn't need Longs, but if it ain't broke, don't fix it | |
| class OcamlRandom3a private constructor(private val state: LongArray, private var index: Int) { | |
| constructor() : this(LongArray(55) { 0 }, 0) | |
| constructor(seed: Long) : this() { | |
| init(seed) | |
| } | |
| private val md5 = MD5() | |
| internal fun init(seed: Long) { | |
| var seed = seed | |
| fun mdg(): Long { | |
| seed++ | |
| val d = md5.digest((seed.toString()).encodeToByteArray()) | |
| return (d[0].toUByte().toLong() + (d[1].toUByte().toLong() shl 8) + (d[2].toUByte().toLong() shl 16)) xor | |
| (d[3].toUByte().toLong() shl 22) | |
| } | |
| for (i in 0..54) { | |
| state[i] = mdg() | |
| } | |
| index = 0 | |
| } | |
| // 30 random bits as the lower part of an integer | |
| private fun bits(): Int { | |
| index = (index + 1) % 55 | |
| val curVal = state[index] | |
| val newVal = state[(index + 24) % 55] + (curVal) | |
| state[index] = newVal | |
| return (newVal and 0x3FFFFFFF).toInt() | |
| } | |
| fun nextDouble(): Double { | |
| val scale = 1073741824.0 | |
| val r0 = bits().toDouble() | |
| val r1 = bits().toDouble() | |
| val r2 = bits().toDouble() | |
| return ((r0 / scale + r1) / scale + r2) / scale | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment