Skip to main content

solverforge_core/domain/supply/
inverse.rs

1/* Inverse variable supply for O(1) "who points to this value?" lookups.
2
3# Zero-Erasure Design
4
5- **Index-based**: Stores `value -> entity_index` mappings, not cloned entities
6- **Owned**: No `Arc`, `RwLock`, or interior mutability - uses `&mut self`
7- **Generic**: Full type information preserved, no `dyn Any`
8*/
9
10use std::collections::HashMap;
11use std::hash::Hash;
12
13/* Index-based inverse variable supply.
14
15For a chained variable where `entity.previous = value`, this supply answers:
16"Given `value`, which entity index has `entities[idx].previous == value`?"
17
18Returns entity indices - caller accesses actual entity via `solution.entities[idx]`.
19
20# Example
21
22```
23use solverforge_core::domain::supply::InverseSupply;
24
25let mut supply: InverseSupply<i32> = InverseSupply::new();
26
27// Entity at index 0 points to value 42
28supply.insert(42, 0);
29
30// Entity at index 1 points to value 99
31supply.insert(99, 1);
32
33assert_eq!(supply.get(&42), Some(0));
34assert_eq!(supply.get(&99), Some(1));
35assert_eq!(supply.get(&100), None);
36```
37*/
38#[derive(Debug)]
39pub struct InverseSupply<V>
40where
41    V: Eq + Hash,
42{
43    // Mapping from value to the entity index pointing to it.
44    inverse_map: HashMap<V, usize>,
45}
46
47impl<V> Default for InverseSupply<V>
48where
49    V: Eq + Hash,
50{
51    fn default() -> Self {
52        Self::new()
53    }
54}
55
56impl<V> InverseSupply<V>
57where
58    V: Eq + Hash,
59{
60    pub fn new() -> Self {
61        Self {
62            inverse_map: HashMap::new(),
63        }
64    }
65
66    pub fn with_capacity(capacity: usize) -> Self {
67        Self {
68            inverse_map: HashMap::with_capacity(capacity),
69        }
70    }
71
72    /* Gets the entity index that points to the given value.
73
74    Returns `None` if no entity currently points to this value.
75    */
76    #[inline]
77    pub fn get(&self, value: &V) -> Option<usize> {
78        self.inverse_map.get(value).copied()
79    }
80
81    // Registers that an entity index now points to a value.
82    //
83    #[inline]
84    pub fn insert(&mut self, value: V, entity_idx: usize) -> Option<usize> {
85        self.inverse_map.insert(value, entity_idx)
86    }
87
88    // Removes the mapping for a value.
89    //
90    #[inline]
91    pub fn remove(&mut self, value: &V) -> Option<usize> {
92        self.inverse_map.remove(value)
93    }
94
95    /* Updates the mapping: removes old value, inserts new.
96
97    Use this when an entity changes which value it points to.
98    */
99    #[inline]
100    pub fn update(&mut self, old_value: Option<&V>, new_value: V, entity_idx: usize) {
101        if let Some(old) = old_value {
102            self.remove(old);
103        }
104        self.insert(new_value, entity_idx);
105    }
106
107    // Clears all mappings.
108    #[inline]
109    pub fn clear(&mut self) {
110        self.inverse_map.clear();
111    }
112
113    #[inline]
114    pub fn len(&self) -> usize {
115        self.inverse_map.len()
116    }
117
118    // Returns true if no mappings exist.
119    #[inline]
120    pub fn is_empty(&self) -> bool {
121        self.inverse_map.is_empty()
122    }
123
124    // Returns an iterator over all (value, entity_index) pairs.
125    #[inline]
126    pub fn iter(&self) -> impl Iterator<Item = (&V, &usize)> {
127        self.inverse_map.iter()
128    }
129}