use core::iter::FusedIterator;
use crate::utils::FindChangePoints;
use alloc::vec::Vec;
use rand::distr::weighted::WeightedIndex;
use rand::prelude::*;
pub fn get_implied_distribution(f: impl Fn(u64) -> usize) -> (Vec<(u64, usize)>, Vec<f64>) {
let mut change_points = Vec::new();
for item in FindChangePoints::new(f) {
let len = item.1;
change_points.push(item);
if len > 128 {
break;
}
}
let probabilities = change_points
.windows(2)
.map(|window| {
let (input, len) = window[0];
let (next_input, _next_len) = window[1];
let prob = 2.0_f64.powi(-(len as i32));
prob * (next_input - input) as f64
})
.collect::<Vec<_>>();
(change_points, probabilities)
}
#[derive(Clone, Copy, Debug)]
struct InfiniteIterator;
impl Iterator for InfiniteIterator {
type Item = ();
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
Some(())
}
}
impl FusedIterator for InfiniteIterator {}
pub fn sample_implied_distribution(
len: impl Fn(u64) -> usize,
rng: &mut impl Rng,
) -> impl Iterator<Item = u64> + '_ {
let (change_points, probabilities) = get_implied_distribution(len);
let dist = WeightedIndex::new(probabilities)
.expect("get_implied_distribution returns non-empty, positive weights");
InfiniteIterator.map(move |_| {
let idx = dist.sample(rng);
let (start_input, _len) = change_points[idx];
let (end_input, _len) = change_points[idx + 1];
rng.random_range(start_input..end_input)
})
}