Skip to main content

pumpkin_core/engine/predicates/
predicate_constructor.rs

1use super::predicate::Predicate;
2
3/// A trait which defines methods for creating a [`Predicate`].
4pub trait PredicateConstructor {
5    /// The value used to represent a bound.
6    type Value;
7
8    /// Creates a lower-bound predicate (e.g. `[x >= v]`).
9    fn lower_bound_predicate(&self, bound: Self::Value) -> Predicate;
10
11    /// Creates an upper-bound predicate (e.g. `[x <= v]`).
12    fn upper_bound_predicate(&self, bound: Self::Value) -> Predicate;
13
14    /// Creates an equality predicate (e.g. `[x == v]`).
15    fn equality_predicate(&self, bound: Self::Value) -> Predicate;
16
17    /// Creates a disequality predicate (e.g. `[x != v]`).
18    fn disequality_predicate(&self, bound: Self::Value) -> Predicate;
19}
20
21/// A macro which allows for the creation of a [`Predicate`].
22///
23/// # Example
24/// ```rust
25/// # use pumpkin_core::Solver;
26/// # use pumpkin_core::predicate;
27/// # use pumpkin_core::predicates::Predicate;
28/// let mut solver = Solver::default();
29/// let x = solver.new_bounded_integer(0, 10);
30///
31/// let lower_bound_predicate = predicate!(x >= 5);
32/// assert_eq!(lower_bound_predicate.get_domain(), x);
33/// assert_eq!(lower_bound_predicate.get_right_hand_side(), 5);
34///
35/// let upper_bound_predicate = predicate!(x <= 5);
36/// assert_eq!(upper_bound_predicate.get_domain(), x);
37/// assert_eq!(upper_bound_predicate.get_right_hand_side(), 5);
38///
39/// let equality_predicate = predicate!(x == 5);
40/// assert_eq!(equality_predicate.get_domain(), x);
41/// assert_eq!(equality_predicate.get_right_hand_side(), 5);
42///
43/// let disequality_predicate = predicate!(x != 5);
44/// assert_eq!(disequality_predicate.get_domain(), x);
45/// assert_eq!(disequality_predicate.get_right_hand_side(), 5);
46/// ```
47#[macro_export]
48macro_rules! predicate {
49    ($($var:ident).+$([$index:expr])? >= $bound:expr) => {{
50        #[allow(unused, reason = "could be imported at call-site")]
51        use $crate::predicates::PredicateConstructor;
52        $($var).+$([$index])?.lower_bound_predicate($bound)
53    }};
54    ($($var:ident).+$([$index:expr])? <= $bound:expr) => {{
55        #[allow(unused, reason = "could be imported at call-site")]
56        use $crate::predicates::PredicateConstructor;
57        $($var).+$([$index])?.upper_bound_predicate($bound)
58    }};
59    ($($var:ident).+$([$index:expr])? == $value:expr) => {{
60        #[allow(unused, reason = "could be imported at call-site")]
61        use $crate::predicates::PredicateConstructor;
62        $($var).+$([$index])?.equality_predicate($value)
63    }};
64    ($($var:ident).+$([$index:expr])? != $value:expr) => {{
65        #[allow(unused, reason = "could be imported at call-site")]
66        use $crate::predicates::PredicateConstructor;
67        $($var).+$([$index])?.disequality_predicate($value)
68    }};
69}
70
71#[cfg(test)]
72mod tests {
73    use crate::variables::DomainId;
74
75    #[test]
76    fn macro_local_identifiers_are_matched() {
77        let x = DomainId::new(0);
78
79        assert_eq!(x, predicate![x >= 2].get_domain());
80        assert_eq!(x, predicate![x <= 3].get_domain());
81        assert_eq!(x, predicate![x == 5].get_domain());
82        assert_eq!(x, predicate![x != 5].get_domain());
83
84        assert_eq!(2, predicate![x >= 2].get_right_hand_side());
85        assert_eq!(3, predicate![x <= 3].get_right_hand_side());
86        assert_eq!(5, predicate![x == 5].get_right_hand_side());
87        assert_eq!(5, predicate![x != 5].get_right_hand_side());
88
89        assert!(predicate!(x >= 2).is_lower_bound_predicate());
90        assert!(!predicate!(x >= 2).is_upper_bound_predicate());
91        assert!(!predicate!(x >= 2).is_equality_predicate());
92        assert!(!predicate!(x >= 2).is_not_equal_predicate());
93
94        assert!(predicate!(x <= 3).is_upper_bound_predicate());
95        assert!(!predicate!(x <= 3).is_lower_bound_predicate());
96        assert!(!predicate!(x <= 3).is_equality_predicate());
97        assert!(!predicate!(x <= 3).is_not_equal_predicate());
98
99        assert!(predicate!(x == 5).is_equality_predicate());
100        assert!(!predicate!(x == 5).is_lower_bound_predicate());
101        assert!(!predicate!(x == 5).is_upper_bound_predicate());
102        assert!(!predicate!(x == 5).is_not_equal_predicate());
103
104        assert!(predicate!(x != 5).is_not_equal_predicate());
105        assert!(!predicate!(x != 5).is_lower_bound_predicate());
106        assert!(!predicate!(x != 5).is_upper_bound_predicate());
107        assert!(!predicate!(x != 5).is_equality_predicate());
108    }
109
110    #[test]
111    fn macro_nested_identifiers_are_matched() {
112        struct Wrapper {
113            x: DomainId,
114        }
115
116        let wrapper = Wrapper {
117            x: DomainId::new(0),
118        };
119
120        assert_eq!(wrapper.x, predicate![wrapper.x >= 2].get_domain());
121        assert_eq!(wrapper.x, predicate![wrapper.x <= 3].get_domain());
122        assert_eq!(wrapper.x, predicate![wrapper.x == 5].get_domain());
123        assert_eq!(wrapper.x, predicate![wrapper.x != 5].get_domain());
124
125        assert_eq!(2, predicate![wrapper.x >= 2].get_right_hand_side());
126        assert_eq!(3, predicate![wrapper.x <= 3].get_right_hand_side());
127        assert_eq!(5, predicate![wrapper.x == 5].get_right_hand_side());
128        assert_eq!(5, predicate![wrapper.x != 5].get_right_hand_side());
129    }
130
131    #[test]
132    fn macro_index_expressions_are_matched() {
133        let wrapper = [DomainId::new(0)];
134
135        assert_eq!(wrapper[0], predicate![wrapper[0] >= 2].get_domain());
136        assert_eq!(wrapper[0], predicate![wrapper[0] <= 3].get_domain());
137        assert_eq!(wrapper[0], predicate![wrapper[0] == 5].get_domain());
138        assert_eq!(wrapper[0], predicate![wrapper[0] != 5].get_domain());
139
140        assert_eq!(2, predicate![wrapper[0] >= 2].get_right_hand_side());
141        assert_eq!(3, predicate![wrapper[0] <= 3].get_right_hand_side());
142        assert_eq!(5, predicate![wrapper[0] == 5].get_right_hand_side());
143        assert_eq!(5, predicate![wrapper[0] != 5].get_right_hand_side());
144    }
145}