use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
pub struct FoldingSet<T: FoldingSetNode> {
nodes: HashMap<u64, Vec<T>>,
size: usize,
}
pub trait FoldingSetNode: Clone + Eq + Hash {
fn profile(&self) -> u64;
fn equals(&self, other: &Self) -> bool {
self == other
}
}
impl<T: FoldingSetNode> FoldingSet<T> {
pub fn new() -> Self {
Self {
nodes: HashMap::new(),
size: 0,
}
}
pub fn insert(&mut self, node: T) -> T {
let profile = node.profile();
let bucket = self.nodes.entry(profile).or_insert_with(Vec::new);
for existing in bucket.iter() {
if node.equals(existing) {
return existing.clone();
}
}
bucket.push(node.clone());
self.size += 1;
node
}
pub fn find(&self, profile: u64, equals_fn: impl Fn(&T) -> bool) -> Option<&T> {
self.nodes
.get(&profile)
.and_then(|bucket| bucket.iter().find(|n| equals_fn(n)))
}
pub fn remove(&mut self, profile: u64, equals_fn: impl Fn(&T) -> bool) -> Option<T> {
if let Some(bucket) = self.nodes.get_mut(&profile) {
if let Some(pos) = bucket.iter().position(|n| equals_fn(n)) {
let removed = bucket.remove(pos);
self.size -= 1;
if bucket.is_empty() {
self.nodes.remove(&profile);
}
return Some(removed);
}
}
None
}
pub fn len(&self) -> usize {
self.size
}
pub fn is_empty(&self) -> bool {
self.size == 0
}
pub fn clear(&mut self) {
self.nodes.clear();
self.size = 0;
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.nodes.values().flat_map(|bucket| bucket.iter())
}
pub fn contains(&self, profile: u64, equals_fn: impl Fn(&T) -> bool) -> bool {
self.find(profile, equals_fn).is_some()
}
}
impl<T: FoldingSetNode> Default for FoldingSet<T> {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct FoldingSetNodeID {
hash: u64,
}
impl FoldingSetNodeID {
pub fn new() -> Self {
Self {
hash: 0x9e37_79b9_7f4a_7c15,
} }
pub fn add_integer(&mut self, val: u64) {
self.hash ^= val;
self.hash = self.hash.wrapping_mul(0x100_0000_01b3);
}
pub fn add_bytes(&mut self, bytes: &[u8]) {
for &b in bytes {
self.hash ^= b as u64;
self.hash = self.hash.wrapping_mul(0x100_0000_01b3);
}
}
pub fn add_string(&mut self, s: &str) {
self.add_bytes(s.as_bytes());
}
pub fn add_boolean(&mut self, b: bool) {
self.add_integer(b as u64);
}
pub fn compute_hash(&self) -> u64 {
self.hash
}
}
impl Default for FoldingSetNodeID {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct TestNode {
id: u32,
value: String,
}
impl FoldingSetNode for TestNode {
fn profile(&self) -> u64 {
let mut nid = FoldingSetNodeID::new();
nid.add_integer(self.id as u64);
nid.add_string(&self.value);
nid.compute_hash()
}
}
#[test]
fn test_folding_set_insert_unique() {
let mut set = FoldingSet::new();
let n1 = TestNode {
id: 1,
value: "hello".into(),
};
let n2 = TestNode {
id: 2,
value: "world".into(),
};
set.insert(n1);
set.insert(n2);
assert_eq!(set.len(), 2);
}
#[test]
fn test_folding_set_insert_duplicate() {
let mut set = FoldingSet::new();
let n1 = TestNode {
id: 1,
value: "dup".into(),
};
let n2 = TestNode {
id: 1,
value: "dup".into(),
};
let r1 = set.insert(n1);
let r2 = set.insert(n2);
assert_eq!(set.len(), 1); assert_eq!(r1.id, r2.id);
}
#[test]
fn test_folding_set_find() {
let mut set = FoldingSet::new();
let n1 = TestNode {
id: 42,
value: "findme".into(),
};
set.insert(n1);
let mut nid = FoldingSetNodeID::new();
nid.add_integer(42);
nid.add_string("findme");
let profile = nid.compute_hash();
let found = set.find(profile, |n| n.id == 42);
assert!(found.is_some());
assert_eq!(found.unwrap().value, "findme");
}
#[test]
fn test_folding_set_remove() {
let mut set = FoldingSet::new();
let n1 = TestNode {
id: 1,
value: "remove".into(),
};
set.insert(n1);
let mut nid = FoldingSetNodeID::new();
nid.add_integer(1);
nid.add_string("remove");
let profile = nid.compute_hash();
let removed = set.remove(profile, |n| n.id == 1);
assert!(removed.is_some());
assert_eq!(set.len(), 0);
}
#[test]
fn test_folding_set_clear() {
let mut set = FoldingSet::new();
for i in 0..10 {
set.insert(TestNode {
id: i,
value: format!("node{}", i),
});
}
assert_eq!(set.len(), 10);
set.clear();
assert_eq!(set.len(), 0);
assert!(set.is_empty());
}
#[test]
fn test_node_id_hash() {
let mut nid = FoldingSetNodeID::new();
nid.add_integer(12345);
nid.add_string("test");
nid.add_boolean(true);
let h1 = nid.compute_hash();
let mut nid2 = FoldingSetNodeID::new();
nid2.add_integer(12345);
nid2.add_string("test");
nid2.add_boolean(true);
let h2 = nid2.compute_hash();
assert_eq!(h1, h2); }
}