use std::collections::HashSet;
use rand::{Rng, rng};
pub trait PoolItem: std::hash::Hash + Eq + Sync {}
impl<T> PoolItem for T where T: std::hash::Hash + Eq + Sync {}
pub trait ItemPool<T: PoolItem> {
fn pool(&mut self) -> &mut Vec<T>;
fn put(&mut self, item: T) {
self.pool().push(item);
}
fn remove_one(&mut self) -> Option<T> {
let store = self.pool();
if store.is_empty() {
None
} else {
Some(store.remove(rng().random_range(0..store.len())))
}
}
fn remove_many(&mut self, size: usize) -> Vec<T> {
let store = self.pool();
debug_assert!(!store.is_empty(), "The pool is empty");
let mut remaining = store.len();
debug_assert!(
size <= remaining,
"Requested size exceeds available items in the pool: {} <= {}",
size,
remaining
);
let mut values = Vec::with_capacity(size);
for _ in 0..size {
values.push(store.remove(rng().random_range(0..remaining)));
remaining -= 1;
}
values
}
fn remove_set(&mut self, mut size: usize) -> HashSet<T> {
let store = self.pool();
debug_assert!(!store.is_empty(), "The pool is empty");
let mut remaining = store.len();
debug_assert!(
size <= remaining,
"Requested size exceeds available items in the pool: {} <= {}",
size,
remaining
);
let mut colliding = Vec::new();
let mut values = HashSet::new();
while size - colliding.len() > 0 {
let draw = store.remove(rng().random_range(0..remaining));
match values.contains(&draw) {
true => colliding.push(draw),
false => {
values.insert(draw);
size -= 1;
remaining -= 1;
}
}
}
store.append(&mut colliding);
values
}
}
pub trait RecyclingItemPool<T: PoolItem>: ItemPool<T> {
fn get_discard_pool(&mut self) -> &mut Vec<T>;
fn discard_one(&mut self, item: T) {
self.get_discard_pool().push(item);
}
fn recycle_discarded(&mut self) {
let mut discard_pool = std::mem::take(self.get_discard_pool());
self.pool().append(&mut discard_pool);
}
fn get_set(&mut self, size: usize) -> HashSet<T> {
self.recycle_discarded();
ItemPool::remove_set(self, size)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct TestItem(i32);
struct TestPool {
items: Vec<TestItem>,
}
impl ItemPool<TestItem> for TestPool {
fn pool(&mut self) -> &mut Vec<TestItem> {
&mut self.items
}
}
struct TestRecyclingPool {
items: Vec<TestItem>,
discarded: Vec<TestItem>,
}
impl ItemPool<TestItem> for TestRecyclingPool {
fn pool(&mut self) -> &mut Vec<TestItem> {
&mut self.items
}
}
impl RecyclingItemPool<TestItem> for TestRecyclingPool {
fn get_discard_pool(&mut self) -> &mut Vec<TestItem> {
&mut self.discarded
}
}
#[test]
fn test_put() {
let mut pool = TestPool { items: vec![] };
pool.put(TestItem(1));
pool.put(TestItem(2));
assert_eq!(pool.pool().len(), 2);
}
#[test]
fn test_remove_one_empty() {
let mut pool = TestPool { items: vec![] };
assert_eq!(pool.remove_one(), None);
}
#[test]
fn test_remove_one_success() {
let mut pool = TestPool {
items: vec![TestItem(1), TestItem(2), TestItem(3)],
};
let item = pool.remove_one();
assert!(item.is_some());
assert_eq!(pool.pool().len(), 2);
}
#[test]
fn test_remove_many() {
let mut pool = TestPool {
items: (0..10).map(TestItem).collect(),
};
let items = pool.remove_many(5);
assert_eq!(items.len(), 5);
assert_eq!(pool.pool().len(), 5);
}
#[test]
fn test_remove_set_uniqueness() {
let mut pool = TestPool {
items: (0..10).map(TestItem).collect(),
};
let set = pool.remove_set(5);
assert_eq!(set.len(), 5);
assert_eq!(pool.pool().len(), 5);
let vec: Vec<_> = set.into_iter().collect();
let unique_set: HashSet<_> = vec.iter().copied().collect();
assert_eq!(vec.len(), unique_set.len());
}
#[test]
fn test_remove_set_all_items() {
let mut pool = TestPool {
items: (0..5).map(TestItem).collect(),
};
let set = pool.remove_set(5);
assert_eq!(set.len(), 5);
assert_eq!(pool.pool().len(), 0);
}
#[test]
fn test_discard_and_recycle() {
let mut pool = TestRecyclingPool {
items: (0..5).map(TestItem).collect(),
discarded: vec![],
};
pool.discard_one(TestItem(10));
pool.discard_one(TestItem(11));
assert_eq!(pool.get_discard_pool().len(), 2);
assert_eq!(pool.pool().len(), 5);
pool.recycle_discarded();
assert_eq!(pool.get_discard_pool().len(), 0);
assert_eq!(pool.pool().len(), 7);
}
#[test]
fn test_get_set_with_recycling() {
let mut pool = TestRecyclingPool {
items: (0..3).map(TestItem).collect(),
discarded: (3..7).map(TestItem).collect(),
};
let set = pool.get_set(5);
assert_eq!(set.len(), 5);
assert_eq!(pool.get_discard_pool().len(), 0);
assert_eq!(pool.pool().len(), 2);
}
#[test]
fn test_multiple_operations() {
let mut pool = TestRecyclingPool {
items: (0..10).map(TestItem).collect(),
discarded: vec![],
};
let _first = pool.remove_one();
assert_eq!(pool.pool().len(), 9);
let _set = pool.remove_set(3);
assert_eq!(pool.pool().len(), 6);
pool.discard_one(TestItem(100));
pool.recycle_discarded();
assert_eq!(pool.pool().len(), 7);
}
#[test]
fn test_pool_trait_object_safety() {
let mut pool = TestPool {
items: vec![TestItem(1), TestItem(2)],
};
let item_ref: &mut dyn ItemPool<TestItem> = &mut pool;
item_ref.put(TestItem(3));
assert_eq!(item_ref.pool().len(), 3);
}
}