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}