use super::generator::{BitGenerator, SeedableBitGenerator};
use super::seed_sequence::SeedSequence;
const PHILOX_M0: u64 = 0xD2E7_470E_E14C_6C93;
const PHILOX_M1: u64 = 0xCA5A_8263_9512_1157;
const PHILOX_W0: u64 = 0x9E37_79B9_7F4A_7C15;
const PHILOX_W1: u64 = 0xBB67_AE85_84CA_A73B;
#[inline]
fn mulhilo64(a: u64, b: u64) -> (u64, u64) {
let product = (a as u128) * (b as u128);
(product as u64, (product >> 64) as u64)
}
#[inline]
fn philox_round(ctr: [u64; 4], key: [u64; 2]) -> [u64; 4] {
let (lo0, hi0) = mulhilo64(PHILOX_M0, ctr[0]);
let (lo1, hi1) = mulhilo64(PHILOX_M1, ctr[2]);
[hi1 ^ ctr[1] ^ key[0], lo1, hi0 ^ ctr[3] ^ key[1], lo0]
}
#[inline]
fn philox_bump_key(key: [u64; 2]) -> [u64; 2] {
[
key[0].wrapping_add(PHILOX_W0),
key[1].wrapping_add(PHILOX_W1),
]
}
fn philox4x64_10(ctr: [u64; 4], key: [u64; 2]) -> [u64; 4] {
let mut c = ctr;
let mut k = key;
for round in 0..10 {
c = philox_round(c, k);
if round < 9 {
k = philox_bump_key(k);
}
}
c
}
#[inline]
fn counter_increment(ctr: &mut [u64; 4]) {
for word in ctr.iter_mut() {
*word = word.wrapping_add(1);
if *word != 0 {
return;
}
}
}
pub struct Philox4x64BitGenerator {
key: [u64; 2],
ctr: [u64; 4],
buffer: [u64; 4],
buffer_pos: usize,
}
impl Philox4x64BitGenerator {
pub fn new(seed: u64) -> Self {
let seed_seq = SeedSequence::new(seed);
Self::from_seed_sequence_impl(&seed_seq)
}
pub fn from_key(key0: u64, key1: u64) -> Self {
Self::from_key_and_counter([key0, key1], [0, 0, 0, 0])
}
pub fn from_key_and_counter(key: [u64; 2], counter: [u64; 4]) -> Self {
Self {
key,
ctr: counter,
buffer: [0, 0, 0, 0],
buffer_pos: 4,
}
}
fn from_seed_sequence_impl(seed_seq: &SeedSequence) -> Self {
let words = seed_seq.generate_state_u64(2);
Self::from_key(words[0], words[1])
}
pub fn key(&self) -> [u64; 2] {
self.key
}
pub fn counter(&self) -> [u64; 4] {
self.ctr
}
}
impl BitGenerator for Philox4x64BitGenerator {
fn next_u64(&mut self) -> u64 {
if self.buffer_pos == 4 {
counter_increment(&mut self.ctr);
self.buffer = philox4x64_10(self.ctr, self.key);
self.buffer_pos = 0;
}
let value = self.buffer[self.buffer_pos];
self.buffer_pos += 1;
value
}
fn next_u32(&mut self) -> u32 {
(self.next_u64() >> 32) as u32
}
fn next_f64(&mut self) -> f64 {
(self.next_u64() >> 11) as f64 * (1.0 / (1u64 << 53) as f64)
}
fn seed(&mut self, seed: u64) {
*self = Self::new(seed);
}
}
impl SeedableBitGenerator for Philox4x64BitGenerator {
fn from_seed_sequence(seed_seq: &SeedSequence) -> Self {
Self::from_seed_sequence_impl(seed_seq)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn draw_n(gen: &mut Philox4x64BitGenerator, n: usize) -> Vec<u64> {
(0..n).map(|_| gen.next_u64()).collect()
}
#[test]
fn matches_numpy_key_12345_0_two_blocks() {
let mut gen = Philox4x64BitGenerator::from_key(12345, 0);
assert_eq!(gen.key(), [12345, 0]);
assert_eq!(
draw_n(&mut gen, 8),
vec![
11923609910150341984,
14282716219641783572,
14507188490975060125,
2944039161201405073,
2968871015012291328,
15096419966541852992,
16923256687495202939,
1160419304018695661,
]
);
assert_eq!(gen.counter(), [2, 0, 0, 0]);
}
#[test]
fn matches_numpy_key_12345_0_explicit_counter_5() {
let mut gen = Philox4x64BitGenerator::from_key_and_counter([12345, 0], [5, 0, 0, 0]);
assert_eq!(
draw_n(&mut gen, 4),
vec![
14204178594271561098,
4077206687122642885,
13868212636456409208,
8171055895019847646,
]
);
}
#[test]
fn matches_numpy_key_0_12345() {
let mut gen = Philox4x64BitGenerator::from_key(0, 12345);
assert_eq!(
draw_n(&mut gen, 4),
vec![
16603019634450795966,
2013504916108008274,
8977841106562892152,
3879301421665326984,
]
);
}
#[test]
fn matches_numpy_key_7_99999999999_three_blocks() {
let mut gen = Philox4x64BitGenerator::from_key(7, 99999999999);
assert_eq!(
draw_n(&mut gen, 12),
vec![
8254373014095508315,
2302241542758195920,
2028493942077039283,
11752752479999847065,
16064001591349734671,
2526155484687315525,
8721105590713415716,
16619633126342208717,
3586411780284126987,
13878104919006832679,
17043517329545207428,
13472755383390816889,
]
);
}
#[test]
fn matches_numpy_seed_42() {
let mut gen = Philox4x64BitGenerator::new(42);
assert_eq!(gen.key(), [11465652750463011511, 15382171918060459190]);
assert_eq!(
draw_n(&mut gen, 4),
vec![
1587852024645073290,
2611271723512893552,
4982337093617253890,
16123152800351476682,
]
);
}
#[test]
fn matches_numpy_seed_0() {
let mut gen = Philox4x64BitGenerator::new(0);
assert_eq!(
draw_n(&mut gen, 8),
vec![
259491006799949737,
4754966410622352325,
8698845897610382596,
1686395276220330909,
18061843536446043542,
4723914225006068263,
17258640445484096837,
3505852312317462091,
]
);
}
#[test]
fn matches_numpy_seed_1() {
let mut gen = Philox4x64BitGenerator::new(1);
assert_eq!(
draw_n(&mut gen, 8),
vec![
1232279569898196538,
1457532264001425278,
106569017797417483,
14878344917644725055,
4521404008232170583,
5545736711149519115,
6632677743886600724,
8560958798690075040,
]
);
}
#[test]
fn matches_numpy_seed_u64_max() {
let mut gen = Philox4x64BitGenerator::new(u64::MAX);
assert_eq!(
draw_n(&mut gen, 8),
vec![
10924362251224890726,
17368330293776168947,
17111559942051582280,
11921582585585661498,
7322015467787760668,
11741153101999238196,
4294947573519283067,
8333663531596248063,
]
);
}
#[test]
fn seeded_generator_is_deterministic() {
let mut a = Philox4x64BitGenerator::new(2024);
let mut b = Philox4x64BitGenerator::new(2024);
assert_eq!(draw_n(&mut a, 16), draw_n(&mut b, 16));
}
#[test]
fn different_keys_produce_different_streams() {
let mut a = Philox4x64BitGenerator::from_key(1, 0);
let mut b = Philox4x64BitGenerator::from_key(2, 0);
assert_ne!(draw_n(&mut a, 4), draw_n(&mut b, 4));
}
#[test]
fn seed_method_reseeds_deterministically() {
let mut gen = Philox4x64BitGenerator::from_key(1, 1);
gen.next_u64();
gen.seed(42);
let mut expected = Philox4x64BitGenerator::new(42);
assert_eq!(draw_n(&mut gen, 4), draw_n(&mut expected, 4));
}
#[test]
fn counter_increment_carries_across_words() {
let mut ctr = [u64::MAX, 0, 0, 0];
counter_increment(&mut ctr);
assert_eq!(ctr, [0, 1, 0, 0]);
let mut ctr_full_carry = [u64::MAX, u64::MAX, u64::MAX, 5];
counter_increment(&mut ctr_full_carry);
assert_eq!(ctr_full_carry, [0, 0, 0, 6]);
}
#[test]
fn from_seed_sequence_matches_seeded_key() {
let seq = SeedSequence::new(42);
let mut from_seq = Philox4x64BitGenerator::from_seed_sequence(&seq);
let mut from_seed = Philox4x64BitGenerator::new(42);
assert_eq!(from_seq.key(), from_seed.key());
assert_eq!(draw_n(&mut from_seq, 4), draw_n(&mut from_seed, 4));
}
}