//! Seeded, deterministic RNG (xorshift64* over splitmix64-derived streams). Mods only ever
//! reach it via `ctx:random()`, which keeps replays/tests stable — that's why it lives in the
//! shared context core, not as a free function.

pub struct Rng {
    state: u64,
}
impl Rng {
    pub fn seeded(seed: u64) -> Rng {
        Rng {
            state: seed ^ 0x9E37_79B9_7F4A_7C15,
        }
    }

    /// A child stream for one pass. Advances the parent (so successive frames fork
    /// distinct streams) and seeds the child from the mixed bits.
    pub fn fork(&mut self) -> Rng {
        self.state = self.state.wrapping_add(0x9E37_79B9_7F4A_7C15);
        let mut z = self.state;
        z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
        z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
        Rng {
            state: (z ^ (z >> 31)) | 1,
        }
    }

    pub fn next_u32(&mut self) -> u32 {
        let mut x = self.state;
        x ^= x << 13;
        x ^= x >> 7;
        x ^= x << 17;
        self.state = x;
        (x >> 32) as u32
    }

    pub fn f32(&mut self) -> f32 {
        (self.next_u32() >> 8) as f32 / (1u32 << 24) as f32
    }
    pub fn range_f32(&mut self, min: f32, max: f32) -> f32 {
        min + (self.f32() * (max - min))
    }
}
