1use rustev_contract::canonical::tagged_digest;
9
10pub const GENERATOR: &str = "xoshiro256starstar-splitmix64/1";
12pub const STREAM_TAG: &str = "rustev.bootstrap/1";
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct SplitMix64 {
18 pub x: u64,
19}
20
21impl SplitMix64 {
22 pub fn next_u64(&mut self) -> u64 {
23 self.x = self.x.wrapping_add(0x9E37_79B9_7F4A_7C15);
24 let mut z = self.x;
25 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
26 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
27 z ^ (z >> 31)
28 }
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct Xoshiro256StarStar {
34 pub s: [u64; 4],
35}
36
37impl Xoshiro256StarStar {
38 pub fn from_splitmix(seed: u64) -> Self {
40 let mut sm = SplitMix64 { x: seed };
41 let s = [sm.next_u64(), sm.next_u64(), sm.next_u64(), sm.next_u64()];
42 Xoshiro256StarStar { s }
43 }
44
45 pub fn next_u64(&mut self) -> u64 {
46 let s = &mut self.s;
47 let result = s[1].wrapping_mul(5).rotate_left(7).wrapping_mul(9);
48 let t = s[1] << 17;
49 s[2] ^= s[0];
50 s[3] ^= s[1];
51 s[1] ^= s[2];
52 s[0] ^= s[3];
53 s[2] ^= t;
54 s[3] = s[3].rotate_left(45);
55 result
56 }
57
58 pub fn index(&mut self, n: u64) -> u64 {
60 assert!(n >= 1, "an index needs a non-empty range");
61 let rem = (u64::MAX % n + 1) % n;
63 loop {
64 let u = self.next_u64();
65 if rem != 0 && u >= 0u64.wrapping_sub(rem) {
66 continue;
67 }
68 return u % n;
69 }
70 }
71}
72
73pub fn stream_seed(seed: &str, label: &str) -> u64 {
77 let mut bytes = Vec::with_capacity(seed.len() + 1 + label.len());
78 bytes.extend_from_slice(seed.as_bytes());
79 bytes.push(0);
80 bytes.extend_from_slice(label.as_bytes());
81 let digest = tagged_digest(STREAM_TAG, &bytes);
82 let hex = &digest["sha256:".len().."sha256:".len() + 16];
83 u64::from_str_radix(hex, 16).expect("a tagged digest is lowercase hex")
84}
85
86pub fn stream(seed: &str, label: &str) -> Xoshiro256StarStar {
88 Xoshiro256StarStar::from_splitmix(stream_seed(seed, label))
89}
90
91pub fn run_seed(seed: &str, r: usize) -> u64 {
94 stream(seed, &format!("run/{r}")).next_u64()
95}