use std::collections::{BTreeMap,BTreeSet};
use std::fmt::Debug;
use parking_lot::Mutex;
#[derive(Debug)]
pub struct Histogram<K>(BTreeMap<K,usize>);
impl<K> Default for Histogram<K> {
fn default()->Self { Histogram(BTreeMap::new()) }
}
#[allow(dead_code)]
impl<K:Ord + Debug + Copy> Histogram<K> {
pub fn new()->Self { Self::default() }
pub fn inc(&mut self, k:K) {
*self.0.entry(k).or_default() += 1;
}
pub fn dec(&mut self, k:K) {
use std::collections::btree_map::Entry::*;
let Occupied(mut slot) = self.0.entry(k) else { panic!("Underflow, k={k:?}") };
*slot.get_mut() -= 1;
if *slot.get() == 0 { slot.remove(); }
}
pub fn iter(&self)->impl '_ + Iterator<Item=(K,usize)> {
self.0.iter().map(|(&k,&v)| (k,v))
}
pub fn keys(&self)->impl '_ + Iterator<Item=K> {
self.0.keys().copied()
}
}
#[derive(Debug)]
pub struct TokenHist<K>(Mutex<Histogram<K>>);
impl<K> Default for TokenHist<K> {
fn default()->Self { TokenHist(Mutex::default()) }
}
impl<K: Ord+Debug+Copy> TokenHist<K> {
#[allow(dead_code)]
pub fn new()->Self { Self::default() }
pub fn make_token(&self, k:K)->Token<'_,K> {
self.0.lock().inc(k);
Token { k, hist: self }
}
#[allow(dead_code)]
pub fn counts(&self)->BTreeMap<K,usize> {
self.0.lock().0.clone()
}
pub fn keys(&self)->BTreeSet<K> {
self.0.lock().keys().collect()
}
}
pub struct Token<'hist, K:Ord+Debug+Copy> {
hist: &'hist TokenHist<K>,
k: K
}
impl<K: Ord+Debug+Copy> std::ops::Deref for Token<'_,K> {
type Target=K;
fn deref(&self)->&K { &self.k }
}
impl<K: Ord+Debug+Copy> Drop for Token<'_,K> {
fn drop(&mut self) {
self.hist.0.lock().dec(self.k);
}
}
#[cfg(test)]
mod tests {
use proptest::prelude::*;
use super::*;
proptest! {
#[test] fn test_histogram(items in prop::collection::vec(0_usize..=0xF, 0..=0xFFF)) {
let mut h: Histogram<usize> = Histogram::new();
for &it in items.iter() { h.inc(it); }
assert_eq!(h.iter().map(|(_,v)| v).sum::<usize>(), items.len());
for (k,v) in h.iter() {
assert_eq!(v, items.iter().filter(|&&it| it == k).count());
}
for &it in items.iter() { h.dec(it); }
assert!(h.keys().next().is_none());
}
#[test] fn test_token_hist(items in prop::collection::vec(0_usize..=0xF, 0..=0xFFF)) {
let h: TokenHist<usize> = TokenHist::new();
let mut tokens: Vec<Token<'_, usize>> = vec![];
for &it in items.iter() { tokens.push(h.make_token(it)); }
assert_eq!(h.counts().into_iter().map(|(_,v)| v).sum::<usize>(), items.len());
for (k,v) in h.counts() {
assert_eq!(v, items.iter().filter(|&&it| it == k).count());
}
tokens.truncate(0);
assert_eq!(h.keys().len(), 0);
}
}
}