solverforge_core/domain/
variable.rs

1//! Variable type definitions
2
3use std::any::TypeId;
4
5/// The type of a planning variable.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub enum VariableType {
8    /// A genuine planning variable that the solver optimizes.
9    Genuine,
10    /// A chained planning variable where entities form chains rooted at anchors.
11    ///
12    /// Chained variables are used for problems like vehicle routing where:
13    /// - Each entity points to either an anchor (problem fact) or another entity
14    /// - Entities form chains: Anchor ← Entity1 ← Entity2 ← Entity3
15    /// - No cycles or branching allowed
16    Chained,
17    /// A list variable containing multiple values.
18    List,
19    /// A shadow variable computed from other variables.
20    Shadow(ShadowVariableKind),
21}
22
23/// The kind of shadow variable.
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25pub enum ShadowVariableKind {
26    /// Custom shadow variable with user-defined listener.
27    Custom,
28    /// Inverse of another variable (bidirectional relationship).
29    InverseRelation,
30    /// Index within a list variable.
31    Index,
32    /// Next element in a list variable.
33    NextElement,
34    /// Previous element in a list variable.
35    PreviousElement,
36    /// Anchor in a chained variable.
37    Anchor,
38    /// Cascading update from other shadow variables.
39    Cascading,
40    /// Piggyback on another shadow variable's listener.
41    Piggyback,
42}
43
44/// The type of value range for a planning variable.
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub enum ValueRangeType {
47    /// A collection of discrete values.
48    Collection,
49    /// A countable range (e.g., integers from 1 to 100).
50    CountableRange {
51        /// Inclusive start of the range.
52        from: i64,
53        /// Exclusive end of the range.
54        to: i64,
55    },
56    /// An entity-dependent value range.
57    EntityDependent,
58}
59
60impl VariableType {
61    /// Returns true if this is a genuine (non-shadow) variable.
62    ///
63    /// Genuine variables include basic, chained, and list variables.
64    pub fn is_genuine(&self) -> bool {
65        matches!(
66            self,
67            VariableType::Genuine | VariableType::Chained | VariableType::List
68        )
69    }
70
71    /// Returns true if this is a shadow variable.
72    pub fn is_shadow(&self) -> bool {
73        matches!(self, VariableType::Shadow(_))
74    }
75
76    /// Returns true if this is a list variable.
77    pub fn is_list(&self) -> bool {
78        matches!(self, VariableType::List)
79    }
80
81    /// Returns true if this is a chained variable.
82    ///
83    /// Chained variables form chains rooted at anchor problem facts.
84    pub fn is_chained(&self) -> bool {
85        matches!(self, VariableType::Chained)
86    }
87
88    /// Returns true if this is a basic genuine variable (not chained or list).
89    pub fn is_basic(&self) -> bool {
90        matches!(self, VariableType::Genuine)
91    }
92}
93
94impl ShadowVariableKind {
95    /// Returns true if this shadow variable requires a custom listener.
96    pub fn requires_listener(&self) -> bool {
97        matches!(
98            self,
99            ShadowVariableKind::Custom | ShadowVariableKind::Cascading
100        )
101    }
102
103    /// Returns true if this shadow variable is automatically maintained.
104    pub fn is_automatic(&self) -> bool {
105        matches!(
106            self,
107            ShadowVariableKind::InverseRelation
108                | ShadowVariableKind::Index
109                | ShadowVariableKind::NextElement
110                | ShadowVariableKind::PreviousElement
111                | ShadowVariableKind::Anchor
112        )
113    }
114}
115
116/// Information about a chained variable's configuration.
117///
118/// Chained variables require knowledge of the anchor type to distinguish
119/// between anchor values (chain roots) and entity values (chain members).
120#[derive(Debug, Clone, PartialEq, Eq)]
121pub struct ChainedVariableInfo {
122    /// The TypeId of the anchor type (problem fact at chain root).
123    pub anchor_type_id: TypeId,
124    /// The TypeId of the entity type (chain members).
125    pub entity_type_id: TypeId,
126    /// Whether this variable has an associated anchor shadow variable.
127    pub has_anchor_shadow: bool,
128}
129
130impl ChainedVariableInfo {
131    /// Creates new chained variable info.
132    pub fn new<Anchor: 'static, Entity: 'static>() -> Self {
133        Self {
134            anchor_type_id: TypeId::of::<Anchor>(),
135            entity_type_id: TypeId::of::<Entity>(),
136            has_anchor_shadow: false,
137        }
138    }
139
140    /// Creates new chained variable info with anchor shadow variable.
141    pub fn with_anchor_shadow<Anchor: 'static, Entity: 'static>() -> Self {
142        Self {
143            anchor_type_id: TypeId::of::<Anchor>(),
144            entity_type_id: TypeId::of::<Entity>(),
145            has_anchor_shadow: true,
146        }
147    }
148
149    /// Returns true if the given TypeId is the anchor type.
150    pub fn is_anchor_type(&self, type_id: TypeId) -> bool {
151        self.anchor_type_id == type_id
152    }
153
154    /// Returns true if the given TypeId is the entity type.
155    pub fn is_entity_type(&self, type_id: TypeId) -> bool {
156        self.entity_type_id == type_id
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    #[test]
165    fn test_variable_type_is_genuine() {
166        assert!(VariableType::Genuine.is_genuine());
167        assert!(VariableType::Chained.is_genuine());
168        assert!(VariableType::List.is_genuine());
169        assert!(!VariableType::Shadow(ShadowVariableKind::Custom).is_genuine());
170    }
171
172    #[test]
173    fn test_variable_type_is_shadow() {
174        assert!(!VariableType::Genuine.is_shadow());
175        assert!(!VariableType::Chained.is_shadow());
176        assert!(!VariableType::List.is_shadow());
177        assert!(VariableType::Shadow(ShadowVariableKind::Custom).is_shadow());
178        assert!(VariableType::Shadow(ShadowVariableKind::InverseRelation).is_shadow());
179    }
180
181    #[test]
182    fn test_variable_type_is_chained() {
183        assert!(!VariableType::Genuine.is_chained());
184        assert!(VariableType::Chained.is_chained());
185        assert!(!VariableType::List.is_chained());
186        assert!(!VariableType::Shadow(ShadowVariableKind::Anchor).is_chained());
187    }
188
189    #[test]
190    fn test_variable_type_is_list() {
191        assert!(!VariableType::Genuine.is_list());
192        assert!(!VariableType::Chained.is_list());
193        assert!(VariableType::List.is_list());
194        assert!(!VariableType::Shadow(ShadowVariableKind::Index).is_list());
195    }
196
197    #[test]
198    fn test_variable_type_is_basic() {
199        assert!(VariableType::Genuine.is_basic());
200        assert!(!VariableType::Chained.is_basic());
201        assert!(!VariableType::List.is_basic());
202        assert!(!VariableType::Shadow(ShadowVariableKind::Custom).is_basic());
203    }
204
205    #[test]
206    fn test_shadow_variable_kind_requires_listener() {
207        assert!(ShadowVariableKind::Custom.requires_listener());
208        assert!(ShadowVariableKind::Cascading.requires_listener());
209        assert!(!ShadowVariableKind::InverseRelation.requires_listener());
210        assert!(!ShadowVariableKind::Index.requires_listener());
211        assert!(!ShadowVariableKind::Anchor.requires_listener());
212    }
213
214    #[test]
215    fn test_shadow_variable_kind_is_automatic() {
216        assert!(!ShadowVariableKind::Custom.is_automatic());
217        assert!(!ShadowVariableKind::Cascading.is_automatic());
218        assert!(ShadowVariableKind::InverseRelation.is_automatic());
219        assert!(ShadowVariableKind::Index.is_automatic());
220        assert!(ShadowVariableKind::NextElement.is_automatic());
221        assert!(ShadowVariableKind::PreviousElement.is_automatic());
222        assert!(ShadowVariableKind::Anchor.is_automatic());
223    }
224
225    struct TestAnchor;
226    struct TestEntity;
227
228    #[test]
229    fn test_chained_variable_info_new() {
230        let info = ChainedVariableInfo::new::<TestAnchor, TestEntity>();
231
232        assert_eq!(info.anchor_type_id, TypeId::of::<TestAnchor>());
233        assert_eq!(info.entity_type_id, TypeId::of::<TestEntity>());
234        assert!(!info.has_anchor_shadow);
235    }
236
237    #[test]
238    fn test_chained_variable_info_with_anchor_shadow() {
239        let info = ChainedVariableInfo::with_anchor_shadow::<TestAnchor, TestEntity>();
240
241        assert_eq!(info.anchor_type_id, TypeId::of::<TestAnchor>());
242        assert_eq!(info.entity_type_id, TypeId::of::<TestEntity>());
243        assert!(info.has_anchor_shadow);
244    }
245
246    #[test]
247    fn test_chained_variable_info_type_checks() {
248        let info = ChainedVariableInfo::new::<TestAnchor, TestEntity>();
249
250        assert!(info.is_anchor_type(TypeId::of::<TestAnchor>()));
251        assert!(!info.is_anchor_type(TypeId::of::<TestEntity>()));
252
253        assert!(info.is_entity_type(TypeId::of::<TestEntity>()));
254        assert!(!info.is_entity_type(TypeId::of::<TestAnchor>()));
255    }
256}