use core::{
hash::{Hash, Hasher},
ops::Bound,
};
use crate::{Domain, GenericRange};
impl<T: Domain + Hash> Hash for GenericRange<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
if <T as Domain>::DISCRETE {
match self.start {
Bound::Included(_) | Bound::Unbounded => self.start.hash(state),
Bound::Excluded(ref start) => match start.successor() {
None => self.start.hash(state),
Some(succ) => Bound::Included(succ).hash(state),
},
}
match self.end {
Bound::Included(_) | Bound::Unbounded => self.end.hash(state),
Bound::Excluded(ref end) => match end.predecessor() {
None => self.end.hash(state),
Some(pre) => Bound::Included(pre).hash(state),
},
}
} else {
self.start.hash(state);
self.end.hash(state);
}
}
}
#[cfg(test)]
mod tests {
use core::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;
use proptest::prelude::*;
use crate::GenericRange;
fn calculate_hash<T: Hash>(t: &T) -> u64 {
let mut s = DefaultHasher::new();
t.hash(&mut s);
s.finish()
}
#[test]
fn differing_range_same_hash() {
let range1 = GenericRange::from(5..10);
let range2 = GenericRange::new_open_closed(4, 9);
assert_eq!(range1, range2);
assert_eq!(calculate_hash(&range1), calculate_hash(&range2));
}
proptest! {
#[ignore]
#[test]
fn unique_hashes(range1 in any::<GenericRange<u8>>(), range2 in any::<GenericRange<u8>>()) {
let equality = range1.eq(&range2);
let hash1 = calculate_hash(&range1);
let hash2 = calculate_hash(&range2);
if equality {
prop_assert_eq!(hash1, hash2);
} else {
prop_assert_ne!(hash1, hash2);
}
}
}
}