use crate::*;
use rand::{rngs::OsRng, RngCore};
use std::hash::Hash;
pub fn pick<T: Clone + Eq + Hash>(amount: usize, conf: Config<T>) -> Result<Vec<T>, Error> {
Picker::build(conf)?.pick(amount)
}
pub struct Picker<T: Clone + Eq + Hash, R: RngCore> {
rng: R,
table: Vec<(T, f64)>,
grid: Vec<f64>,
grid_width: f64,
repetitive: bool,
picked_indexes: Vec<usize>,
}
impl<T: Clone + Eq + Hash> Picker<T, OsRng> {
pub fn build(conf: Config<T>) -> Result<Self, Error> {
Picker::build_with_rng(conf, OsRng)
}
}
impl<T: Clone + Eq + Hash, R: RngCore> Picker<T, R> {
pub fn build_with_rng(conf: Config<T>, rng: R) -> Result<Self, Error> {
let table = conf.vec_table()?;
let table_len = table.len();
let mut grid = Vec::with_capacity(table_len);
let mut cur = 0.;
for (_, val) in &table {
cur += val;
grid.push(cur);
}
let grid_width = *grid.last().unwrap();
Ok(Self {
rng,
table,
grid,
grid_width,
repetitive: conf.repetitive,
picked_indexes: Vec::with_capacity(table_len),
})
}
#[inline(always)]
pub fn table_len(&self) -> usize {
self.table.len()
}
#[inline(always)]
pub fn pick(&mut self, amount: usize) -> Result<Vec<T>, Error> {
self.pick_indexes(amount)?;
Ok(self
.picked_indexes
.iter()
.map(|&i| self.item_key(i))
.collect())
}
#[inline]
pub fn write_to(&mut self, dest: &mut [T]) -> Result<(), Error> {
self.pick_indexes(dest.len())?;
for (i, k) in dest.iter_mut().enumerate() {
*k = self.item_key(self.picked_indexes[i]);
}
Ok(())
}
pub fn test_freqs(&mut self, amount: usize, test_times: usize) -> Result<Table<T>, Error> {
let mut tbl_freq = vec![0_usize; self.table_len()];
if !self.repetitive {
for _ in 0..test_times {
self.pick_indexes(amount)?;
for &idx in &self.picked_indexes {
tbl_freq[idx] += 1;
}
}
} else {
let mut tbl_picked = vec![false; self.table_len()];
for _ in 0..test_times {
for b in tbl_picked.iter_mut() {
*b = false;
}
self.pick_indexes(amount)?;
for &idx in &self.picked_indexes {
if !tbl_picked[idx] {
tbl_freq[idx] += 1;
tbl_picked[idx] = true;
}
}
}
}
let test_times = test_times as f64;
let table = tbl_freq
.iter()
.enumerate()
.map(|(i, &v)| (self.item_key(i), v as f64 / test_times))
.collect();
Ok(table)
}
#[inline]
fn pick_indexes(&mut self, amount: usize) -> Result<(), Error> {
if !self.repetitive && amount > self.table_len() {
return Err(Error::InvalidAmount);
}
self.picked_indexes.clear();
let mut tbl_picked = vec![false; self.table_len()];
while self.picked_indexes.len() < amount {
let i = self.pick_index()?;
if !self.repetitive {
if tbl_picked[i] {
continue;
}
tbl_picked[i] = true;
}
self.picked_indexes.push(i);
}
Ok(())
}
#[inline(always)]
fn pick_index(&mut self) -> Result<usize, Error> {
let mut bytes = [0u8; 4];
self.rng
.try_fill_bytes(&mut bytes)
.map_err(Error::RandError)?;
let val = (u32::from_ne_bytes(bytes) as f64) / (u32::MAX as f64) * self.grid_width;
for (i, &v) in self.grid.iter().enumerate() {
if val <= v {
return Ok(i);
};
}
Ok(self.table_len() - 1) }
#[inline(always)]
fn item_key(&self, i: usize) -> T {
self.table[i].0.clone()
}
}