#![deny(missing_docs, missing_debug_implementations, unreachable_pub)]
#![cfg_attr(doc, deny(rustdoc::broken_intra_doc_links))]
#![warn(rust_2018_idioms)]
#[cfg(feature = "amortize")]
use griddle::HashMap;
use std::borrow::Borrow;
use std::collections::hash_map::RandomState;
#[cfg(not(feature = "amortize"))]
use std::collections::HashMap;
use std::convert::TryFrom;
use std::hash::{BuildHasher, Hash};
#[cfg(feature = "serde")]
mod serde;
pub struct HashBag<T, S = RandomState> {
items: HashMap<T, usize, S>,
count: usize,
}
impl<T: Clone + Hash, S: Clone + BuildHasher> Clone for HashBag<T, S> {
fn clone(&self) -> Self {
Self {
items: self.items.clone(),
count: self.count,
}
}
fn clone_from(&mut self, source: &Self) {
self.items.clone_from(&source.items);
self.count = source.count;
}
}
impl<T: Hash + Eq> HashBag<T, RandomState> {
#[inline]
pub fn new() -> HashBag<T, RandomState> {
Self::with_hasher(RandomState::new())
}
#[inline]
pub fn with_capacity(capacity: usize) -> HashBag<T, RandomState> {
Self::with_capacity_and_hasher(capacity, RandomState::new())
}
}
impl<T, S> HashBag<T, S> {
#[inline]
pub fn capacity(&self) -> usize {
self.items.capacity()
}
#[inline]
pub fn iter(&self) -> Iter<'_, T> {
Iter::new(self.items.iter(), self.count)
}
#[inline]
pub fn set_iter(&self) -> SetIter<'_, T> {
SetIter(self.items.iter())
}
#[inline]
pub fn len(&self) -> usize {
self.count
}
#[inline]
pub fn set_len(&self) -> usize {
self.items.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.count == 0
}
#[inline]
pub fn drain(&mut self) -> Drain<'_, T> {
self.count = 0;
Drain(self.items.drain())
}
#[inline]
pub fn clear(&mut self) {
self.count = 0;
self.items.clear();
}
}
impl<T, S> HashBag<T, S>
where
T: Eq + Hash,
S: BuildHasher,
{
#[inline]
pub fn with_hasher(hash_builder: S) -> HashBag<T, S> {
HashBag {
items: HashMap::with_hasher(hash_builder),
count: 0,
}
}
#[inline]
pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> HashBag<T, S> {
HashBag {
items: HashMap::with_capacity_and_hasher(capacity, hash_builder),
count: 0,
}
}
#[inline]
pub fn hasher(&self) -> &S {
self.items.hasher()
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.items.reserve(additional)
}
#[inline]
pub fn shrink_to_fit(&mut self) {
self.items.shrink_to_fit()
}
#[inline]
pub fn contains<Q: ?Sized>(&self, value: &Q) -> usize
where
T: Borrow<Q>,
Q: Hash + Eq,
{
self.items.get(value).cloned().unwrap_or(0)
}
#[inline]
pub fn get<Q: ?Sized>(&self, value: &Q) -> Option<(&T, usize)>
where
T: Borrow<Q>,
Q: Hash + Eq,
{
self.items
.get_key_value(value)
.map(|(t, count)| (t, *count))
}
#[inline]
pub fn entry(&mut self, value: T) -> Entry<'_, T, S> {
Entry((
ForiegnEntry::new(self.items.entry(value)),
&mut self.count,
PhantomData,
))
}
#[inline]
pub fn insert(&mut self, value: T) -> usize {
self.insert_many(value, 1)
}
#[inline]
pub fn insert_many(&mut self, value: T, count: usize) -> usize {
if count == 0 {
return self.contains(&value);
}
self.count += count;
let n = self.items.entry(value).or_insert(0);
let was_there = *n;
*n += count;
was_there
}
#[inline]
pub fn replace(&mut self, value: T) -> usize {
let n = self.items.remove(&value).unwrap_or(0);
self.count -= n;
self.items.insert(value, 1);
self.count += 1;
n
}
#[inline]
pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> usize
where
T: Borrow<Q>,
Q: Hash + Eq,
{
match self.items.get_mut(value) {
None => 0,
#[cfg(debug_assertions)]
Some(n) if *n == 0 => unreachable!(),
Some(n) if *n == 1 => {
self.count -= 1;
self.items.remove(value);
1
}
Some(n) => {
self.count -= 1;
*n -= 1;
*n + 1
}
}
}
#[inline]
pub fn remove_up_to<Q: ?Sized>(&mut self, value: &Q, quantity: usize) -> usize
where
T: Borrow<Q>,
Q: Hash + Eq,
{
match self.items.get_mut(value) {
None => 0,
Some(&mut n) if n <= quantity => {
self.count -= n;
self.items.remove(value);
0
}
Some(n) => {
self.count -= quantity;
*n -= quantity;
*n
}
}
}
pub fn outer_join<'a, OtherS>(
&'a self,
other: &'a HashBag<T, OtherS>,
) -> impl Iterator<Item = (&'a T, usize, usize)>
where
OtherS: BuildHasher,
{
self.items
.iter()
.map(move |(x, &self_count)| (x, self_count, other.contains(x)))
.chain(other.items.iter().filter_map(move |(x, &other_count)| {
let self_count = self.contains(x);
if self_count == 0 {
Some((x, self_count, other_count))
} else {
None
}
}))
}
pub fn difference<'a, OtherS>(
&'a self,
other: &'a HashBag<T, OtherS>,
) -> impl Iterator<Item = (&'a T, usize)>
where
OtherS: BuildHasher,
{
self.outer_join(other)
.take_while(|(_, self_count, _)| self_count > &0)
.filter(|(_x, self_count, other_count)| self_count > other_count)
.map(|(x, self_count, other_count)| (x, self_count - other_count))
}
pub fn signed_difference<'a, OtherS>(
&'a self,
other: &'a HashBag<T, OtherS>,
) -> impl Iterator<Item = (&'a T, isize)>
where
OtherS: BuildHasher,
{
self.outer_join(other).map(|(x, self_count, other_count)| {
let diff = if self_count >= other_count {
isize::try_from(self_count - other_count).unwrap_or(std::isize::MAX)
} else {
isize::try_from(other_count - self_count)
.map(|x| -x)
.unwrap_or(std::isize::MIN)
};
(x, diff)
})
}
pub fn not_in<'a, OtherS>(
&'a self,
other: &'a HashBag<T, OtherS>,
) -> impl Iterator<Item = (&'a T, usize)>
where
OtherS: BuildHasher,
{
self.outer_join(other)
.take_while(|(_, self_count, _)| self_count > &0)
.filter_map(|(k, self_count, other_count)| {
if other_count == 0 {
Some((k, self_count))
} else {
None
}
})
}
#[inline]
pub fn try_take<Q: ?Sized>(&mut self, value: &Q) -> Result<T, Option<(&T, usize)>>
where
T: Borrow<Q>,
Q: Hash + Eq,
{
match self.items.remove_entry(value) {
Some((t, 1)) => {
self.count -= 1;
Ok(t)
}
Some((t, n)) => {
self.count -= 1;
self.items.insert(t, n - 1);
Err(Some(
self.items
.get_key_value(value)
.map(|(t, n)| (t, *n + 1))
.unwrap(),
))
}
None => Err(None),
}
}
#[inline]
pub fn take_all<Q: ?Sized>(&mut self, value: &Q) -> Option<(T, usize)>
where
T: Borrow<Q>,
Q: Hash + Eq,
{
let (t, n) = self.items.remove_entry(value)?;
self.count -= n;
Some((t, n))
}
pub fn retain<F>(&mut self, mut f: F)
where
F: FnMut(&T, usize) -> usize,
{
let count = &mut self.count;
self.items.retain(|t, n| {
let keep = std::cmp::min(*n, f(t, *n));
*count -= *n - keep;
if keep == 0 {
false
} else {
*n = keep;
true
}
});
}
}
use std::fmt;
use std::marker::PhantomData;
impl<T> fmt::Debug for HashBag<T>
where
T: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_set().entries(self.iter()).finish()
}
}
impl<T, S> Default for HashBag<T, S>
where
T: Eq + Hash,
S: BuildHasher + Default,
{
fn default() -> Self {
Self::with_hasher(S::default())
}
}
impl<T, S> PartialEq<HashBag<T, S>> for HashBag<T, S>
where
T: Eq + Hash,
S: BuildHasher,
{
fn eq(&self, other: &Self) -> bool {
self.count == other.count && self.items == other.items
}
}
impl<T, S> Eq for HashBag<T, S>
where
T: Eq + Hash,
S: BuildHasher,
{
}
impl<'a, T, S> Extend<&'a T> for HashBag<T, S>
where
T: 'a + Eq + Hash + Clone,
S: BuildHasher,
{
fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
for e in iter {
self.insert(e.clone());
}
}
}
impl<'a, T, S> Extend<(&'a T, usize)> for HashBag<T, S>
where
T: 'a + Eq + Hash + Clone,
S: BuildHasher,
{
fn extend<I: IntoIterator<Item = (&'a T, usize)>>(&mut self, iter: I) {
for (e, n) in iter {
self.count += n;
*self.items.entry(e.clone()).or_insert(0) += n;
}
}
}
impl<T, S> Extend<T> for HashBag<T, S>
where
T: Eq + Hash,
S: BuildHasher,
{
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for e in iter {
self.insert(e);
}
}
}
impl<T, S> Extend<(T, usize)> for HashBag<T, S>
where
T: Eq + Hash,
S: BuildHasher,
{
fn extend<I: IntoIterator<Item = (T, usize)>>(&mut self, iter: I) {
for (e, n) in iter {
self.count += n;
if n != 0 {
*self.items.entry(e).or_insert(0) += n;
}
}
}
}
impl<T, S> std::iter::FromIterator<T> for HashBag<T, S>
where
T: Eq + Hash,
S: BuildHasher + Default,
{
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut bag = Self::default();
bag.extend(iter);
bag
}
}
impl<T, S> std::iter::FromIterator<(T, usize)> for HashBag<T, S>
where
T: Eq + Hash,
S: BuildHasher + Default,
{
fn from_iter<I: IntoIterator<Item = (T, usize)>>(iter: I) -> Self {
let mut bag = Self::default();
bag.extend(iter);
bag
}
}
impl<'a, T, S> IntoIterator for &'a HashBag<T, S> {
type Item = &'a T;
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Iter<'a, T> {
self.iter()
}
}
impl<T, S> IntoIterator for HashBag<T, S> {
type Item = (T, usize);
type IntoIter = IntoIter<T>;
fn into_iter(self) -> IntoIter<T> {
IntoIter(self.items.into_iter())
}
}
#[cfg(feature = "amortize")]
pub(crate) mod entry {
use griddle::hash_map::Entry;
#[derive(Debug)]
pub(crate) struct ForiegnEntry<'a, T, S> {
pub(crate) entry: Entry<'a, T, usize, S>,
}
impl<'a, T, S> ForiegnEntry<'a, T, S> {
pub(crate) fn new(entry: Entry<'a, T, usize, S>) -> Self {
Self { entry }
}
pub(crate) fn get_mut(&mut self) -> Option<&mut usize> {
match &mut self.entry {
Entry::Occupied(entry) => Some(entry.get_mut()),
Entry::Vacant(_) => None,
}
}
}
}
#[cfg(not(feature = "amortize"))]
pub(crate) mod entry {
use std::{collections::hash_map::Entry, marker::PhantomData};
#[derive(Debug)]
pub(crate) struct ForiegnEntry<'a, T, S> {
pub(crate) entry: Entry<'a, T, usize>,
data: PhantomData<S>,
}
impl<'a, T, S> ForiegnEntry<'a, T, S> {
pub(crate) fn new(entry: Entry<'a, T, usize>) -> Self {
Self {
entry,
data: PhantomData,
}
}
pub(crate) fn get_mut(&mut self) -> Option<&mut usize> {
match &mut self.entry {
Entry::Occupied(entry) => Some(entry.get_mut()),
Entry::Vacant(_) => None,
}
}
}
}
use entry::ForiegnEntry;
type EntryInner<'a, T, S> = (ForiegnEntry<'a, T, S>, &'a mut usize, PhantomData<S>);
#[derive(Debug)]
pub struct Entry<'a, T, S>(EntryInner<'a, T, S>);
impl<'a, T, S> Entry<'a, T, S>
where
T: Hash + Eq,
S: BuildHasher,
{
pub fn and_modify<F>(mut self, f: F) -> Self
where
F: FnOnce(&mut usize),
{
if let Some(n) = self.0 .0.get_mut() {
let init = *n;
f(n);
*self.0 .1 += *n;
*self.0 .1 -= init;
}
Self((self.0 .0, self.0 .1, PhantomData))
}
pub fn value(&self) -> &T {
self.0 .0.entry.key()
}
pub fn or_insert(mut self) -> usize {
if self.0 .0.get_mut().is_none() {
*self.0 .1 += 1;
}
*self.0 .0.entry.or_insert(1)
}
pub fn or_insert_many(mut self, quantity: usize) -> usize {
if self.0 .0.get_mut().is_none() {
*self.0 .1 += quantity;
}
*self.0 .0.entry.or_insert(quantity)
}
}
#[cfg(feature = "amortize")]
type IterInner<'a, T> = griddle::hash_map::Iter<'a, T, usize>;
#[cfg(not(feature = "amortize"))]
type IterInner<'a, T> = std::collections::hash_map::Iter<'a, T, usize>;
pub struct Iter<'a, T> {
iter: IterInner<'a, T>,
repeat: Option<(&'a T, usize)>,
left: usize,
}
impl<'a, T> std::iter::FusedIterator for Iter<'a, T> where IterInner<'a, T>: std::iter::FusedIterator
{}
impl<'a, T> ExactSizeIterator for Iter<'a, T> where IterInner<'a, T>: ExactSizeIterator {}
impl<'a, T> Clone for Iter<'a, T> {
fn clone(&self) -> Self {
Iter {
iter: self.iter.clone(),
repeat: self.repeat,
left: self.left,
}
}
}
impl<T: fmt::Debug> fmt::Debug for Iter<'_, T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_set().entries(self.clone()).finish()
}
}
impl<'a, T> Iter<'a, T> {
fn new(it: IterInner<'a, T>, n: usize) -> Self {
Self {
iter: it,
repeat: None,
left: n,
}
}
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
if let Some((t, ref mut n)) = self.repeat {
if *n == 0 {
self.repeat = None;
} else {
*n -= 1;
self.left -= 1;
return Some(t);
}
}
let (next, n) = self.iter.next()?;
if *n > 1 {
self.repeat = Some((next, *n - 1));
}
self.left -= 1;
Some(next)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.left, Some(self.left))
}
}
pub struct SetIter<'a, T>(IterInner<'a, T>);
impl<'a, T> std::iter::FusedIterator for SetIter<'a, T> where
IterInner<'a, T>: std::iter::FusedIterator
{
}
impl<'a, T> ExactSizeIterator for SetIter<'a, T> where IterInner<'a, T>: ExactSizeIterator {}
impl<'a, T> Clone for SetIter<'a, T> {
fn clone(&self) -> Self {
SetIter(self.0.clone())
}
}
impl<T: fmt::Debug> fmt::Debug for SetIter<'_, T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_set().entries(self.clone()).finish()
}
}
impl<'a, T> Iterator for SetIter<'a, T> {
type Item = (&'a T, usize);
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(t, n)| (t, *n))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
#[cfg(feature = "amortize")]
type IntoIterInner<T> = griddle::hash_map::IntoIter<T, usize>;
#[cfg(not(feature = "amortize"))]
type IntoIterInner<T> = std::collections::hash_map::IntoIter<T, usize>;
pub struct IntoIter<T>(IntoIterInner<T>);
impl<T> std::iter::FusedIterator for IntoIter<T> where IntoIterInner<T>: std::iter::FusedIterator {}
impl<T> ExactSizeIterator for IntoIter<T> where IntoIterInner<T>: ExactSizeIterator {}
impl<T: fmt::Debug> fmt::Debug for IntoIter<T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(fmt)
}
}
impl<T> Iterator for IntoIter<T> {
type Item = (T, usize);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
#[cfg(feature = "amortize")]
type DrainInner<'a, T> = griddle::hash_map::Drain<'a, T, usize>;
#[cfg(not(feature = "amortize"))]
type DrainInner<'a, T> = std::collections::hash_map::Drain<'a, T, usize>;
pub struct Drain<'a, T>(DrainInner<'a, T>);
impl<'a, T> std::iter::FusedIterator for Drain<'a, T> where
DrainInner<'a, T>: std::iter::FusedIterator
{
}
impl<'a, T> ExactSizeIterator for Drain<'a, T> where DrainInner<'a, T>: ExactSizeIterator {}
impl<'a, T: fmt::Debug> fmt::Debug for Drain<'a, T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(fmt)
}
}
impl<'a, T> Iterator for Drain<'a, T> {
type Item = (T, usize);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use std::iter::FromIterator;
use super::*;
#[test]
fn format_all_the_things() {
let mut vikings: HashBag<&'static str> =
["Einar", "Olaf", "Harald"].iter().cloned().collect();
println!("{:?}", vikings);
println!("{:?}", vikings.iter());
println!("{:?}", vikings.set_iter());
println!("{:?}", vikings.clone().into_iter());
println!("{:?}", vikings.drain());
}
#[test]
fn sane_iterators() {
let mut vikings: HashBag<&'static str> =
["Einar", "Einar", "Harald"].iter().cloned().collect();
assert_eq!(vikings.iter().count(), 3);
assert_eq!(vikings.iter().size_hint(), (3, Some(3)));
assert_eq!(vikings.iter().clone().count(), 3);
assert_eq!(vikings.set_iter().count(), 2);
assert_eq!(vikings.set_iter().clone().count(), 2);
assert_eq!(vikings.set_iter().size_hint(), (2, Some(2)));
let ii = vikings.clone().into_iter();
assert_eq!(ii.size_hint(), (2, Some(2)));
assert_eq!(ii.count(), 2);
let di = vikings.drain();
assert_eq!(di.size_hint(), (2, Some(2)));
assert_eq!(di.count(), 2);
}
#[test]
fn test_difference_size_hint() {
let bag: HashBag<_> = [3, 2, 1].iter().cloned().collect();
let empty_bag = HashBag::new();
let mut difference = bag.difference(&empty_bag);
assert_eq!(difference.size_hint(), (0, Some(3)));
difference.next().unwrap();
assert_eq!(difference.size_hint(), (0, Some(2)));
difference.next().unwrap();
assert_eq!(difference.size_hint(), (0, Some(1)));
difference.next().unwrap();
assert_eq!(difference.size_hint(), (0, Some(0)));
assert_eq!(difference.next(), None);
assert_eq!(difference.size_hint(), (0, Some(0)));
}
#[test]
fn test_difference_from_empty() {
do_test_difference(&[], &[], &[]);
do_test_difference(&[], &[1], &[]);
do_test_difference(&[], &[1, 1], &[]);
do_test_difference(&[], &[1, 1, 2], &[]);
}
#[test]
fn test_difference_from_one() {
do_test_difference(&[1], &[], &[1]);
do_test_difference(&[1], &[1], &[]);
do_test_difference(&[1], &[1, 1], &[]);
do_test_difference(&[1], &[2], &[1]);
do_test_difference(&[1], &[1, 2], &[]);
do_test_difference(&[1], &[2, 2], &[1]);
}
#[test]
fn test_difference_from_duplicate_ones() {
do_test_difference(&[1, 1], &[], &[1, 1]);
do_test_difference(&[1, 1], &[1], &[1]);
do_test_difference(&[1, 1], &[1, 1], &[]);
do_test_difference(&[1, 1], &[2], &[1, 1]);
do_test_difference(&[1, 1], &[1, 2], &[1]);
do_test_difference(&[1, 1], &[2, 2], &[1, 1]);
}
#[test]
fn test_difference_from_one_one_two() {
do_test_difference(&[1, 1, 2], &[], &[1, 1, 2]);
do_test_difference(&[1, 1, 2], &[1], &[1, 2]);
do_test_difference(&[1, 1, 2], &[1, 1], &[2]);
do_test_difference(&[1, 1, 2], &[2], &[1, 1]);
do_test_difference(&[1, 1, 2], &[1, 2], &[1]);
do_test_difference(&[1, 1, 2], &[2, 2], &[1, 1]);
}
#[test]
fn test_difference_from_larger_bags() {
do_test_difference(&[1, 2, 2, 3], &[3], &[1, 2, 2]);
do_test_difference(&[1, 2, 2, 3], &[4], &[1, 2, 2, 3]);
do_test_difference(&[2, 2, 2, 2], &[2, 2], &[2, 2]);
do_test_difference(&[2, 2, 2, 2], &[], &[2, 2, 2, 2]);
}
fn do_test_difference(
self_entries: &[isize],
other_entries: &[isize],
expected_entries: &[isize],
) {
let this = self_entries.iter().collect::<HashBag<_>>();
let other = other_entries.iter().collect::<HashBag<_>>();
let expected = expected_entries.iter().collect::<HashBag<_>>();
let mut actual = HashBag::new();
for (t, n) in this.difference(&other) {
actual.insert_many(*t, n);
}
assert_eq!(actual, expected);
}
#[test]
fn test_outer_join_order_with_disjoint_sets() {
do_test_outer_join_order(&[1, 2, 3], &[4, 5, 6]);
do_test_outer_join_order(&[1, 2, 2, 3], &[4, 4, 5, 6]);
}
#[test]
fn test_outer_join_order_with_overlap() {
do_test_outer_join_order(&[1, 2, 3], &[2, 3, 4]);
do_test_outer_join_order(&[1, 1, 2, 3], &[2, 3, 3, 3, 4]);
}
fn do_test_outer_join_order(this: &[usize], other: &[usize]) {
let this_hashbag: HashBag<usize> = this.iter().cloned().collect();
let other_hashbag: HashBag<usize> = other.iter().cloned().collect();
let min_other_exclusive_key_idx = this_hashbag
.outer_join(&other_hashbag)
.enumerate()
.find(|(_, (_, self_count, _))| self_count == &0)
.map(|(idx, _)| idx);
if let Some(idx) = min_other_exclusive_key_idx {
assert_eq!(idx, this_hashbag.set_len());
}
}
#[test]
fn test_outer_join_with_empty_self() {
do_test_outer_join(&[], &[1, 2, 2, 3], &[(&1, 0, 1), (&2, 0, 2), (&3, 0, 1)]);
}
#[test]
fn test_outer_join_with_empty_other() {
do_test_outer_join(&[1, 2, 2, 3], &[], &[(&1, 1, 0), (&2, 2, 0), (&3, 1, 0)]);
}
#[test]
fn test_outer_join_with_overlap() {
do_test_outer_join(
&[1, 2, 2, 3, 3],
&[3, 4, 5, 5],
&[(&1, 1, 0), (&2, 2, 0), (&3, 2, 1), (&4, 0, 1), (&5, 0, 2)],
);
}
fn do_test_outer_join(
this: &[usize],
other: &[usize],
expected_entries: &[(&usize, usize, usize)],
) {
let this_hashbag: HashBag<_> = this.iter().cloned().collect();
let other_hashbag: HashBag<_> = other.iter().cloned().collect();
let expected: HashSet<_> = HashSet::from_iter(expected_entries.iter().cloned());
let actual: HashSet<_> = this_hashbag.outer_join(&other_hashbag).collect();
assert_eq!(expected, actual);
}
#[test]
fn test_not_in_with_empty_self() {
do_test_not_in(&[], &[1, 2, 3, 3], &[]);
}
#[test]
fn test_not_in_with_empty_other() {
do_test_not_in(&[1, 2, 3, 3], &[], &[1, 2, 3, 3]);
}
#[test]
fn test_not_in_with_overlap() {
do_test_not_in(&[1, 2, 3, 3], &[2, 4], &[1, 3, 3]);
}
fn do_test_not_in(this: &[usize], other: &[usize], expected_entries: &[usize]) {
let this_hashbag: HashBag<_> = this.iter().cloned().collect();
let other_hashbag: HashBag<_> = other.iter().cloned().collect();
let expected: HashBag<_> = expected_entries.iter().cloned().collect();
let actual: HashBag<_> =
this_hashbag
.not_in(&other_hashbag)
.fold(HashBag::new(), |mut bag, (k, count)| {
bag.insert_many(*k, count);
bag
});
assert_eq!(expected, actual);
}
#[test]
fn test_signed_difference_with_empty_self() {
do_test_signed_difference(&[], &[1, 2, 2, 3], &[(&1, -1), (&2, -2), (&3, -1)]);
}
#[test]
fn test_signed_difference_with_empty_other() {
do_test_signed_difference(&[1, 2, 2, 3], &[], &[(&1, 1), (&2, 2), (&3, 1)]);
}
#[test]
fn test_signed_difference_with_overlap() {
do_test_signed_difference(
&[1, 2, 2, 3, 3],
&[3, 4, 5, 5],
&[(&1, 1), (&2, 2), (&3, 1), (&4, -1), (&5, -2)],
);
}
#[test]
fn test_signed_difference_with_both_large() {
let mut this_hashbag = HashBag::new();
let mut other_hashbag = HashBag::new();
let large_count = std::isize::MAX as usize;
this_hashbag.insert_many(1, large_count + 1000);
other_hashbag.insert_many(1, large_count);
let expected: HashSet<_> = HashSet::from_iter([(&1, 1000)].iter().cloned());
let actual: HashSet<_> = this_hashbag.signed_difference(&other_hashbag).collect();
assert_eq!(expected, actual);
let expected: HashSet<_> = HashSet::from_iter([(&1, -1000)].iter().cloned());
let actual: HashSet<_> = other_hashbag.signed_difference(&this_hashbag).collect();
assert_eq!(expected, actual);
}
#[test]
fn test_signed_difference_too_large_to_hold_clamp() {
let mut this_hashbag = HashBag::new();
let empty_hashbag = HashBag::new();
let large_count = std::isize::MAX as usize;
this_hashbag.insert_many(1, large_count + 1000);
let expected: HashSet<_> = HashSet::from_iter([(&1, std::isize::MAX)].iter().cloned());
let actual: HashSet<_> = this_hashbag.signed_difference(&empty_hashbag).collect();
assert_eq!(expected, actual);
let expected: HashSet<_> = HashSet::from_iter([(&1, std::isize::MIN)].iter().cloned());
let actual: HashSet<_> = empty_hashbag.signed_difference(&this_hashbag).collect();
assert_eq!(expected, actual);
}
fn do_test_signed_difference(
this: &[usize],
other: &[usize],
expected_entries: &[(&usize, isize)],
) {
let this_hashbag: HashBag<_> = this.iter().cloned().collect();
let other_hashbag: HashBag<_> = other.iter().cloned().collect();
let expected: HashSet<_> = HashSet::from_iter(expected_entries.iter().cloned());
let actual: HashSet<_> = this_hashbag.signed_difference(&other_hashbag).collect();
assert_eq!(expected, actual);
}
#[test]
fn test_no_zeros_counts() {
let mut hashbag = HashBag::new();
hashbag.insert(100);
hashbag.retain(|_, _| 0);
hashbag.insert_many(1, 0);
hashbag.extend(vec![(2, 0)]);
assert_eq!(hashbag.len(), 0);
assert_eq!(hashbag.iter().count(), 0);
assert_eq!(hashbag.set_len(), 0);
assert_eq!(hashbag.set_iter().count(), 0);
}
#[test]
fn remove_up_to_affects_count() {
let mut bag = HashBag::new();
bag.insert_many(42, 3);
assert_eq!(bag.len(), 3);
assert_eq!(bag.remove_up_to(&0, 1), 0);
assert_eq!(bag.len(), 3);
assert_eq!(bag.remove_up_to(&42, 1), 2);
assert_eq!(bag.len(), 2);
assert_eq!(bag.remove_up_to(&42, 10), 0);
assert_eq!(bag.len(), 0);
}
#[test]
fn entry_inserts_values() {
let mut bag = HashBag::new();
bag.entry(42);
assert_eq!(bag.contains(&42), 0);
bag.entry(84).or_insert_many(3);
assert_eq!(bag.contains(&84), 3);
assert_eq!(bag.len(), 3);
bag.entry(84).or_insert_many(2);
assert_eq!(bag.len(), 3);
bag.entry(84).or_insert();
assert_eq!(bag.len(), 3);
}
#[test]
fn entry_affects_count() {
let mut bag = HashBag::new();
bag.entry(42);
assert_eq!(bag.len(), 0);
bag.entry(42).and_modify(|n| *n += 3);
assert_eq!(bag.len(), 0);
bag.entry(42).or_insert_many(3);
assert_eq!(bag.len(), 3);
bag.entry(42).and_modify(|n| *n += 3);
assert_eq!(bag.len(), 6);
bag.entry(84).or_insert();
assert_eq!(bag.len(), 7);
}
}