use {
crate::hash::map,
ahash::HashMap,
core::{hash::Hash, num::NonZero},
std::collections::hash_map,
};
#[non_exhaustive]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Multiset<T>
where
T: Eq + Hash,
{
counts: HashMap<T, NonZero<usize>>,
}
impl<T> Default for Multiset<T>
where
T: Eq + Hash,
{
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T> Multiset<T>
where
T: Eq + Hash,
{
#[inline]
pub fn insert(&mut self, value: T) {
self.counts
.entry(value)
.and_modify(|count: &mut NonZero<usize>| {
#[expect(
clippy::arithmetic_side_effects,
reason = "intentional (and extremely unlikely)"
)]
let incremented = unsafe { NonZero::new_unchecked(count.get() + 1) };
*count = incremented;
})
.or_insert(const { NonZero::new(1).unwrap() });
}
#[inline]
pub(crate) fn iter(&self) -> impl Iterator<Item = (&T, NonZero<usize>)> {
self.counts.iter().map(|(k, &v)| (k, v))
}
#[inline]
#[must_use]
pub(crate) fn iter_dedup(&self) -> hash_map::Keys<'_, T, NonZero<usize>> {
self.counts.keys()
}
#[inline]
#[must_use]
pub const fn new() -> Self {
Self { counts: map() }
}
#[inline]
#[must_use]
pub(crate) fn pop(&mut self) -> Option<T>
where
T: Clone,
{
let k = self.iter_dedup().next()?.clone();
let () = self.remove(&k);
Some(k)
}
#[inline]
pub(crate) fn remove(&mut self, t: &T) {
let Some(n) = self.counts.get_mut(t) else {
return;
};
let Some(decremented) = NonZero::new(unsafe { n.get().unchecked_sub(1) }) else {
let _: Option<_> = self.counts.remove(t);
return;
};
*n = decremented;
}
#[inline]
pub(crate) fn total(&self) -> usize {
self.iter().map(|(_, n)| n.get()).sum()
}
}
impl<T> FromIterator<T> for Multiset<T>
where
T: Eq + Hash,
{
#[inline]
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = T>,
{
let mut acc = Self::new();
for t in iter {
acc.insert(t);
}
acc
}
}
impl<T> FromIterator<(T, usize)> for Multiset<T>
where
T: Eq + Hash,
{
#[inline]
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = (T, usize)>,
{
Self {
counts: iter
.into_iter()
.filter_map(|(k, v)| Some((k, NonZero::new(v)?)))
.collect(),
}
}
}
impl<T> FromIterator<(T, NonZero<usize>)> for Multiset<T>
where
T: Eq + Hash,
{
#[inline]
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = (T, NonZero<usize>)>,
{
Self {
counts: iter.into_iter().collect(),
}
}
}