1#![allow(dead_code)]
2use crate::ast::{
3 AbstractLiteral, Atom, DeclarationKind, Expression as Expr, Field, Literal as Lit, Metadata,
4 comprehension::{Comprehension, ComprehensionQualifier},
5 matrix,
6};
7use crate::into_matrix;
8use itertools::{Itertools as _, izip};
9use std::cmp::Ordering as CmpOrdering;
10use std::collections::HashSet;
11
12pub fn eval_constant(expr: &Expr) -> Option<Lit> {
17 match expr {
18 Expr::Supset(_, a, b) => match (a.as_ref(), b.as_ref()) {
19 (
20 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Set(a)))),
21 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Set(b)))),
22 ) => {
23 let a_set: HashSet<Lit> = a.iter().cloned().collect();
24 let b_set: HashSet<Lit> = b.iter().cloned().collect();
25
26 if a_set.difference(&b_set).count() > 0 {
27 Some(Lit::Bool(a_set.is_superset(&b_set)))
28 } else {
29 Some(Lit::Bool(false))
30 }
31 }
32 _ => None,
33 },
34 Expr::SupsetEq(_, a, b) => match (a.as_ref(), b.as_ref()) {
35 (
36 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Set(a)))),
37 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Set(b)))),
38 ) => Some(Lit::Bool(
39 a.iter()
40 .cloned()
41 .collect::<HashSet<Lit>>()
42 .is_superset(&b.iter().cloned().collect::<HashSet<Lit>>()),
43 )),
44 _ => None,
45 },
46 Expr::Subset(_, a, b) => match (a.as_ref(), b.as_ref()) {
47 (
48 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Set(a)))),
49 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Set(b)))),
50 ) => {
51 let a_set: HashSet<Lit> = a.iter().cloned().collect();
52 let b_set: HashSet<Lit> = b.iter().cloned().collect();
53
54 if b_set.difference(&a_set).count() > 0 {
55 Some(Lit::Bool(a_set.is_subset(&b_set)))
56 } else {
57 Some(Lit::Bool(false))
58 }
59 }
60 _ => None,
61 },
62 Expr::SubsetEq(_, a, b) => match (a.as_ref(), b.as_ref()) {
63 (
64 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Set(a)))),
65 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Set(b)))),
66 ) => Some(Lit::Bool(
67 a.iter()
68 .cloned()
69 .collect::<HashSet<Lit>>()
70 .is_subset(&b.iter().cloned().collect::<HashSet<Lit>>()),
71 )),
72 _ => None,
73 },
74 Expr::Intersect(_, a, b) => match (a.as_ref(), b.as_ref()) {
75 (
76 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Set(a)))),
77 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Set(b)))),
78 ) => {
79 let mut res: Vec<Lit> = Vec::new();
80 for lit in a.iter() {
81 if b.contains(lit) && !res.contains(lit) {
82 res.push(lit.clone());
83 }
84 }
85 Some(Lit::AbstractLiteral(AbstractLiteral::Set(res)))
86 }
87 _ => None,
88 },
89 Expr::Union(_, a, b) => match (a.as_ref(), b.as_ref()) {
90 (
91 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Set(a)))),
92 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Set(b)))),
93 ) => {
94 let mut res: Vec<Lit> = Vec::new();
95 for lit in a.iter() {
96 res.push(lit.clone());
97 }
98 for lit in b.iter() {
99 if !res.contains(lit) {
100 res.push(lit.clone());
101 }
102 }
103 Some(Lit::AbstractLiteral(AbstractLiteral::Set(res)))
104 }
105 _ => None,
106 },
107 Expr::In(_, a, b) => {
108 if let (
109 Expr::Atomic(_, Atom::Literal(Lit::Int(c))),
110 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Set(d)))),
111 ) = (a.as_ref(), b.as_ref())
112 {
113 for lit in d.iter() {
114 if let Lit::Int(x) = lit
115 && c == x
116 {
117 return Some(Lit::Bool(true));
118 }
119 }
120 Some(Lit::Bool(false))
121 } else {
122 None
123 }
124 }
125 Expr::FromSolution(_, _) => None,
126 Expr::DominanceRelation(_, _) => None,
127 Expr::InDomain(_, e, domain) => {
128 let Expr::Atomic(_, Atom::Literal(lit)) = e.as_ref() else {
129 return None;
130 };
131
132 domain.contains(lit).ok().map(Into::into)
133 }
134 Expr::Atomic(_, Atom::Literal(c)) => Some(c.clone()),
135 Expr::Atomic(_, Atom::Reference(reference)) => reference.resolve_constant(),
136 Expr::AbstractLiteral(_, a) => Some(Lit::AbstractLiteral(a.clone().into_literals()?)),
137 Expr::Comprehension(_, comprehension) => {
138 eval_constant_comprehension(comprehension.as_ref())
139 }
140 Expr::AbstractComprehension(_, _) => None,
141 Expr::RecordField(_, rec, fld_name) => {
142 if let Expr::Atomic(
143 _,
144 Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Record(ents))),
145 ) = rec.as_ref()
146 {
147 for Field { name, value } in ents {
148 if name.eq(fld_name) {
149 return Some(value.clone());
150 }
151 }
152 }
153 None
154 }
155 Expr::UnsafeIndex(_, subject, indices) | Expr::SafeIndex(_, subject, indices) => {
156 let subject: Lit = eval_constant(subject.as_ref())?;
157 let indices: Vec<Lit> = indices
158 .iter()
159 .map(eval_constant)
160 .collect::<Option<Vec<Lit>>>()?;
161
162 match subject {
163 Lit::AbstractLiteral(subject @ AbstractLiteral::Matrix(_, _)) => {
164 matrix::flatten_enumerate(subject)
165 .find(|(i, _)| i == &indices)
166 .map(|(_, x)| x)
167 }
168 Lit::AbstractLiteral(subject @ AbstractLiteral::Tuple(_)) => {
169 let AbstractLiteral::Tuple(elems) = subject else {
170 return None;
171 };
172
173 assert!(indices.len() == 1, "nested tuples not supported yet");
174
175 let Lit::Int(index) = indices[0].clone() else {
176 return None;
177 };
178
179 if elems.len() < index as usize || index < 1 {
180 return None;
181 }
182
183 let item = elems[index as usize - 1].clone();
185
186 Some(item)
187 }
188 Lit::AbstractLiteral(subject @ AbstractLiteral::Record(_)) => {
189 let AbstractLiteral::Record(elems) = subject else {
190 return None;
191 };
192
193 assert!(indices.len() == 1, "nested record not supported yet");
194
195 let Lit::Int(index) = indices[0].clone() else {
196 return None;
197 };
198
199 if elems.len() < index as usize || index < 1 {
200 return None;
201 }
202
203 let item = elems[index as usize - 1].clone();
205 Some(item.value)
206 }
207 _ => None,
208 }
209 }
210 Expr::UnsafeSlice(_, subject, indices) | Expr::SafeSlice(_, subject, indices) => {
211 let subject: Lit = eval_constant(subject.as_ref())?;
212 let Lit::AbstractLiteral(subject @ AbstractLiteral::Matrix(_, _)) = subject else {
213 return None;
214 };
215
216 let hole_dim = indices
217 .iter()
218 .cloned()
219 .position(|x| x.is_none())
220 .expect("slice expression should have a hole dimension");
221
222 let missing_domain = matrix::index_domains(&subject)[hole_dim].clone();
223
224 let indices: Vec<Option<Lit>> = indices
225 .iter()
226 .cloned()
227 .map(|x| {
228 match x {
231 Some(x) => eval_constant(&x).map(Some),
232 None => Some(None),
233 }
234 })
235 .collect::<Option<Vec<Option<Lit>>>>()?;
236
237 let indices_in_slice: Vec<Vec<Lit>> = missing_domain
238 .values()
239 .ok()?
240 .map(|i| {
241 let mut indices = indices.clone();
242 indices[hole_dim] = Some(i);
243 indices.into_iter().map(|x| x.unwrap()).collect_vec()
246 })
247 .collect_vec();
248
249 let elems = matrix::flatten_enumerate(subject)
251 .filter(|(i, _)| indices_in_slice.contains(i))
252 .map(|(_, elem)| elem)
253 .collect();
254
255 Some(Lit::AbstractLiteral(into_matrix![elems]))
256 }
257 Expr::Abs(_, e) => un_op::<i32, i32>(|a| a.abs(), e).map(Lit::Int),
258 Expr::Eq(_, a, b) => bin_op::<i32, bool>(|a, b| a == b, a, b)
259 .or_else(|| bin_op::<bool, bool>(|a, b| a == b, a, b))
260 .map(Lit::Bool),
261 Expr::Neq(_, a, b) => bin_op::<i32, bool>(|a, b| a != b, a, b).map(Lit::Bool),
262 Expr::Lt(_, a, b) => bin_op::<i32, bool>(|a, b| a < b, a, b).map(Lit::Bool),
263 Expr::Gt(_, a, b) => bin_op::<i32, bool>(|a, b| a > b, a, b).map(Lit::Bool),
264 Expr::Leq(_, a, b) => bin_op::<i32, bool>(|a, b| a <= b, a, b).map(Lit::Bool),
265 Expr::Geq(_, a, b) => bin_op::<i32, bool>(|a, b| a >= b, a, b).map(Lit::Bool),
266 Expr::Not(_, expr) => un_op::<bool, bool>(|e| !e, expr).map(Lit::Bool),
267 Expr::And(_, e) => {
268 vec_lit_op::<bool, bool>(|e| e.iter().all(|&e| e), e.as_ref()).map(Lit::Bool)
269 }
270 Expr::Table(_, _, _) => None,
271 Expr::NegativeTable(_, _, _) => None,
272 Expr::Root(_, _) => None,
273 Expr::Or(_, es) => {
274 for e in (**es).clone().unwrap_list()? {
276 if let Expr::Atomic(_, Atom::Literal(Lit::Bool(true))) = e {
277 return Some(Lit::Bool(true));
278 };
279 }
280
281 vec_lit_op::<bool, bool>(|e| e.iter().any(|&e| e), es.as_ref()).map(Lit::Bool)
282 }
283 Expr::Imply(_, box1, box2) => {
284 let a: &Atom = (&**box1).try_into().ok()?;
285 let b: &Atom = (&**box2).try_into().ok()?;
286
287 let a: bool = a.try_into().ok()?;
288 let b: bool = b.try_into().ok()?;
289
290 if a {
291 Some(Lit::Bool(b))
293 } else {
294 Some(Lit::Bool(true))
296 }
297 }
298 Expr::Iff(_, box1, box2) => {
299 let a: &Atom = (&**box1).try_into().ok()?;
300 let b: &Atom = (&**box2).try_into().ok()?;
301
302 let a: bool = a.try_into().ok()?;
303 let b: bool = b.try_into().ok()?;
304
305 Some(Lit::Bool(a == b))
306 }
307 Expr::Sum(_, exprs) => vec_lit_op::<i32, i32>(|e| e.iter().sum(), exprs).map(Lit::Int),
308 Expr::Product(_, exprs) => {
309 vec_lit_op::<i32, i32>(|e| e.iter().product(), exprs).map(Lit::Int)
310 }
311 Expr::FlatIneq(_, a, b, c) => {
312 let a: i32 = a.try_into().ok()?;
313 let b: i32 = b.try_into().ok()?;
314 let c: i32 = c.try_into().ok()?;
315
316 Some(Lit::Bool(a <= b + c))
317 }
318 Expr::FlatSumGeq(_, exprs, a) => {
319 let sum = exprs.iter().try_fold(0, |acc, atom: &Atom| {
320 let n: i32 = atom.try_into().ok()?;
321 let acc = acc + n;
322 Some(acc)
323 })?;
324
325 Some(Lit::Bool(sum >= a.try_into().ok()?))
326 }
327 Expr::FlatSumLeq(_, exprs, a) => {
328 let sum = exprs.iter().try_fold(0, |acc, atom: &Atom| {
329 let n: i32 = atom.try_into().ok()?;
330 let acc = acc + n;
331 Some(acc)
332 })?;
333
334 Some(Lit::Bool(sum >= a.try_into().ok()?))
335 }
336 Expr::Min(_, e) => {
337 opt_vec_lit_op::<i32, i32>(|e| e.iter().min().copied(), e.as_ref()).map(Lit::Int)
338 }
339 Expr::Max(_, e) => {
340 opt_vec_lit_op::<i32, i32>(|e| e.iter().max().copied(), e.as_ref()).map(Lit::Int)
341 }
342 Expr::UnsafeDiv(_, a, b) | Expr::SafeDiv(_, a, b) => {
343 if unwrap_expr::<i32>(b)? == 0 {
344 return None;
345 }
346 bin_op::<i32, i32>(|a, b| ((a as f32) / (b as f32)).floor() as i32, a, b).map(Lit::Int)
347 }
348 Expr::UnsafeMod(_, a, b) | Expr::SafeMod(_, a, b) => {
349 if unwrap_expr::<i32>(b)? == 0 {
350 return None;
351 }
352 bin_op::<i32, i32>(|a, b| a - b * (a as f32 / b as f32).floor() as i32, a, b)
353 .map(Lit::Int)
354 }
355 Expr::Substring(_, s, t) => match (s.as_ref(), t.as_ref()) {
356 (
357 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Sequence(s)))),
358 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Sequence(t)))),
359 ) => {
360 if s.len() > t.len() {
361 return Some(Lit::Bool(false));
362 }
363
364 let found = t.windows(s.len()).any(|window| window == s.as_slice());
365 Some(Lit::Bool(found))
366 }
367 _ => None,
368 },
369 Expr::Subsequence(_, s, t) => match (s.as_ref(), t.as_ref()) {
370 (
371 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Sequence(s)))),
372 Expr::Atomic(_, Atom::Literal(Lit::AbstractLiteral(AbstractLiteral::Sequence(t)))),
373 ) => {
374 let mut i = 0;
375 let mut j = 0;
376
377 while i < s.len() && j < t.len() {
378 if s[i] == t[j] {
379 i += 1;
380 }
381 j += 1;
382 }
383
384 Some(Lit::Bool(i == s.len()))
385 }
386 _ => None,
387 },
388 Expr::MinionDivEqUndefZero(_, a, b, c) => {
389 let a: i32 = a.try_into().ok()?;
391 let b: i32 = b.try_into().ok()?;
392 let c: i32 = c.try_into().ok()?;
393
394 if b == 0 {
395 return None;
396 }
397
398 let a = a as f32;
399 let b = b as f32;
400 let div: i32 = (a / b).floor() as i32;
401 Some(Lit::Bool(div == c))
402 }
403 Expr::Bubble(_, a, b) => bin_op::<bool, bool>(|a, b| a && b, a, b).map(Lit::Bool),
404 Expr::MinionReify(_, a, b) => {
405 let result = eval_constant(a)?;
406
407 let result: bool = result.try_into().ok()?;
408 let b: bool = b.try_into().ok()?;
409
410 Some(Lit::Bool(b == result))
411 }
412 Expr::MinionReifyImply(_, a, b) => {
413 let result = eval_constant(a)?;
414
415 let result: bool = result.try_into().ok()?;
416 let b: bool = b.try_into().ok()?;
417
418 if b {
419 Some(Lit::Bool(result))
420 } else {
421 Some(Lit::Bool(true))
422 }
423 }
424 Expr::MinionModuloEqUndefZero(_, a, b, c) => {
425 let a: i32 = a.try_into().ok()?;
433 let b: i32 = b.try_into().ok()?;
434 let c: i32 = c.try_into().ok()?;
435
436 if b == 0 {
437 return None;
438 }
439
440 let modulo = a - b * (a as f32 / b as f32).floor() as i32;
441 Some(Lit::Bool(modulo == c))
442 }
443 Expr::MinionPow(_, a, b, c) => {
444 let a: i32 = a.try_into().ok()?;
447 let b: i32 = b.try_into().ok()?;
448 let c: i32 = c.try_into().ok()?;
449
450 if a <= 0 {
451 return None;
452 }
453
454 if b <= 0 {
455 return None;
456 }
457
458 if c <= 0 {
459 return None;
460 }
461
462 Some(Lit::Bool(a ^ b == c))
463 }
464 Expr::MinionWInSet(_, _, _) => None,
465 Expr::MinionWInIntervalSet(_, x, intervals) => {
466 let x_lit: &Lit = x.try_into().ok()?;
467
468 let x_lit = match x_lit.clone() {
469 Lit::Int(i) => Some(i),
470 Lit::Bool(true) => Some(1),
471 Lit::Bool(false) => Some(0),
472 _ => None,
473 }?;
474
475 let mut intervals = intervals.iter();
476 while let Some(lower) = intervals.next() {
477 let Some(upper) = intervals.next() else {
478 break;
479 };
480 if &x_lit >= lower && &x_lit <= upper {
481 return Some(Lit::Bool(true));
482 }
483 }
484
485 Some(Lit::Bool(false))
486 }
487 Expr::Flatten(_, _, _) => {
488 None
490 }
491 Expr::AllDiff(_, e) => {
492 let es = (**e).clone().unwrap_list()?;
493 let mut lits: HashSet<Lit> = HashSet::new();
494 for expr in es {
495 let Expr::Atomic(_, Atom::Literal(x)) = expr else {
496 return None;
497 };
498 match x {
499 Lit::Int(_) | Lit::Bool(_) => {
500 if lits.contains(&x) {
501 return Some(Lit::Bool(false));
502 } else {
503 lits.insert(x.clone());
504 }
505 }
506 Lit::AbstractLiteral(_) => return None, }
508 }
509 Some(Lit::Bool(true))
510 }
511 Expr::FlatAllDiff(_, es) => {
512 let mut lits: HashSet<Lit> = HashSet::new();
513 for atom in es {
514 let Atom::Literal(x) = atom else {
515 return None;
516 };
517
518 match x {
519 Lit::Int(_) | Lit::Bool(_) => {
520 if lits.contains(x) {
521 return Some(Lit::Bool(false));
522 } else {
523 lits.insert(x.clone());
524 }
525 }
526 Lit::AbstractLiteral(_) => return None, }
528 }
529 Some(Lit::Bool(true))
530 }
531 Expr::FlatWatchedLiteral(_, _, _) => None,
532 Expr::AuxDeclaration(_, _, _) => None,
533 Expr::Neg(_, a) => match eval_constant(a.as_ref())? {
534 Lit::Int(a) => Some(Lit::Int(-a)),
535 _ => None,
536 },
537 Expr::Factorial(_, _) => None,
538 Expr::Minus(_, a, b) => bin_op::<i32, i32>(|a, b| a - b, a, b).map(Lit::Int),
539 Expr::FlatMinusEq(_, a, b) => {
540 let a: i32 = a.try_into().ok()?;
541 let b: i32 = b.try_into().ok()?;
542 Some(Lit::Bool(a == -b))
543 }
544 Expr::FlatProductEq(_, a, b, c) => {
545 let a: i32 = a.try_into().ok()?;
546 let b: i32 = b.try_into().ok()?;
547 let c: i32 = c.try_into().ok()?;
548 Some(Lit::Bool(a * b == c))
549 }
550 Expr::FlatWeightedSumLeq(_, cs, vs, total) => {
551 let cs: Vec<i32> = cs
552 .iter()
553 .map(|x| TryInto::<i32>::try_into(x).ok())
554 .collect::<Option<Vec<i32>>>()?;
555 let vs: Vec<i32> = vs
556 .iter()
557 .map(|x| TryInto::<i32>::try_into(x).ok())
558 .collect::<Option<Vec<i32>>>()?;
559 let total: i32 = total.try_into().ok()?;
560
561 let sum: i32 = izip!(cs, vs).fold(0, |acc, (c, v)| acc + (c * v));
562
563 Some(Lit::Bool(sum <= total))
564 }
565 Expr::FlatWeightedSumGeq(_, cs, vs, total) => {
566 let cs: Vec<i32> = cs
567 .iter()
568 .map(|x| TryInto::<i32>::try_into(x).ok())
569 .collect::<Option<Vec<i32>>>()?;
570 let vs: Vec<i32> = vs
571 .iter()
572 .map(|x| TryInto::<i32>::try_into(x).ok())
573 .collect::<Option<Vec<i32>>>()?;
574 let total: i32 = total.try_into().ok()?;
575
576 let sum: i32 = izip!(cs, vs).fold(0, |acc, (c, v)| acc + (c * v));
577
578 Some(Lit::Bool(sum >= total))
579 }
580 Expr::FlatAbsEq(_, x, y) => {
581 let x: i32 = x.try_into().ok()?;
582 let y: i32 = y.try_into().ok()?;
583
584 Some(Lit::Bool(x == y.abs()))
585 }
586 Expr::UnsafePow(_, a, b) | Expr::SafePow(_, a, b) => {
587 let a: &Atom = a.try_into().ok()?;
588 let a: i32 = a.try_into().ok()?;
589
590 let b: &Atom = b.try_into().ok()?;
591 let b: i32 = b.try_into().ok()?;
592
593 if (a != 0 || b != 0) && b >= 0 {
594 Some(Lit::Int(a.pow(b as u32)))
595 } else {
596 None
597 }
598 }
599 Expr::Metavar(_, _) => None,
600 Expr::MinionElementOne(_, _, _, _) => None,
601 Expr::ToInt(_, expression) => {
602 let lit = eval_constant(expression.as_ref())?;
603 match lit {
604 Lit::Int(_) => Some(lit),
605 Lit::Bool(true) => Some(Lit::Int(1)),
606 Lit::Bool(false) => Some(Lit::Int(0)),
607 _ => None,
608 }
609 }
610 Expr::SATInt(_, _, _, _) => {
611 None
619 }
620 Expr::PairwiseSum(_, a, b) => {
621 match (eval_constant(a.as_ref())?, eval_constant(b.as_ref())?) {
622 (Lit::Int(a_int), Lit::Int(b_int)) => Some(Lit::Int(a_int + b_int)),
623 _ => None,
624 }
625 }
626 Expr::PairwiseProduct(_, a, b) => {
627 match (eval_constant(a.as_ref())?, eval_constant(b.as_ref())?) {
628 (Lit::Int(a_int), Lit::Int(b_int)) => Some(Lit::Int(a_int * b_int)),
629 _ => None,
630 }
631 }
632 Expr::Defined(_, _) => todo!(),
633 Expr::Range(_, _) => todo!(),
634 Expr::Image(_, _, _) => todo!(),
635 Expr::ImageSet(_, _, _) => todo!(),
636 Expr::PreImage(_, _, _) => todo!(),
637 Expr::Inverse(_, _, _) => todo!(),
638 Expr::Restrict(_, _, _) => todo!(),
639 Expr::Active(_, _, _) => todo!(),
640 Expr::ToSet(_, _) => todo!(),
641 Expr::ToMSet(_, _) => todo!(),
642 Expr::ToRelation(_, _) => todo!(),
643 Expr::RelationProj(_, _, _) => todo!(),
644 Expr::Apart(_, _, _) => todo!(),
645 Expr::Together(_, _, _) => todo!(),
646 Expr::Participants(_, _) => todo!(),
647 Expr::Party(_, _, _) => todo!(),
648 Expr::Parts(_, _) => todo!(),
649 Expr::Card(_, _) => todo!(),
650 Expr::LexLt(_, a, b) => {
651 let lt = vec_expr_pairs_op::<i32, _>(a, b, |pairs, (a_len, b_len)| {
652 pairs
653 .iter()
654 .find_map(|(a, b)| match a.cmp(b) {
655 CmpOrdering::Less => Some(true), CmpOrdering::Greater => Some(false), CmpOrdering::Equal => None, })
659 .unwrap_or(a_len < b_len) })?;
661 Some(lt.into())
662 }
663 Expr::LexLeq(_, a, b) => {
664 let lt = vec_expr_pairs_op::<i32, _>(a, b, |pairs, (a_len, b_len)| {
665 pairs
666 .iter()
667 .find_map(|(a, b)| match a.cmp(b) {
668 CmpOrdering::Less => Some(true),
669 CmpOrdering::Greater => Some(false),
670 CmpOrdering::Equal => None,
671 })
672 .unwrap_or(a_len <= b_len) })?;
674 Some(lt.into())
675 }
676 Expr::LexGt(_, a, b) => eval_constant(&Expr::LexLt(Metadata::new(), b.clone(), a.clone())),
677 Expr::LexGeq(_, a, b) => {
678 eval_constant(&Expr::LexLeq(Metadata::new(), b.clone(), a.clone()))
679 }
680 Expr::FlatLexLt(_, a, b) => {
681 let lt = atoms_pairs_op::<i32, _>(a, b, |pairs, (a_len, b_len)| {
682 pairs
683 .iter()
684 .find_map(|(a, b)| match a.cmp(b) {
685 CmpOrdering::Less => Some(true),
686 CmpOrdering::Greater => Some(false),
687 CmpOrdering::Equal => None,
688 })
689 .unwrap_or(a_len < b_len)
690 })?;
691 Some(lt.into())
692 }
693 Expr::FlatLexLeq(_, a, b) => {
694 let lt = atoms_pairs_op::<i32, _>(a, b, |pairs, (a_len, b_len)| {
695 pairs
696 .iter()
697 .find_map(|(a, b)| match a.cmp(b) {
698 CmpOrdering::Less => Some(true),
699 CmpOrdering::Greater => Some(false),
700 CmpOrdering::Equal => None,
701 })
702 .unwrap_or(a_len <= b_len)
703 })?;
704 Some(lt.into())
705 }
706 }
707}
708
709pub fn un_op<T, A>(f: fn(T) -> A, a: &Expr) -> Option<A>
710where
711 T: TryFrom<Lit>,
712{
713 let a = unwrap_expr::<T>(a)?;
714 Some(f(a))
715}
716
717pub fn bin_op<T, A>(f: fn(T, T) -> A, a: &Expr, b: &Expr) -> Option<A>
718where
719 T: TryFrom<Lit>,
720{
721 let a = unwrap_expr::<T>(a)?;
722 let b = unwrap_expr::<T>(b)?;
723 Some(f(a, b))
724}
725
726#[allow(dead_code)]
727pub fn tern_op<T, A>(f: fn(T, T, T) -> A, a: &Expr, b: &Expr, c: &Expr) -> Option<A>
728where
729 T: TryFrom<Lit>,
730{
731 let a = unwrap_expr::<T>(a)?;
732 let b = unwrap_expr::<T>(b)?;
733 let c = unwrap_expr::<T>(c)?;
734 Some(f(a, b, c))
735}
736
737pub fn vec_op<T, A>(f: fn(Vec<T>) -> A, a: &[Expr]) -> Option<A>
738where
739 T: TryFrom<Lit>,
740{
741 let a = a.iter().map(unwrap_expr).collect::<Option<Vec<T>>>()?;
742 Some(f(a))
743}
744
745pub fn vec_lit_op<T, A>(f: fn(Vec<T>) -> A, a: &Expr) -> Option<A>
746where
747 T: TryFrom<Lit>,
748{
749 Some(f(eval_list_items(a)?))
750}
751
752type PairsCallback<T, A> = fn(Vec<(T, T)>, (usize, usize)) -> A;
753
754fn vec_expr_pairs_op<T, A>(a: &Expr, b: &Expr, f: PairsCallback<T, A>) -> Option<A>
757where
758 T: TryFrom<Lit>,
759{
760 let a_exprs = a.clone().unwrap_matrix_unchecked()?.0;
761 let b_exprs = b.clone().unwrap_matrix_unchecked()?.0;
762 let lens = (a_exprs.len(), b_exprs.len());
763
764 let lit_pairs = std::iter::zip(a_exprs, b_exprs)
765 .map(|(a, b)| Some((unwrap_expr(&a)?, unwrap_expr(&b)?)))
766 .collect::<Option<Vec<(T, T)>>>()?;
767 Some(f(lit_pairs, lens))
768}
769
770fn atoms_pairs_op<T, A>(a: &[Atom], b: &[Atom], f: PairsCallback<T, A>) -> Option<A>
772where
773 T: TryFrom<Atom>,
774{
775 let lit_pairs = Iterator::zip(a.iter(), b.iter())
776 .map(|(a, b)| Some((a.clone().try_into().ok()?, b.clone().try_into().ok()?)))
777 .collect::<Option<Vec<(T, T)>>>()?;
778 Some(f(lit_pairs, (a.len(), b.len())))
779}
780
781pub fn opt_vec_op<T, A>(f: fn(Vec<T>) -> Option<A>, a: &[Expr]) -> Option<A>
782where
783 T: TryFrom<Lit>,
784{
785 let a = a.iter().map(unwrap_expr).collect::<Option<Vec<T>>>()?;
786 f(a)
787}
788
789pub fn opt_vec_lit_op<T, A>(f: fn(Vec<T>) -> Option<A>, a: &Expr) -> Option<A>
790where
791 T: TryFrom<Lit>,
792{
793 f(eval_list_items(a)?)
794}
795
796#[allow(dead_code)]
797pub fn flat_op<T, A>(f: fn(Vec<T>, T) -> A, a: &[Expr], b: &Expr) -> Option<A>
798where
799 T: TryFrom<Lit>,
800{
801 let a = a.iter().map(unwrap_expr).collect::<Option<Vec<T>>>()?;
802 let b = unwrap_expr::<T>(b)?;
803 Some(f(a, b))
804}
805
806pub fn unwrap_expr<T: TryFrom<Lit>>(expr: &Expr) -> Option<T> {
807 let c = eval_constant(expr)?;
808 TryInto::<T>::try_into(c).ok()
809}
810
811fn eval_list_items<T>(expr: &Expr) -> Option<Vec<T>>
812where
813 T: TryFrom<Lit>,
814{
815 if let Some(items) = expr
816 .clone()
817 .unwrap_matrix_unchecked()
818 .map(|(items, _)| items)
819 {
820 return items.iter().map(unwrap_expr).collect();
821 }
822
823 let Lit::AbstractLiteral(list) = eval_constant(expr)? else {
824 return None;
825 };
826
827 let items = list.unwrap_list()?;
828 items
829 .iter()
830 .cloned()
831 .map(TryInto::try_into)
832 .collect::<Result<Vec<_>, _>>()
833 .ok()
834}
835
836fn eval_constant_comprehension(comprehension: &Comprehension) -> Option<Lit> {
837 let mut values = Vec::new();
838 eval_comprehension_qualifiers(comprehension, 0, &mut values)?;
839 Some(Lit::AbstractLiteral(
840 AbstractLiteral::matrix_implied_indices(values),
841 ))
842}
843
844fn eval_comprehension_qualifiers(
845 comprehension: &Comprehension,
846 qualifier_index: usize,
847 values: &mut Vec<Lit>,
848) -> Option<()> {
849 if qualifier_index == comprehension.qualifiers.len() {
850 values.push(eval_constant(&comprehension.return_expression)?);
851 return Some(());
852 }
853
854 match &comprehension.qualifiers[qualifier_index] {
855 ComprehensionQualifier::Generator { ptr } => {
856 let domain = ptr.domain()?;
857 let generator_values = domain
858 .resolve()
859 .and_then(|x| x.values())
860 .ok()?
861 .collect_vec();
862
863 for value in generator_values {
864 with_temporary_quantified_binding(ptr, &value, || {
865 eval_comprehension_qualifiers(comprehension, qualifier_index + 1, values)
866 })?;
867 }
868 }
869 ComprehensionQualifier::ExpressionGenerator { ptr } => {
870 let expr = ptr.as_quantified_expr()?.clone();
872 let generator_values = generator_values_from_expr(&expr)?;
873
874 for value in generator_values {
875 with_temporary_quantified_binding(ptr, &value, || {
876 eval_comprehension_qualifiers(comprehension, qualifier_index + 1, values)
877 })?;
878 }
879 }
880 ComprehensionQualifier::Condition(condition) => match eval_constant(condition)? {
881 Lit::Bool(true) => {
882 eval_comprehension_qualifiers(comprehension, qualifier_index + 1, values)?
883 }
884 Lit::Bool(false) => {}
885 _ => return None,
886 },
887 }
888
889 Some(())
890}
891
892fn generator_values_from_expr(expr: &Expr) -> Option<Vec<Lit>> {
893 match eval_constant(expr)? {
894 Lit::AbstractLiteral(AbstractLiteral::Set(values))
895 | Lit::AbstractLiteral(AbstractLiteral::MSet(values))
896 | Lit::AbstractLiteral(AbstractLiteral::Tuple(values)) => Some(values),
897 Lit::AbstractLiteral(list) => list.unwrap_list().cloned(),
898 _ => None,
899 }
900}
901
902fn with_temporary_quantified_binding<T>(
903 quantified: &crate::ast::DeclarationPtr,
904 value: &Lit,
905 f: impl FnOnce() -> Option<T>,
906) -> Option<T> {
907 let mut targets = vec![quantified.clone()];
908 if let DeclarationKind::Quantified(inner) = &*quantified.kind()
909 && let Some(generator) = inner.generator()
910 {
911 targets.push(generator.clone());
912 }
913
914 let mut originals = Vec::with_capacity(targets.len());
915 for mut target in targets {
916 let old_kind = target.replace_kind(DeclarationKind::TemporaryValueLetting(Expr::Atomic(
917 Metadata::new(),
918 Atom::Literal(value.clone()),
919 )));
920 originals.push((target, old_kind));
921 }
922
923 let result = f();
924
925 for (mut target, old_kind) in originals.into_iter().rev() {
926 let _ = target.replace_kind(old_kind);
927 }
928
929 result
930}