solverforge-core 0.8.1

Core types and traits for SolverForge constraint solver
Documentation
/* Inverse variable supply for O(1) "who points to this value?" lookups.

# Zero-Erasure Design

- **Index-based**: Stores `value -> entity_index` mappings, not cloned entities
- **Owned**: No `Arc`, `RwLock`, or interior mutability - uses `&mut self`
- **Generic**: Full type information preserved, no `dyn Any`
*/

use std::collections::HashMap;
use std::hash::Hash;

/* Index-based inverse variable supply.

For a chained variable where `entity.previous = value`, this supply answers:
"Given `value`, which entity index has `entities[idx].previous == value`?"

Returns entity indices - caller accesses actual entity via `solution.entities[idx]`.

# Example

```
use solverforge_core::domain::supply::InverseSupply;

let mut supply: InverseSupply<i32> = InverseSupply::new();

// Entity at index 0 points to value 42
supply.insert(42, 0);

// Entity at index 1 points to value 99
supply.insert(99, 1);

assert_eq!(supply.get(&42), Some(0));
assert_eq!(supply.get(&99), Some(1));
assert_eq!(supply.get(&100), None);
```
*/
#[derive(Debug)]
pub struct InverseSupply<V>
where
    V: Eq + Hash,
{
    // Mapping from value to the entity index pointing to it.
    inverse_map: HashMap<V, usize>,
}

impl<V> Default for InverseSupply<V>
where
    V: Eq + Hash,
{
    fn default() -> Self {
        Self::new()
    }
}

impl<V> InverseSupply<V>
where
    V: Eq + Hash,
{
    pub fn new() -> Self {
        Self {
            inverse_map: HashMap::new(),
        }
    }

    pub fn with_capacity(capacity: usize) -> Self {
        Self {
            inverse_map: HashMap::with_capacity(capacity),
        }
    }

    /* Gets the entity index that points to the given value.

    Returns `None` if no entity currently points to this value.
    */
    #[inline]
    pub fn get(&self, value: &V) -> Option<usize> {
        self.inverse_map.get(value).copied()
    }

    // Registers that an entity index now points to a value.
    //
    #[inline]
    pub fn insert(&mut self, value: V, entity_idx: usize) -> Option<usize> {
        self.inverse_map.insert(value, entity_idx)
    }

    // Removes the mapping for a value.
    //
    #[inline]
    pub fn remove(&mut self, value: &V) -> Option<usize> {
        self.inverse_map.remove(value)
    }

    /* Updates the mapping: removes old value, inserts new.

    Use this when an entity changes which value it points to.
    */
    #[inline]
    pub fn update(&mut self, old_value: Option<&V>, new_value: V, entity_idx: usize) {
        if let Some(old) = old_value {
            self.remove(old);
        }
        self.insert(new_value, entity_idx);
    }

    // Clears all mappings.
    #[inline]
    pub fn clear(&mut self) {
        self.inverse_map.clear();
    }

    #[inline]
    pub fn len(&self) -> usize {
        self.inverse_map.len()
    }

    // Returns true if no mappings exist.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.inverse_map.is_empty()
    }

    // Returns an iterator over all (value, entity_index) pairs.
    #[inline]
    pub fn iter(&self) -> impl Iterator<Item = (&V, &usize)> {
        self.inverse_map.iter()
    }
}