mago_algebra/
assertion_set.rs1use std::hash::BuildHasher;
2
3use foldhash::HashSet;
4use foldhash::fast::FixedState;
5
6use mago_codex::assertion::Assertion;
7
8pub type Disjunction<T> = Vec<T>;
12
13pub type Conjunction<T> = Vec<T>;
17
18pub type AssertionSet = Conjunction<Disjunction<Assertion>>;
28
29#[inline]
37pub fn add_or_assertion(possibilities: &mut AssertionSet, assertion: Assertion) {
38 if possibilities.is_empty() {
39 possibilities.push(vec![assertion]);
42 return;
43 }
44
45 for clause in possibilities {
46 clause.push(assertion.clone());
47 }
48}
49
50#[inline]
56pub fn add_and_assertion(possibilities: &mut AssertionSet, assertion: Assertion) {
57 possibilities.push(vec![assertion]);
59}
60
61#[inline]
67pub fn add_and_clause(assertion_set: &mut AssertionSet, or_assertions: &[Assertion]) {
68 if or_assertions.is_empty() {
69 *assertion_set = vec![vec![]];
72 return;
73 }
74
75 assertion_set.push(or_assertions.to_vec());
76}
77
78#[inline]
84#[must_use]
85pub fn negate_assertion_set(assertion_set: AssertionSet) -> AssertionSet {
86 let dnf: AssertionSet = assertion_set
89 .into_iter()
90 .filter(|or_clause| or_clause.iter().all(Assertion::is_negatable))
91 .map(|or_clause| or_clause.into_iter().map(|a| a.get_negation()).collect::<Vec<_>>())
92 .filter(|and_clause| !and_clause.is_empty())
93 .collect();
94
95 if dnf.is_empty() {
96 return vec![vec![]];
99 }
100
101 let mut result_cnf: AssertionSet = dnf[0].iter().map(|literal| vec![literal.clone()]).collect();
105
106 for and_clause in dnf.iter().skip(1) {
108 let mut next_result_cnf = AssertionSet::new();
109 for literal in and_clause {
112 for cnf_clause in &result_cnf {
113 let mut new_clause = cnf_clause.clone();
114 new_clause.push(literal.clone());
115 next_result_cnf.push(new_clause);
116 }
117 }
118
119 result_cnf = next_result_cnf;
120 }
121
122 result_cnf
123}
124
125#[inline]
130pub fn and_assertion_sets(set_a: AssertionSet, set_b: AssertionSet) -> AssertionSet {
131 if (set_a.len() == 1 && set_a[0].is_empty()) || (set_b.len() == 1 && set_b[0].is_empty()) {
132 return vec![vec![]];
134 }
135
136 let mut result: AssertionSet = set_a;
137
138 let mut existing_clause_hashes: HashSet<u64> = result.iter().map(hash_disjunction).collect();
140
141 for disjunction in set_b {
143 let disjunction_hash = hash_disjunction(&disjunction);
144 if existing_clause_hashes.insert(disjunction_hash) {
145 result.push(disjunction);
146 }
147 }
148
149 result
150}
151
152fn hash_disjunction(disjunction: &Disjunction<Assertion>) -> u64 {
154 let mut assertion_hashes: Vec<_> = disjunction.iter().map(mago_codex::assertion::Assertion::to_hash).collect();
155 assertion_hashes.sort_unstable();
156
157 FixedState::default().hash_one(&assertion_hashes)
158}
159
160#[cfg(test)]
161mod tests {
162 use mago_codex::ttype::atomic::TAtomic;
163 use mago_codex::ttype::atomic::scalar::TScalar;
164
165 use super::*;
166
167 fn assert_sets_equal(a: AssertionSet, b: AssertionSet) {
168 let mut sorted_a: Vec<_> = a
169 .into_iter()
170 .map(|mut v| {
171 v.sort();
172 v
173 })
174 .collect();
175 sorted_a.sort();
176 let mut sorted_b: Vec<_> = b
177 .into_iter()
178 .map(|mut v| {
179 v.sort();
180 v
181 })
182 .collect();
183 sorted_b.sort();
184 assert_eq!(sorted_a, sorted_b);
185 }
186
187 #[test]
188 fn test_add_or_assertion() {
189 let mut set = vec![vec![Assertion::Truthy], vec![Assertion::Falsy]];
191 add_or_assertion(&mut set, Assertion::IsType(TAtomic::Scalar(TScalar::string())));
193 let expected = vec![
194 vec![Assertion::Truthy, Assertion::IsType(TAtomic::Scalar(TScalar::string()))],
195 vec![Assertion::Falsy, Assertion::IsType(TAtomic::Scalar(TScalar::string()))],
196 ];
197 assert_sets_equal(expected, set);
198 }
199
200 #[test]
201 fn test_add_or_assertion_to_empty() {
202 let mut set = vec![];
203 add_or_assertion(&mut set, Assertion::IsType(TAtomic::Scalar(TScalar::string())));
204 let expected = vec![vec![Assertion::IsType(TAtomic::Scalar(TScalar::string()))]];
205 assert_sets_equal(expected, set);
206 }
207
208 #[test]
209 fn test_add_and_assertion() {
210 let mut set = vec![vec![Assertion::Truthy, Assertion::Falsy]];
211 add_and_assertion(&mut set, Assertion::IsType(TAtomic::Scalar(TScalar::string())));
212 let expected = vec![
213 vec![Assertion::Truthy, Assertion::Falsy],
214 vec![Assertion::IsType(TAtomic::Scalar(TScalar::string()))],
215 ];
216 assert_sets_equal(expected, set);
217 }
218
219 #[test]
220 fn test_add_and_clause() {
221 let mut set = vec![vec![Assertion::Truthy]];
222 let or_clause = vec![
223 Assertion::IsType(TAtomic::Scalar(TScalar::string())),
224 Assertion::IsType(TAtomic::Scalar(TScalar::int())),
225 ];
226 add_and_clause(&mut set, &or_clause);
227 let expected = vec![
228 vec![Assertion::Truthy],
229 vec![
230 Assertion::IsType(TAtomic::Scalar(TScalar::string())),
231 Assertion::IsType(TAtomic::Scalar(TScalar::int())),
232 ],
233 ];
234 assert_sets_equal(expected, set);
235 }
236
237 #[test]
238 fn test_add_and_empty_clause() {
239 let mut set = vec![vec![Assertion::Truthy]];
240 add_and_clause(&mut set, &[]);
241 let expected = vec![vec![]];
242 assert_sets_equal(expected, set);
243 }
244
245 #[test]
246 fn test_negate_simple_or_clause() {
247 let initial_cnf = vec![vec![
248 Assertion::IsType(TAtomic::Scalar(TScalar::string())),
249 Assertion::IsType(TAtomic::Scalar(TScalar::int())),
250 ]];
251 let expected_cnf = vec![
252 vec![Assertion::IsNotType(TAtomic::Scalar(TScalar::string()))],
253 vec![Assertion::IsNotType(TAtomic::Scalar(TScalar::int()))],
254 ];
255 assert_sets_equal(expected_cnf, negate_assertion_set(initial_cnf));
256 }
257
258 #[test]
259 fn test_negate_simple_and_clause() {
260 let initial_cnf = vec![vec![Assertion::Truthy], vec![Assertion::Falsy]];
261 let expected_cnf = vec![vec![Assertion::Falsy, Assertion::Truthy]];
262 assert_sets_equal(expected_cnf, negate_assertion_set(initial_cnf));
263 }
264}