#![allow(clippy::unreadable_literal)]
use super::BitGenerator;
pub struct Sfc64 {
a: u64,
b: u64,
c: u64,
counter: u64,
}
impl Sfc64 {
#[inline]
fn advance(&mut self) -> u64 {
let tmp = self.a.wrapping_add(self.b).wrapping_add(self.counter);
self.counter = self.counter.wrapping_add(1);
self.a = self.b ^ (self.b >> 11);
self.b = self.c.wrapping_add(self.c << 3);
self.c = self.c.rotate_left(24).wrapping_add(tmp);
tmp
}
}
impl BitGenerator for Sfc64 {
fn next_u64(&mut self) -> u64 {
self.advance()
}
fn seed_from_u64(seed: u64) -> Self {
let mut rng = Self {
a: seed,
b: seed,
c: seed,
counter: 1,
};
for _ in 0..12 {
let _ = rng.advance();
}
rng
}
fn jump(&mut self) -> Option<()> {
None
}
fn stream(_seed: u64, _stream_id: u64) -> Option<Self> {
None
}
}
impl Clone for Sfc64 {
fn clone(&self) -> Self {
Self {
a: self.a,
b: self.b,
c: self.c,
counter: self.counter,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deterministic_output() {
let mut a = Sfc64::seed_from_u64(0xc0ffee);
let mut b = Sfc64::seed_from_u64(0xc0ffee);
for _ in 0..1000 {
assert_eq!(a.next_u64(), b.next_u64());
}
}
#[test]
fn different_seeds_differ() {
let mut a = Sfc64::seed_from_u64(1);
let mut b = Sfc64::seed_from_u64(2);
let mut diff = false;
for _ in 0..100 {
if a.next_u64() != b.next_u64() {
diff = true;
break;
}
}
assert!(diff);
}
#[test]
fn full_range() {
let mut rng = Sfc64::seed_from_u64(0x1234_5678_9abc_def0);
let mut hi = false;
let mut lo = false;
for _ in 0..10_000 {
let v = rng.next_u64();
if v > u64::MAX / 2 {
hi = true;
} else {
lo = true;
}
if hi && lo {
break;
}
}
assert!(hi && lo);
}
}