Skip to main content

canrun_collections/lmap/
compare.rs

1use super::{unify_entries, LMap};
2use canrun::{custom, project_2, DomainType, Goal, IntoVal, UnifyIn};
3use std::fmt::Debug;
4use std::hash::Hash;
5
6/// Assert that [`LMap`] `a` is a subset of [`LMap`] `b`.
7///
8/// This means that all of the keys in `a` unify with keys in `b` AND the
9/// corresponding values also unify. This is the opposite of [`is_superset`].
10///
11/// # Example:
12/// ```
13/// use canrun::{var, Goal};
14/// use canrun_collections::lmap::{lmap, is_subset};
15/// use canrun_collections::example::Collections;
16///
17/// let x = var();
18/// let goal: Goal<Collections> = is_subset(lmap! {x => 2}, lmap! {1 => 2, 3 => 4});
19/// let results: Vec<_> = goal.query(x).collect();
20/// assert_eq!(results, vec![1]);
21/// ```
22pub fn is_subset<'a, K, V, A, B, D>(a: A, b: B) -> Goal<'a, D>
23where
24    K: Debug + Eq + Hash + UnifyIn<'a, D> + 'a,
25    V: Debug + UnifyIn<'a, D> + 'a,
26    A: IntoVal<LMap<K, V>>,
27    B: IntoVal<LMap<K, V>>,
28    D: DomainType<'a, LMap<K, V>> + DomainType<'a, K> + DomainType<'a, V> + 'a,
29{
30    project_2(a, b, |a, b| {
31        custom(move |state| unify_entries(state, a.clone(), b.clone()))
32    })
33}
34
35/// Assert that [`LMap`] `a` is a superset of [`LMap`] `b`.
36///
37/// This means that all of the keys in `b` unify with keys in `a` AND the
38/// corresponding values also unify. This is the opposite of [`is_subset`].
39///
40/// # Example:
41/// ```
42/// use canrun::{var, Goal};
43/// use canrun_collections::lmap::{lmap, is_superset};
44/// use canrun_collections::example::Collections;
45///
46/// let x = var();
47/// let goal: Goal<Collections> = is_superset(lmap! {x => 2, 3 => 4}, lmap! {1 => 2});
48/// let results: Vec<_> = goal.query(x).collect();
49/// assert_eq!(results, vec![1]);
50/// ```
51pub fn is_superset<'a, K, V, A, B, D>(a: A, b: B) -> Goal<'a, D>
52where
53    K: Debug + Eq + Hash + UnifyIn<'a, D> + 'a,
54    V: Debug + UnifyIn<'a, D> + 'a,
55    A: IntoVal<LMap<K, V>>,
56    B: IntoVal<LMap<K, V>>,
57    D: DomainType<'a, LMap<K, V>> + DomainType<'a, K> + DomainType<'a, V> + 'a,
58{
59    is_subset(b, a)
60}
61
62#[cfg(test)]
63mod tests {
64    use super::{is_subset, is_superset};
65    use crate::example::Collections;
66    use crate::lmap;
67    use canrun::{var, Goal, IterResolved};
68
69    #[test]
70    fn is_subset_should_succeed_on() {
71        let x = var();
72        let cases = vec![
73            (lmap! {1 => 2}, lmap! {1 => 2}),
74            (lmap! {1 => 2}, lmap! {1 => 2, 3 => 4}),
75            (lmap! {x => 2}, lmap! {1 => 2, 3 => 4}),
76            (lmap! {x => 2}, lmap! {x => 2, 3 => 4}),
77        ];
78        for (a, b) in cases {
79            let goal: Goal<Collections> = is_subset(&a, &b);
80            if goal.iter_resolved().count() != 1 {
81                panic!("is_subset failed on {:?} {:?}", a, b);
82            }
83        }
84    }
85
86    #[test]
87    fn is_subset_should_fail_on() {
88        let x = var();
89        let cases = vec![
90            (lmap! {1 => 2, 3 => 4}, lmap! {1 => 2}),
91            (lmap! {x => 2}, lmap! {1 => 1}),
92            (lmap! {x => 2}, lmap! {1 => 2, x => 4}),
93        ];
94        for (a, b) in cases {
95            let goal: Goal<Collections> = is_subset(&a, &b);
96            if goal.iter_resolved().count() != 0 {
97                panic!("is_subset erroneously succeeded on {:?} {:?}", a, b);
98            }
99        }
100    }
101
102    #[test]
103    fn is_superset_should_succeed_on() {
104        let x = var();
105        let cases = vec![
106            (lmap! {1 => 2}, lmap! {1 => 2}),
107            (lmap! {1 => 2, 3 => 4}, lmap! {1 => 2}),
108            (lmap! {x => 2, 3 => 4}, lmap! {x => 2}),
109        ];
110        for (a, b) in cases {
111            let goal: Goal<Collections> = is_superset(&a, &b);
112            if goal.iter_resolved().count() != 1 {
113                panic!("is_superset failed on {:?} {:?}", a, b);
114            }
115        }
116    }
117
118    #[test]
119    fn is_superset_should_fail_on() {
120        let x = var();
121        let cases = vec![
122            (lmap! {1 => 2}, lmap! {1 => 2, 3 => 4}),
123            (lmap! {x => 2}, lmap! {1 => 1}),
124            (lmap! {1 => 2, x => 4}, lmap! {x => 2}),
125        ];
126        for (a, b) in cases {
127            let goal: Goal<Collections> = is_superset(&a, &b);
128            if goal.iter_resolved().count() != 0 {
129                panic!("is_superset erroneously succeeded on {:?} {:?}", a, b);
130            }
131        }
132    }
133}