use std::collections::HashMap;
use std::hash::Hash;
pub type EffectHashMap<K, V> = im::HashMap<K, V>;
#[inline]
pub fn empty<K, V>() -> EffectHashMap<K, V>
where
K: Hash + Eq + Clone,
V: Clone,
{
EffectHashMap::new()
}
#[inline]
pub fn from_iter<K, V, I>(iter: I) -> EffectHashMap<K, V>
where
I: IntoIterator<Item = (K, V)>,
K: Hash + Eq + Clone,
V: Clone,
{
iter.into_iter().collect()
}
#[inline]
pub fn get<'a, K, V, Q>(map: &'a EffectHashMap<K, V>, key: &Q) -> Option<&'a V>
where
Q: Hash + Eq + ?Sized,
K: Hash + Eq + Clone + std::borrow::Borrow<Q>,
V: Clone,
{
map.get(key)
}
#[inline]
pub fn has<K, V, Q>(map: &EffectHashMap<K, V>, key: &Q) -> bool
where
Q: Hash + Eq + ?Sized,
K: Hash + Eq + Clone + std::borrow::Borrow<Q>,
V: Clone,
{
map.contains_key(key)
}
#[inline]
pub fn set<K, V>(map: &EffectHashMap<K, V>, key: K, value: V) -> EffectHashMap<K, V>
where
K: Hash + Eq + Clone,
V: Clone,
{
map.update(key, value)
}
#[inline]
pub fn remove<K, V, Q>(map: &EffectHashMap<K, V>, key: &Q) -> EffectHashMap<K, V>
where
Q: Hash + Eq + ?Sized,
K: Hash + Eq + Clone + std::borrow::Borrow<Q>,
V: Clone,
{
map.without(key)
}
#[inline]
pub fn modify<K, V, F>(map: &EffectHashMap<K, V>, key: K, f: F) -> EffectHashMap<K, V>
where
K: Hash + Eq + Clone,
V: Clone,
F: FnOnce(Option<V>) -> Option<V>,
{
map.alter(f, key)
}
#[inline]
pub fn modify_at<K, V, F>(map: &EffectHashMap<K, V>, key: K, f: F) -> EffectHashMap<K, V>
where
K: Hash + Eq + Clone,
V: Clone,
F: FnOnce(&V) -> V,
{
match map.get(&key) {
Some(v) => map.update(key, f(v)),
None => map.clone(),
}
}
#[inline]
pub fn map_values<K, V, W, F>(map: EffectHashMap<K, V>, mut f: F) -> EffectHashMap<K, W>
where
K: Hash + Eq + Clone,
V: Clone,
W: Clone,
F: FnMut(V) -> W,
{
map.into_iter().map(|(k, v)| (k, f(v))).collect()
}
#[inline]
pub fn filter<K, V, F>(map: &EffectHashMap<K, V>, mut pred: F) -> EffectHashMap<K, V>
where
K: Hash + Eq + Clone,
V: Clone,
F: FnMut(&K, &V) -> bool,
{
map
.iter()
.filter(|(k, v)| pred(k, v))
.map(|(k, v)| (k.clone(), v.clone()))
.collect()
}
#[inline]
pub fn reduce<K, V, Acc, F>(map: &EffectHashMap<K, V>, init: Acc, f: F) -> Acc
where
K: Hash + Eq + Clone,
V: Clone,
F: FnMut(Acc, (&K, &V)) -> Acc,
{
map.iter().fold(init, f)
}
#[inline]
pub fn union<K, V>(left: EffectHashMap<K, V>, right: EffectHashMap<K, V>) -> EffectHashMap<K, V>
where
K: Hash + Eq + Clone,
V: Clone,
{
left.union(right)
}
#[inline]
pub fn keys<K, V>(map: &EffectHashMap<K, V>) -> Vec<K>
where
K: Hash + Eq + Clone,
V: Clone,
{
map.keys().cloned().collect()
}
#[inline]
pub fn values<K, V>(map: &EffectHashMap<K, V>) -> Vec<V>
where
K: Hash + Eq + Clone,
V: Clone,
{
map.values().cloned().collect()
}
#[inline]
pub fn size<K, V>(map: &EffectHashMap<K, V>) -> usize
where
K: Hash + Eq + Clone,
V: Clone,
{
map.len()
}
#[inline]
pub fn is_empty<K, V>(map: &EffectHashMap<K, V>) -> bool
where
K: Hash + Eq + Clone,
V: Clone,
{
map.is_empty()
}
#[inline]
pub fn pop<K, V, Q>(map: &EffectHashMap<K, V>, key: &Q) -> Option<(V, EffectHashMap<K, V>)>
where
Q: Hash + Eq + ?Sized,
K: Hash + Eq + Clone + std::borrow::Borrow<Q>,
V: Clone,
{
map.extract(key)
}
#[derive(Debug, Clone, Default)]
pub struct MutableHashMap<K, V>(
pub HashMap<K, V>,
);
impl<K: Hash + Eq, V> MutableHashMap<K, V> {
#[inline]
pub fn new() -> Self {
Self(HashMap::new())
}
#[inline]
pub fn get<Q: Hash + Eq + ?Sized>(&self, key: &Q) -> Option<&V>
where
K: std::borrow::Borrow<Q>,
{
self.0.get(key)
}
#[inline]
pub fn has<Q: Hash + Eq + ?Sized>(&self, key: &Q) -> bool
where
K: std::borrow::Borrow<Q>,
{
self.0.contains_key(key)
}
#[inline]
pub fn set(&mut self, key: K, value: V) {
self.0.insert(key, value);
}
#[inline]
pub fn remove<Q: Hash + Eq + ?Sized>(&mut self, key: &Q) -> Option<V>
where
K: std::borrow::Borrow<Q>,
{
self.0.remove(key)
}
#[inline]
pub fn modify<F>(&mut self, key: K, f: F)
where
F: FnOnce(Option<V>) -> Option<V>,
{
let old = self.0.remove(&key);
if let Some(v) = f(old) {
self.0.insert(key, v);
}
}
#[inline]
pub fn modify_at<F>(&mut self, key: K, f: F)
where
F: FnOnce(&V) -> V,
{
if let Some(v) = self.0.get(&key) {
let nv = f(v);
self.0.insert(key, nv);
}
}
#[inline]
pub fn keys(&self) -> Vec<K>
where
K: Clone,
{
self.0.keys().cloned().collect()
}
#[inline]
pub fn values(&self) -> Vec<V>
where
V: Clone,
{
self.0.values().cloned().collect()
}
#[inline]
pub fn size(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn pop(&mut self, key: &K) -> Option<V> {
self.0.remove(key)
}
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
#[test]
fn hash_map_set_then_get_returns_value() {
let m = empty::<&str, i32>();
let m = set(&m, "a", 1);
assert_eq!(get(&m, "a"), Some(&1));
}
#[test]
fn hash_map_remove_absent_key_is_noop() {
let m = empty::<i32, i32>();
let m2 = remove(&m, &1i32);
assert_eq!(m, m2);
assert_eq!(size(&m2), 0);
}
#[test]
fn hash_map_union_prefers_left_on_conflict() {
let a = from_iter([(1, 10), (2, 20)]);
let b = from_iter([(2, 99), (3, 30)]);
let u = union(a, b);
assert_eq!(get(&u, &1), Some(&10));
assert_eq!(get(&u, &2), Some(&20));
assert_eq!(get(&u, &3), Some(&30));
}
#[rstest]
#[case::empty(empty::<i32,i32>(), 0)]
#[case::single(from_iter([(1, 1)]), 1)]
#[case::multi(from_iter([(1, 1), (2, 2), (3, 3)]), 3)]
fn hash_map_size_tracks_entries(#[case] m: EffectHashMap<i32, i32>, #[case] expected: usize) {
assert_eq!(size(&m), expected);
}
#[test]
fn mutable_hash_map_mutates_in_place() {
let mut m = MutableHashMap::new();
m.set("x", 1i32);
assert_eq!(m.get("x"), Some(&1));
m.set("x", 2);
assert_eq!(m.get("x"), Some(&2));
assert_eq!(m.pop(&"x"), Some(2));
assert!(m.is_empty());
}
#[test]
fn modify_removes_when_closure_returns_none() {
let m = set(&empty(), "k", 1);
let m2 = modify(&m, "k", |_| None::<i32>);
assert!(!has(&m2, "k"));
}
#[test]
fn pop_returns_value_and_new_map() {
let m = set(&empty(), 1u8, "a");
let (v, rest) = pop(&m, &1u8).expect("pop");
assert_eq!(v, "a");
assert!(is_empty(&rest));
}
#[test]
fn modify_at_updates_existing_key() {
let m = set(&empty(), "a", 1i32);
let m2 = modify_at(&m, "a", |v| v + 10);
assert_eq!(get(&m2, "a"), Some(&11));
}
#[test]
fn modify_at_noop_when_key_missing() {
let m = set(&empty(), "a", 1i32);
let m2 = modify_at(&m, "b", |v| v + 10);
assert_eq!(get(&m2, "b"), None);
assert_eq!(get(&m2, "a"), Some(&1));
}
#[test]
fn map_values_transforms_all() {
let m = from_iter([("a", 1i32), ("b", 2)]);
let m2 = map_values(m, |v| v * 2);
assert_eq!(get(&m2, "a"), Some(&2));
assert_eq!(get(&m2, "b"), Some(&4));
}
#[test]
fn filter_keeps_matching_entries() {
let m = from_iter([(1i32, 10), (2, 20), (3, 30)]);
let m2 = filter(&m, |k, _v| *k > 1);
assert!(!has(&m2, &1));
assert!(has(&m2, &2));
assert!(has(&m2, &3));
}
#[test]
fn reduce_sums_values() {
let m = from_iter([("a", 1i32), ("b", 2), ("c", 3)]);
let total = reduce(&m, 0, |acc, (_, v)| acc + v);
assert_eq!(total, 6);
}
#[test]
fn keys_and_values_return_all_entries() {
let m = from_iter([(1i32, "a"), (2, "b")]);
let mut ks = keys(&m);
ks.sort();
assert_eq!(ks, vec![1, 2]);
assert_eq!(values(&m).len(), 2);
}
#[test]
fn is_empty_and_size_on_empty_map() {
let m = empty::<i32, i32>();
assert!(is_empty(&m));
assert_eq!(size(&m), 0);
}
#[test]
fn mutable_hash_map_modify_at_updates_existing() {
let mut m = MutableHashMap::new();
m.set("x", 5i32);
m.modify_at("x", |v| v + 1);
assert_eq!(m.get("x"), Some(&6));
}
#[test]
fn mutable_hash_map_modify_at_noop_when_missing() {
let mut m = MutableHashMap::<&str, i32>::new();
m.modify_at("missing", |v| v + 1);
assert_eq!(m.get("missing"), None);
}
#[test]
fn mutable_hash_map_has_and_remove() {
let mut m = MutableHashMap::new();
m.set(1i32, "a");
assert!(m.has(&1));
assert!(!m.has(&2));
let old = m.remove(&1);
assert_eq!(old, Some("a"));
assert!(!m.has(&1));
}
#[test]
fn mutable_hash_map_keys_and_values() {
let mut m = MutableHashMap::new();
m.set(1i32, "a");
m.set(2, "b");
let mut ks = m.keys();
ks.sort();
assert_eq!(ks, vec![1, 2]);
assert_eq!(m.values().len(), 2);
assert_eq!(m.size(), 2);
assert!(!m.is_empty());
}
#[test]
fn mutable_hash_map_modify_deletes_when_none() {
let mut m = MutableHashMap::new();
m.set("k", 10i32);
m.modify("k", |_| None);
assert!(!m.has("k"));
}
#[test]
fn mutable_hash_map_modify_inserts_when_missing() {
let mut m = MutableHashMap::<&str, i32>::new();
m.modify("k", |_| Some(99));
assert_eq!(m.get("k"), Some(&99));
}
}