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
9use std::collections::HashMap;
10use std::hash::Hash;
11
12/// Index-based inverse variable supply.
13///
14/// For a chained variable where `entity.previous = value`, this supply answers:
15/// "Given `value`, which entity index has `entities[idx].previous == value`?"
16///
17/// Returns entity indices - caller accesses actual entity via `solution.entities[idx]`.
18///
19/// # Example
20///
21/// ```
22/// use solverforge_core::domain::supply::InverseSupply;
23///
24/// let mut supply: InverseSupply<i32> = InverseSupply::new();
25///
26/// // Entity at index 0 points to value 42
27/// supply.insert(42, 0);
28///
29/// // Entity at index 1 points to value 99
30/// supply.insert(99, 1);
31///
32/// assert_eq!(supply.get(&42), Some(0));
33/// assert_eq!(supply.get(&99), Some(1));
34/// assert_eq!(supply.get(&100), None);
35/// ```
36#[derive(Debug)]
37pub struct InverseSupply<V>
38where
39    V: Eq + Hash,
40{
41    /// Mapping from value to the entity index pointing to it.
42    inverse_map: HashMap<V, usize>,
43}
44
45impl<V> Default for InverseSupply<V>
46where
47    V: Eq + Hash,
48{
49    fn default() -> Self {
50        Self::new()
51    }
52}
53
54impl<V> InverseSupply<V>
55where
56    V: Eq + Hash,
57{
58    /// Creates a new empty inverse supply.
59    pub fn new() -> Self {
60        Self {
61            inverse_map: HashMap::new(),
62        }
63    }
64
65    /// Creates a new inverse supply with pre-allocated capacity.
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    #[inline]
76    pub fn get(&self, value: &V) -> Option<usize> {
77        self.inverse_map.get(value).copied()
78    }
79
80    /// Registers that an entity index now points to a value.
81    ///
82    /// Returns the previous entity index if this value was already mapped.
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    /// Returns the entity index that was mapped, if any.
91    #[inline]
92    pub fn remove(&mut self, value: &V) -> Option<usize> {
93        self.inverse_map.remove(value)
94    }
95
96    /// Updates the mapping: removes old value, inserts new.
97    ///
98    /// Use this when an entity changes which value it points to.
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    /// Returns the number of tracked mappings.
114    #[inline]
115    pub fn len(&self) -> usize {
116        self.inverse_map.len()
117    }
118
119    /// Returns true if no mappings exist.
120    #[inline]
121    pub fn is_empty(&self) -> bool {
122        self.inverse_map.is_empty()
123    }
124
125    /// Returns an iterator over all (value, entity_index) pairs.
126    #[inline]
127    pub fn iter(&self) -> impl Iterator<Item = (&V, &usize)> {
128        self.inverse_map.iter()
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    #[test]
137    fn test_insert_and_get() {
138        let mut supply: InverseSupply<i32> = InverseSupply::new();
139
140        supply.insert(1, 0);
141        supply.insert(2, 1);
142
143        assert_eq!(supply.get(&1), Some(0));
144        assert_eq!(supply.get(&2), Some(1));
145        assert_eq!(supply.get(&3), None);
146    }
147
148    #[test]
149    fn test_remove() {
150        let mut supply: InverseSupply<i32> = InverseSupply::new();
151
152        supply.insert(1, 0);
153        assert_eq!(supply.len(), 1);
154
155        let removed = supply.remove(&1);
156        assert_eq!(removed, Some(0));
157        assert_eq!(supply.len(), 0);
158        assert_eq!(supply.get(&1), None);
159    }
160
161    #[test]
162    fn test_update() {
163        let mut supply: InverseSupply<i32> = InverseSupply::new();
164
165        supply.insert(1, 0);
166        supply.update(Some(&1), 2, 0);
167
168        assert_eq!(supply.get(&1), None);
169        assert_eq!(supply.get(&2), Some(0));
170    }
171
172    #[test]
173    fn test_clear() {
174        let mut supply: InverseSupply<i32> = InverseSupply::new();
175
176        supply.insert(1, 0);
177        supply.insert(2, 1);
178        supply.insert(3, 2);
179
180        assert_eq!(supply.len(), 3);
181
182        supply.clear();
183
184        assert!(supply.is_empty());
185    }
186
187    #[test]
188    fn test_overwrite() {
189        let mut supply: InverseSupply<i32> = InverseSupply::new();
190
191        supply.insert(1, 0);
192        let old = supply.insert(1, 5);
193
194        assert_eq!(old, Some(0));
195        assert_eq!(supply.get(&1), Some(5));
196        assert_eq!(supply.len(), 1);
197    }
198
199    #[test]
200    fn test_iter() {
201        let mut supply: InverseSupply<i32> = InverseSupply::new();
202
203        supply.insert(10, 0);
204        supply.insert(20, 1);
205        supply.insert(30, 2);
206
207        let mut pairs: Vec<_> = supply.iter().map(|(&v, &i)| (v, i)).collect();
208        pairs.sort();
209
210        assert_eq!(pairs, vec![(10, 0), (20, 1), (30, 2)]);
211    }
212}