use crate::{Rng, RngExt};
pub(crate) struct IncreasingUniform<R: Rng> {
pub rng: R,
n: u32,
chunk: u32,
chunk_remaining: u8,
}
impl<R: Rng> IncreasingUniform<R> {
pub fn new(rng: R, n: u32) -> Self {
let chunk_remaining = if n == 0 { 1 } else { 0 };
Self {
rng,
n,
chunk: 0,
chunk_remaining,
}
}
#[inline]
pub fn next_index(&mut self) -> usize {
let next_n = self.n + 1;
let next_chunk_remaining = self.chunk_remaining.checked_sub(1).unwrap_or_else(|| {
let (bound, remaining) = calculate_bound_u32(next_n);
self.chunk = self.rng.random_range(..bound);
remaining - 1
});
let result = if next_chunk_remaining == 0 {
self.chunk as usize
} else {
let r = self.chunk % next_n;
self.chunk /= next_n;
r as usize
};
self.chunk_remaining = next_chunk_remaining;
self.n = next_n;
result
}
}
#[inline]
fn calculate_bound_u32(m: u32) -> (u32, u8) {
debug_assert!(m > 0);
#[inline]
const fn inner(m: u32) -> (u32, u8) {
let mut product = m;
let mut current = m + 1;
loop {
if let Some(p) = u32::checked_mul(product, current) {
product = p;
current += 1;
} else {
let count = (current - m) as u8;
return (product, count);
}
}
}
const RESULT2: (u32, u8) = inner(2);
if m == 2 {
return RESULT2;
}
inner(m)
}