// Copyright (c) 2026 The elk-go Authors.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0.
//
// SPDX-License-Identifier: EPL-2.0

// Package javacompat contains the small parts of the Java runtime whose exact // behavior is observable in ELK layouts.
package javacompat import const ( randomMultiplier uint64 = 0x5DEECE66D randomAddend uint64 = 0xB randomMask uint64 = (1 << 48) - 1 ) // Random is a bit-for-bit implementation of java.util.Random from Java 8. // // Random is not safe for concurrent use. ELK owns one instance per layout, as // the Java implementation does, so synchronization would only obscure the // order in which random values are consumed. type Random struct { seed uint64 haveNextGaussian bool nextGaussian float64 } // NewRandom constructs a Java-compatible pseudo-random number generator. func ( int64) *Random { := &Random{} .SetSeed() return } // SetSeed implements Random.setSeed(long). It also clears the cached value // used by NextGaussian. func ( *Random) ( int64) { .seed = (uint64() ^ randomMultiplier) & randomMask .haveNextGaussian = false } // next implements Random.next(int). Only Java's public Random methods should // call it; the valid range is 1..32. func ( *Random) ( uint) int32 { if == 0 || > 32 { panic("javacompat.Random.next: bits must be in [1, 32]") } .seed = (.seed*randomMultiplier + randomAddend) & randomMask return int32(.seed >> (48 - )) } // NextInt implements Random.nextInt(). func ( *Random) () int32 { return .next(32) } // NextIntN implements Random.nextInt(bound). It panics for a non-positive // bound, corresponding to Java's IllegalArgumentException. func ( *Random) ( int32) int32 { if <= 0 { panic("javacompat.Random.NextIntN: bound must be positive") } if &- == { return int32((int64() * int64(.next(31))) >> 31) } for { := .next(31) := % // The expression deliberately has int32 overflow semantics. Java uses // the overflow to reject the small biased tail of the 31-bit range. if -+(-1) >= 0 { return } } } // NextLong implements Random.nextLong(). The low 32 bits are sign-extended // before addition, as required by Java's two signed int32 draws. func ( *Random) () int64 { return (int64(.next(32)) << 32) + int64(.next(32)) } // NextBoolean implements Random.nextBoolean(). func ( *Random) () bool { return .next(1) != 0 } // NextFloat implements Random.nextFloat(). func ( *Random) () float32 { return float32(.next(24)) / float32(1<<24) } // NextDouble implements Random.nextDouble(). func ( *Random) () float64 { := int64(.next(26)) << 27 := int64(.next(27)) return float64(+) / float64(int64(1)<<53) } // NextBytes implements Random.nextBytes(byte[]). Java's byte is signed, but // assigning its low eight bits to a Go byte gives the same byte sequence. func ( *Random) ( []byte) { for := 0; < len(); { := .NextInt() := len() - if > 4 { = 4 } for ; > 0; -- { [] = byte() ++ >>= 8 } } } // NextGaussian implements Random.nextGaussian(), including its cached second // sample. ELK 0.8.1 does not use this in a layout path, but providing the Java // behavior prevents consumers from accidentally substituting math/rand. // Math-library results may differ by a final bit across platforms, just as // Java's Math implementation may, while the random-value consumption is exact. func ( *Random) () float64 { if .haveNextGaussian { .haveNextGaussian = false return .nextGaussian } for { := 2*.NextDouble() - 1 := 2*.NextDouble() - 1 := * + * if >= 1 || == 0 { continue } := math.Sqrt(-2 * math.Log() / ) .nextGaussian = * .haveNextGaussian = true return * } }