1use crate::Arg;
3use rand::{Rng, SeedableRng, rngs::ChaCha8Rng};
4use std::convert::Infallible;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub struct Key(pub [u8; 32]);
9
10impl Key {
11 pub fn seeded(seed: u64) -> Self { Self(ChaCha8Rng::seed_from_u64(seed).get_seed()) }
13 pub fn stream(self, number: u64) -> Stream { Stream { key: self, number } }
15}
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct Stream { pub key: Key, pub number: u64 }
20
21impl Stream {
22 pub fn at(self, block: u64) -> ChaCha8Rng {
24 let mut rng = ChaCha8Rng::from_seed(self.key.0);
25 rng.set_stream(self.number);
26 if block != 0 { rng.set_block_pos(block); }
27 rng
28 }
29}
30
31#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub struct Counter { pub key: Key, pub count: u64 }
34
35impl Counter {
36 pub fn seeded(seed: u64) -> Self { Self { key: Key::seeded(seed), count: 0 } }
38 pub fn next(&mut self) -> Stream {
40 let stream = self.key.stream(self.count);
41 self.count = self.count.wrapping_add(1);
42 stream
43 }
44}
45
46const REGIONS: u64 = 1 << 63;
48const REGION_BLOCKS: u64 = 1 << 23;
49
50pub struct Items { stream: Stream, packed: ChaCha8Rng, w: usize, next: u64 }
52
53impl Items {
54 pub fn new(stream: Stream, w: usize) -> Self { Self { stream, packed: stream.at(0), w, next: 0 } }
56 pub fn item(&mut self) -> Item<'_> {
58 let k = self.next;
59 self.next += 1;
60 Item { left: self.w, k, region: None, items: self }
61 }
62 fn words(&mut self, n: usize) -> impl Iterator<Item = u64> + '_ {
64 let w = self.w;
65 self.next += n as u64;
66 (0..n * w).map(move |_| self.packed.next_u64())
68 }
69 fn region(&self, k: u64) -> ChaCha8Rng { self.stream.at(REGIONS + k * REGION_BLOCKS) }
71}
72
73pub struct Item<'a> { items: &'a mut Items, left: usize, k: u64, region: Option<Box<ChaCha8Rng>> }
75
76impl Drop for Item<'_> { fn drop(&mut self) { for _ in 0..self.left { self.items.packed.next_u64(); } } }
77
78impl rand::TryRng for Item<'_> {
79 type Error = Infallible;
80 fn try_next_u32(&mut self) -> Result<u32, Infallible> { self.try_next_u64().map(|w| (w >> 32) as u32) }
81 fn try_next_u64(&mut self) -> Result<u64, Infallible> {
82 if self.left > 0 { self.left -= 1; return Ok(self.items.packed.next_u64()); }
83 let (items, k) = (&*self.items, self.k);
84 Ok(self.region.get_or_insert_with(|| Box::new(items.region(k))).next_u64())
85 }
86 fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Infallible> { rand::rand_core::utils::fill_bytes_via_next_word(dst, || self.try_next_u64()) }
87}
88
89pub fn roll(bounds: Arg<'_, u64>, n: usize, items: &mut Items, out: &mut Vec<u64>) {
91 match bounds { Arg::One(b) => rolled(std::iter::repeat_n(b, n), items, out), Arg::Many(bs) => rolled(bs[..n].iter().copied(), items, out) }
92}
93
94pub fn below<R: Rng + ?Sized>(b: u64, rng: &mut R) -> u64 { kept(b, || rng.next_u64()) }
96
97fn rolled(bounds: impl ExactSizeIterator<Item = u64> + Clone, items: &mut Items, out: &mut Vec<u64>) {
99 debug_assert_eq!(items.w, 1, "a roll packs one word for each item");
100 let (start, n, first) = (out.len(), bounds.len(), items.next);
101 out.extend(items.words(n));
102 let mut i = 0;
103 while i < n {
104 let k = i + out[start + i..].iter().zip(bounds.clone().skip(i)).position(|(&w, b)| product(w, b).1 < b).unwrap_or(n - i);
106 for (w, b) in out[start + i..start + k].iter_mut().zip(bounds.clone().skip(i)) { *w = product(*w, b).0 }
107 let Some(b) = bounds.clone().nth(k) else { break };
108 let (mut word, mut region) = (Some(out[start + k]), items.region(first + k as u64));
109 out[start + k] = kept(b, || word.take().unwrap_or_else(|| region.next_u64()));
110 i = k + 1;
111 }
112}
113
114fn kept(b: u64, mut next: impl FnMut() -> u64) -> u64 { loop { let (hi, lo) = product(next(), b); if lo >= b || lo >= b.wrapping_neg() % b { break hi; } } }
116
117#[inline]
119fn product(w: u64, b: u64) -> (u64, u64) { let p = u128::from(w) * u128::from(b); ((p >> 64) as u64, p as u64) }
120
121#[inline]
123pub fn unit(w: u64) -> f64 { f64::from_bits((w >> 12) | 1.0f64.to_bits()) - (1.0 - f64::EPSILON / 2.0) }
124
125pub fn units(n: usize, items: &mut Items, out: &mut Vec<f64>) {
127 debug_assert_eq!(items.w, 1, "units pack one word for each item");
128 let start = out.len();
129 out.extend(items.words(n).map(f64::from_bits));
130 for x in &mut out[start..] { *x = unit(x.to_bits()); }
131}
132
133pub fn deal<R: Rng + ?Sized>(n: u64, total: u64, rng: &mut R, out: &mut Vec<u64>) {
135 assert!(n <= total, "a deal can't take more items than its total");
136 out.extend(rand::seq::index::sample(rng, total as usize, n as usize).into_iter().map(|i| i as u64));
137}