1use std::convert::TryInto;
6use std::rc::Rc;
7
8use crate::ast::Declaration;
9use crate::ast::{Atom, Domain, Expression as Expr, Literal as Lit, ReturnType, SymbolTable};
10
11use crate::matrix_expr;
12use crate::metadata::Metadata;
13use crate::rule_engine::{
14 register_rule, register_rule_set, ApplicationError, ApplicationResult, Reduction,
15};
16use crate::rules::extra_check;
17
18use crate::solver::SolverFamily;
19use itertools::Itertools;
20use uniplate::Uniplate;
21use ApplicationError::*;
22
23use super::utils::{is_flat, to_aux_var};
24
25register_rule_set!("Minion", ("Base"), (SolverFamily::Minion));
26
27#[register_rule(("Minion", 4200))]
28fn introduce_producteq(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
29 let val: Atom;
31 let product: Expr;
32
33 match expr.clone() {
34 Expr::Eq(_m, a, b) => {
35 let a_atom: Option<&Atom> = (&a).try_into().ok();
36 let b_atom: Option<&Atom> = (&b).try_into().ok();
37
38 if let Some(f) = a_atom {
39 val = f.clone();
41 product = *b;
42 } else if let Some(f) = b_atom {
43 val = f.clone();
45 product = *a;
46 } else {
47 return Err(RuleNotApplicable);
48 }
49 }
50 Expr::AuxDeclaration(_m, name, e) => {
51 val = name.into();
52 product = *e;
53 }
54 _ => {
55 return Err(RuleNotApplicable);
56 }
57 }
58
59 if !(matches!(product, Expr::Product(_, _,))) {
60 return Err(RuleNotApplicable);
61 }
62
63 let Expr::Product(_, mut factors) = product else {
64 return Err(RuleNotApplicable);
65 };
66
67 if factors.len() < 2 {
68 return Err(RuleNotApplicable);
69 }
70
71 #[allow(clippy::unwrap_used)] let x: Atom = factors
79 .pop()
80 .unwrap()
81 .try_into()
82 .or(Err(RuleNotApplicable))?;
83
84 #[allow(clippy::unwrap_used)] let mut y: Atom = factors
86 .pop()
87 .unwrap()
88 .try_into()
89 .or(Err(RuleNotApplicable))?;
90
91 let mut symbols = symbols.clone();
92 let mut new_tops: Vec<Expr> = vec![];
93
94 while let Some(next_factor) = factors.pop() {
96 let next_factor_atom: Atom = next_factor.clone().try_into().or(Err(RuleNotApplicable))?;
99
100 let aux_var = symbols.gensym();
101 let aux_domain = Expr::Product(Metadata::new(), vec![y.clone().into(), next_factor])
104 .domain_of(&symbols)
105 .ok_or(ApplicationError::DomainError)?;
106
107 symbols.insert(Rc::new(Declaration::new_var(aux_var.clone(), aux_domain)));
108
109 let new_top_expr =
110 Expr::FlatProductEq(Metadata::new(), y, next_factor_atom, aux_var.clone().into());
111
112 new_tops.push(new_top_expr);
113 y = aux_var.into();
114 }
115
116 Ok(Reduction::new(
117 Expr::FlatProductEq(Metadata::new(), x, y, val),
118 new_tops,
119 symbols,
120 ))
121}
122
123#[register_rule(("Minion", 4600))]
183fn introduce_weighted_sumleq_sumgeq(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
184 enum EqualityKind {
217 Eq,
218 Leq,
219 Geq,
220 }
221
222 fn match_sum_total(
229 a: Expr,
230 b: Expr,
231 equality_kind: EqualityKind,
232 ) -> Result<(Vec<Expr>, Atom, EqualityKind), ApplicationError> {
233 match (a, b, equality_kind) {
234 (Expr::Sum(_, sum_terms), Expr::Atomic(_, total), EqualityKind::Leq) => {
235 let sum_terms = sum_terms.unwrap_list().ok_or(RuleNotApplicable)?;
236 Ok((sum_terms, total, EqualityKind::Leq))
237 }
238 (Expr::Atomic(_, total), Expr::Sum(_, sum_terms), EqualityKind::Leq) => {
239 let sum_terms = sum_terms.unwrap_list().ok_or(RuleNotApplicable)?;
240 Ok((sum_terms, total, EqualityKind::Geq))
241 }
242 (Expr::Sum(_, sum_terms), Expr::Atomic(_, total), EqualityKind::Geq) => {
243 let sum_terms = sum_terms.unwrap_list().ok_or(RuleNotApplicable)?;
244 Ok((sum_terms, total, EqualityKind::Geq))
245 }
246 (Expr::Atomic(_, total), Expr::Sum(_, sum_terms), EqualityKind::Geq) => {
247 let sum_terms = sum_terms.unwrap_list().ok_or(RuleNotApplicable)?;
248 Ok((sum_terms, total, EqualityKind::Leq))
249 }
250 (Expr::Sum(_, sum_terms), Expr::Atomic(_, total), EqualityKind::Eq) => {
251 let sum_terms = sum_terms.unwrap_list().ok_or(RuleNotApplicable)?;
252 Ok((sum_terms, total, EqualityKind::Eq))
253 }
254 (Expr::Atomic(_, total), Expr::Sum(_, sum_terms), EqualityKind::Eq) => {
255 let sum_terms = sum_terms.unwrap_list().ok_or(RuleNotApplicable)?;
256 Ok((sum_terms, total, EqualityKind::Eq))
257 }
258 _ => Err(RuleNotApplicable),
259 }
260 }
261
262 let (sum_exprs, total, equality_kind) = match expr.clone() {
263 Expr::Leq(_, a, b) => Ok(match_sum_total(*a, *b, EqualityKind::Leq)?),
264 Expr::Geq(_, a, b) => Ok(match_sum_total(*a, *b, EqualityKind::Geq)?),
265 Expr::Eq(_, a, b) => Ok(match_sum_total(*a, *b, EqualityKind::Eq)?),
266 Expr::AuxDeclaration(_, n, a) => {
267 let total: Atom = n.into();
268 if let Expr::Sum(_, sum_terms) = *a {
269 let sum_terms = sum_terms.unwrap_list().ok_or(RuleNotApplicable)?;
270 Ok((sum_terms, total, EqualityKind::Eq))
271 } else {
272 Err(RuleNotApplicable)
273 }
274 }
275 _ => Err(RuleNotApplicable),
276 }?;
277
278 let mut new_top_exprs: Vec<Expr> = vec![];
279 let mut symbols = symbols.clone();
280
281 let mut coefficients: Vec<Lit> = vec![];
282 let mut vars: Vec<Atom> = vec![];
283
284 let mut found_non_one_coeff = false;
286
287 for expr in sum_exprs {
289 let (coeff, var): (Lit, Atom) = match expr {
290 Expr::Atomic(_, atom) => (Lit::Int(1), atom),
292
293 Expr::Product(_, factors)
299 if factors.len() > 1 && matches!(factors[0], Expr::Atomic(_, Atom::Literal(_))) =>
300 {
301 match &factors[..] {
302 [Expr::Atomic(_, Atom::Literal(c)), Expr::Atomic(_, atom)] => {
304 (c.clone(), atom.clone())
305 }
306
307 [Expr::Atomic(_, Atom::Literal(c)), e1] => {
309 #[allow(clippy::unwrap_used)] let aux_var_info = to_aux_var(e1, &symbols).unwrap();
311
312 symbols = aux_var_info.symbols();
313 let var = aux_var_info.as_atom();
314 new_top_exprs.push(aux_var_info.top_level_expr());
315 (c.clone(), var)
316 }
317
318 [Expr::Atomic(_, Atom::Literal(c)), ref rest @ ..] => {
320 let e1 = Expr::Product(Metadata::new(), rest.to_vec());
321
322 #[allow(clippy::unwrap_used)] let aux_var_info = to_aux_var(&e1, &symbols).unwrap();
324
325 symbols = aux_var_info.symbols();
326 let var = aux_var_info.as_atom();
327 new_top_exprs.push(aux_var_info.top_level_expr());
328 (c.clone(), var)
329 }
330
331 _ => unreachable!(),
332 }
333 }
334
335 Expr::Neg(_, e) => {
339 let v: Atom = if let Some(aux_var_info) = to_aux_var(&e, &symbols) {
341 symbols = aux_var_info.symbols();
342 new_top_exprs.push(aux_var_info.top_level_expr());
343 aux_var_info.as_atom()
344 } else {
345 #[allow(clippy::unwrap_used)]
347 e.try_into().unwrap()
348 };
349
350 (Lit::Int(-1), v)
351 }
352
353 e => {
357 let aux_var_info = to_aux_var(&e, &symbols).ok_or(RuleNotApplicable)?;
359
360 symbols = aux_var_info.symbols();
361 let var = aux_var_info.as_atom();
362 new_top_exprs.push(aux_var_info.top_level_expr());
363 (Lit::Int(1), var)
364 }
365 };
366
367 let coeff_num: i32 = coeff.clone().try_into().or(Err(RuleNotApplicable))?;
368 found_non_one_coeff |= coeff_num != 1;
369 coefficients.push(coeff);
370 vars.push(var);
371 }
372
373 let use_weighted_sum = found_non_one_coeff;
374 let new_expr: Expr = match (equality_kind, use_weighted_sum) {
376 (EqualityKind::Eq, true) => Expr::And(
377 Metadata::new(),
378 Box::new(matrix_expr![
379 Expr::FlatWeightedSumLeq(
380 Metadata::new(),
381 coefficients.clone(),
382 vars.clone(),
383 total.clone(),
384 ),
385 Expr::FlatWeightedSumGeq(Metadata::new(), coefficients, vars, total),
386 ]),
387 ),
388 (EqualityKind::Eq, false) => Expr::And(
389 Metadata::new(),
390 Box::new(matrix_expr![
391 Expr::FlatSumLeq(Metadata::new(), vars.clone(), total.clone()),
392 Expr::FlatSumGeq(Metadata::new(), vars, total),
393 ]),
394 ),
395 (EqualityKind::Leq, true) => {
396 Expr::FlatWeightedSumLeq(Metadata::new(), coefficients, vars, total)
397 }
398 (EqualityKind::Leq, false) => Expr::FlatSumLeq(Metadata::new(), vars, total),
399 (EqualityKind::Geq, true) => {
400 Expr::FlatWeightedSumGeq(Metadata::new(), coefficients, vars, total)
401 }
402 (EqualityKind::Geq, false) => Expr::FlatSumGeq(Metadata::new(), vars, total),
403 };
404
405 Ok(Reduction::new(new_expr, new_top_exprs, symbols))
406}
407
408#[register_rule(("Minion", 4200))]
409fn introduce_diveq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
410 let val: Atom;
412 let div: Expr;
413 let meta: Metadata;
414
415 match expr.clone() {
416 Expr::Eq(m, a, b) => {
417 meta = m;
418
419 let a_atom: Option<&Atom> = (&a).try_into().ok();
420 let b_atom: Option<&Atom> = (&b).try_into().ok();
421
422 if let Some(f) = a_atom {
423 val = f.clone();
425 div = *b;
426 } else if let Some(f) = b_atom {
427 val = f.clone();
429 div = *a;
430 } else {
431 return Err(RuleNotApplicable);
432 }
433 }
434 Expr::AuxDeclaration(m, name, e) => {
435 meta = m;
436 val = name.into();
437 div = *e;
438 }
439 _ => {
440 return Err(RuleNotApplicable);
441 }
442 }
443
444 if !(matches!(div, Expr::SafeDiv(_, _, _))) {
445 return Err(RuleNotApplicable);
446 }
447
448 let children = div.children();
449 let a: &Atom = (&children[0]).try_into().or(Err(RuleNotApplicable))?;
450 let b: &Atom = (&children[1]).try_into().or(Err(RuleNotApplicable))?;
451
452 Ok(Reduction::pure(Expr::MinionDivEqUndefZero(
453 meta.clone_dirty(),
454 a.clone(),
455 b.clone(),
456 val,
457 )))
458}
459
460#[register_rule(("Minion", 4200))]
461fn introduce_modeq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
462 let val: Atom;
464 let div: Expr;
465 let meta: Metadata;
466
467 match expr.clone() {
468 Expr::Eq(m, a, b) => {
469 meta = m;
470 let a_atom: Option<&Atom> = (&a).try_into().ok();
471 let b_atom: Option<&Atom> = (&b).try_into().ok();
472
473 if let Some(f) = a_atom {
474 val = f.clone();
476 div = *b;
477 } else if let Some(f) = b_atom {
478 val = f.clone();
480 div = *a;
481 } else {
482 return Err(RuleNotApplicable);
483 }
484 }
485 Expr::AuxDeclaration(m, name, e) => {
486 meta = m;
487 val = name.into();
488 div = *e;
489 }
490 _ => {
491 return Err(RuleNotApplicable);
492 }
493 }
494
495 if !(matches!(div, Expr::SafeMod(_, _, _))) {
496 return Err(RuleNotApplicable);
497 }
498
499 let children = div.children();
500 let a: &Atom = (&children[0]).try_into().or(Err(RuleNotApplicable))?;
501 let b: &Atom = (&children[1]).try_into().or(Err(RuleNotApplicable))?;
502
503 Ok(Reduction::pure(Expr::MinionModuloEqUndefZero(
504 meta.clone_dirty(),
505 a.clone(),
506 b.clone(),
507 val,
508 )))
509}
510
511#[register_rule(("Minion", 4400))]
512fn introduce_abseq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
513 let (x, abs_y): (Atom, Expr) = match expr.clone() {
514 Expr::Eq(_, a, b) => {
515 let a_atom: Option<&Atom> = (&*a).try_into().ok();
516 let b_atom: Option<&Atom> = (&*b).try_into().ok();
517
518 if let Some(a_atom) = a_atom {
519 Ok((a_atom.clone(), *b))
520 } else if let Some(b_atom) = b_atom {
521 Ok((b_atom.clone(), *a))
522 } else {
523 Err(RuleNotApplicable)
524 }
525 }
526
527 Expr::AuxDeclaration(_, a, b) => Ok((a.into(), *b)),
528
529 _ => Err(RuleNotApplicable),
530 }?;
531
532 let Expr::Abs(_, y) = abs_y else {
533 return Err(RuleNotApplicable);
534 };
535
536 let y: Atom = (*y).try_into().or(Err(RuleNotApplicable))?;
537
538 Ok(Reduction::pure(Expr::FlatAbsEq(Metadata::new(), x, y)))
539}
540
541#[register_rule(("Minion", 4200))]
543fn introduce_poweq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
544 let (a, b, total) = match expr.clone() {
545 Expr::Eq(_, e1, e2) => match (*e1, *e2) {
546 (Expr::Atomic(_, total), Expr::SafePow(_, a, b)) => Ok((a, b, total)),
547 (Expr::SafePow(_, a, b), Expr::Atomic(_, total)) => Ok((a, b, total)),
548 _ => Err(RuleNotApplicable),
549 },
550
551 Expr::AuxDeclaration(_, total, e) => match *e {
552 Expr::SafePow(_, a, b) => Ok((a, b, Atom::Reference(total))),
553 _ => Err(RuleNotApplicable),
554 },
555 _ => Err(RuleNotApplicable),
556 }?;
557
558 let a: Atom = (*a).try_into().or(Err(RuleNotApplicable))?;
559 let b: Atom = (*b).try_into().or(Err(RuleNotApplicable))?;
560
561 Ok(Reduction::pure(Expr::MinionPow(
562 Metadata::new(),
563 a,
564 b,
565 total,
566 )))
567}
568
569#[register_rule(("Minion", 4200))]
571fn introduce_flat_alldiff(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
572 let Expr::AllDiff(_, es) = expr else {
573 return Err(RuleNotApplicable);
574 };
575
576 let es = es.clone().unwrap_list().ok_or(RuleNotApplicable)?;
577
578 let atoms = es
579 .into_iter()
580 .map(|e| match e {
581 Expr::Atomic(_, atom) => Ok(atom),
582 _ => Err(RuleNotApplicable),
583 })
584 .process_results(|iter| iter.collect_vec())?;
585
586 Ok(Reduction::pure(Expr::FlatAllDiff(Metadata::new(), atoms)))
587}
588
589#[register_rule(("Minion", 4400))]
597fn introduce_minuseq_from_eq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
598 let Expr::Eq(_, a, b) = expr else {
599 return Err(RuleNotApplicable);
600 };
601
602 fn try_get_atoms(a: &Expr, b: &Expr) -> Option<(Atom, Atom)> {
603 let a: &Atom = a.try_into().ok()?;
604 let Expr::Neg(_, b) = b else {
605 return None;
606 };
607
608 let b: &Atom = b.try_into().ok()?;
609
610 Some((a.clone(), b.clone()))
611 }
612
613 let a = *a.clone();
614 let b = *b.clone();
615
616 let Some((x, y)) = try_get_atoms(&a, &b).or_else(|| try_get_atoms(&b, &a)) else {
618 return Err(RuleNotApplicable);
619 };
620
621 Ok(Reduction::pure(Expr::FlatMinusEq(Metadata::new(), x, y)))
622}
623
624#[register_rule(("Minion", 4400))]
632fn introduce_minuseq_from_aux_decl(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
633 let Expr::AuxDeclaration(_, a, b) = expr else {
636 return Err(RuleNotApplicable);
637 };
638
639 let a = Atom::Reference(a.clone());
640
641 let Expr::Neg(_, b) = (**b).clone() else {
642 return Err(RuleNotApplicable);
643 };
644
645 let Ok(b) = b.try_into() else {
646 return Err(RuleNotApplicable);
647 };
648
649 Ok(Reduction::pure(Expr::FlatMinusEq(Metadata::new(), a, b)))
650}
651
652#[register_rule(("Minion", 4400))]
662fn introduce_reifyimply_ineq_from_imply(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
663 let Expr::Imply(_, x, y) = expr else {
664 return Err(RuleNotApplicable);
665 };
666
667 let x_atom: &Atom = x.as_ref().try_into().or(Err(RuleNotApplicable))?;
668
669 if let Ok(y_atom) = TryInto::<&Atom>::try_into(y.as_ref()) {
673 Ok(Reduction::pure(Expr::FlatIneq(
674 Metadata::new(),
675 x_atom.clone(),
676 y_atom.clone(),
677 0.into(),
678 )))
679 } else {
680 Ok(Reduction::pure(Expr::MinionReifyImply(
681 Metadata::new(),
682 y.clone(),
683 x_atom.clone(),
684 )))
685 }
686}
687
688#[register_rule(("Minion", 4400))]
692fn introduce_wininterval_set_from_indomain(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
693 let Expr::InDomain(_, e, domain) = expr else {
694 return Err(RuleNotApplicable);
695 };
696
697 let Expr::Atomic(_, atom @ Atom::Reference(_)) = e.as_ref() else {
698 return Err(RuleNotApplicable);
699 };
700
701 let Domain::IntDomain(ranges) = domain else {
702 return Err(RuleNotApplicable);
703 };
704
705 let mut out_ranges = vec![];
706
707 for range in ranges {
708 match range {
709 crate::ast::Range::Single(x) => {
710 out_ranges.push(*x);
711 out_ranges.push(*x);
712 }
713 crate::ast::Range::Bounded(x, y) => {
714 out_ranges.push(*x);
715 out_ranges.push(*y);
716 }
717 crate::ast::Range::UnboundedR(_) | crate::ast::Range::UnboundedL(_) => {
718 return Err(RuleNotApplicable);
719 }
720 }
721 }
722
723 Ok(Reduction::pure(Expr::MinionWInIntervalSet(
724 Metadata::new(),
725 atom.clone(),
726 out_ranges,
727 )))
728}
729
730#[register_rule(("Minion", 4400))]
735fn introduce_element_from_index(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
736 let (equalto, subject, indices) = match expr.clone() {
737 Expr::Eq(_, e1, e2) => match (*e1, *e2) {
738 (Expr::Atomic(_, eq), Expr::SafeIndex(_, subject, indices)) => {
739 Ok((eq, subject, indices))
740 }
741 (Expr::SafeIndex(_, subject, indices), Expr::Atomic(_, eq)) => {
742 Ok((eq, subject, indices))
743 }
744 _ => Err(RuleNotApplicable),
745 },
746 Expr::AuxDeclaration(_, name, expr) => match *expr {
747 Expr::SafeIndex(_, subject, indices) => Ok((Atom::Reference(name), subject, indices)),
748 _ => Err(RuleNotApplicable),
749 },
750 _ => Err(RuleNotApplicable),
751 }?;
752
753 if indices.len() != 1 {
754 return Err(RuleNotApplicable);
755 }
756
757 let Some(list) = subject.unwrap_list() else {
758 return Err(RuleNotApplicable);
759 };
760
761 let Expr::Atomic(_, index) = indices[0].clone() else {
762 return Err(RuleNotApplicable);
763 };
764
765 let mut atom_list = vec![];
766
767 for elem in list {
768 let Expr::Atomic(_, elem) = elem else {
769 return Err(RuleNotApplicable);
770 };
771
772 atom_list.push(elem);
773 }
774
775 Ok(Reduction::pure(Expr::MinionElementOne(
776 Metadata::new(),
777 atom_list,
778 index,
779 equalto,
780 )))
781}
782
783#[register_rule(("Minion", 4200))]
806fn flatten_imply(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
807 let Expr::Imply(meta, x, y) = expr else {
808 return Err(RuleNotApplicable);
809 };
810
811 let aux_var_info = to_aux_var(x.as_ref(), symbols).ok_or(RuleNotApplicable)?;
813
814 let symbols = aux_var_info.symbols();
815 let new_x = aux_var_info.as_expr();
816
817 Ok(Reduction::new(
818 Expr::Imply(meta.clone(), Box::new(new_x), y.clone()),
819 vec![aux_var_info.top_level_expr()],
820 symbols,
821 ))
822}
823
824#[register_rule(("Minion", 4200))]
825fn flatten_generic(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
826 if !matches!(
827 expr,
828 Expr::SafeDiv(_, _, _)
829 | Expr::Neq(_, _, _)
830 | Expr::SafeMod(_, _, _)
831 | Expr::SafePow(_, _, _)
832 | Expr::Leq(_, _, _)
833 | Expr::Geq(_, _, _)
834 | Expr::Abs(_, _)
835 | Expr::Product(_, _)
836 | Expr::Neg(_, _)
837 | Expr::Not(_, _)
838 | Expr::SafeIndex(_, _, _)
839 | Expr::InDomain(_, _, _)
840 ) {
841 return Err(RuleNotApplicable);
842 }
843
844 let mut children = expr.children();
845
846 let mut symbols = symbols.clone();
847 let mut num_changed = 0;
848 let mut new_tops: Vec<Expr> = vec![];
849
850 for child in children.iter_mut() {
851 if let Some(aux_var_info) = to_aux_var(child, &symbols) {
852 symbols = aux_var_info.symbols();
853 new_tops.push(aux_var_info.top_level_expr());
854 *child = aux_var_info.as_expr();
855 num_changed += 1;
856 }
857 }
858
859 if num_changed == 0 {
860 return Err(RuleNotApplicable);
861 }
862
863 let expr = expr.with_children(children);
864
865 Ok(Reduction::new(expr, new_tops, symbols))
866}
867
868#[register_rule(("Minion", 4200))]
869fn flatten_eq(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
870 if !matches!(expr, Expr::Eq(_, _, _)) {
871 return Err(RuleNotApplicable);
872 }
873
874 let mut children = expr.children();
875 debug_assert_eq!(children.len(), 2);
876
877 let mut symbols = symbols.clone();
878 let mut num_changed = 0;
879 let mut new_tops: Vec<Expr> = vec![];
880
881 for child in children.iter_mut() {
882 if let Some(aux_var_info) = to_aux_var(child, &symbols) {
883 symbols = aux_var_info.symbols();
884 new_tops.push(aux_var_info.top_level_expr());
885 *child = aux_var_info.as_expr();
886 num_changed += 1;
887 }
888 }
889
890 if num_changed != 2 {
892 return Err(RuleNotApplicable);
893 }
894
895 let expr = expr.with_children(children);
896
897 Ok(Reduction::new(expr, new_tops, symbols))
898}
899
900#[register_rule(("Minion", 4100))]
906fn geq_to_ineq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
907 let Expr::Geq(meta, e1, e2) = expr.clone() else {
908 return Err(RuleNotApplicable);
909 };
910
911 let Expr::Atomic(_, x) = *e1 else {
912 return Err(RuleNotApplicable);
913 };
914
915 let Expr::Atomic(_, y) = *e2 else {
916 return Err(RuleNotApplicable);
917 };
918
919 Ok(Reduction::pure(Expr::FlatIneq(
920 meta.clone_dirty(),
921 y,
922 x,
923 Lit::Int(0),
924 )))
925}
926
927#[register_rule(("Minion", 4100))]
933fn leq_to_ineq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
934 let Expr::Leq(meta, e1, e2) = expr.clone() else {
935 return Err(RuleNotApplicable);
936 };
937
938 let Expr::Atomic(_, x) = *e1 else {
939 return Err(RuleNotApplicable);
940 };
941
942 let Expr::Atomic(_, y) = *e2 else {
943 return Err(RuleNotApplicable);
944 };
945
946 Ok(Reduction::pure(Expr::FlatIneq(
947 meta.clone_dirty(),
948 x,
949 y,
950 Lit::Int(0),
951 )))
952}
953
954#[register_rule(("Minion",4500))]
960fn x_leq_y_plus_k_to_ineq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
961 let Expr::Leq(meta, e1, e2) = expr.clone() else {
962 return Err(RuleNotApplicable);
963 };
964
965 let Expr::Atomic(_, x) = *e1 else {
966 return Err(RuleNotApplicable);
967 };
968
969 let Expr::Sum(_, sum_exprs) = *e2 else {
970 return Err(RuleNotApplicable);
971 };
972
973 let sum_exprs = sum_exprs.unwrap_list().ok_or(RuleNotApplicable)?;
974 let (y, k) = match sum_exprs.as_slice() {
975 [Expr::Atomic(_, y), Expr::Atomic(_, Atom::Literal(k))] => (y, k),
976 [Expr::Atomic(_, Atom::Literal(k)), Expr::Atomic(_, y)] => (y, k),
977 _ => {
978 return Err(RuleNotApplicable);
979 }
980 };
981
982 Ok(Reduction::pure(Expr::FlatIneq(
983 meta.clone_dirty(),
984 x,
985 y.clone(),
986 k.clone(),
987 )))
988}
989
990#[register_rule(("Minion",4800))]
994fn y_plus_k_geq_x_to_ineq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
995 let Expr::Geq(meta, e2, e1) = expr.clone() else {
997 return Err(RuleNotApplicable);
998 };
999
1000 let Expr::Atomic(_, x) = *e1 else {
1001 return Err(RuleNotApplicable);
1002 };
1003
1004 let Expr::Sum(_, sum_exprs) = *e2 else {
1005 return Err(RuleNotApplicable);
1006 };
1007
1008 let sum_exprs = sum_exprs.unwrap_list().ok_or(RuleNotApplicable)?;
1009 let (y, k) = match sum_exprs.as_slice() {
1010 [Expr::Atomic(_, y), Expr::Atomic(_, Atom::Literal(k))] => (y, k),
1011 [Expr::Atomic(_, Atom::Literal(k)), Expr::Atomic(_, y)] => (y, k),
1012 _ => {
1013 return Err(RuleNotApplicable);
1014 }
1015 };
1016
1017 Ok(Reduction::pure(Expr::FlatIneq(
1018 meta.clone_dirty(),
1019 x,
1020 y.clone(),
1021 k.clone(),
1022 )))
1023}
1024
1025#[register_rule(("Minion", 4100))]
1042fn not_literal_to_wliteral(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
1043 use Domain::BoolDomain;
1044 match expr {
1045 Expr::Not(m, expr) => {
1046 if let Expr::Atomic(_, Atom::Reference(name)) = (**expr).clone() {
1047 if symbols
1048 .domain(&name)
1049 .is_some_and(|x| matches!(x, BoolDomain))
1050 {
1051 return Ok(Reduction::pure(Expr::FlatWatchedLiteral(
1052 m.clone_dirty(),
1053 name.clone(),
1054 Lit::Bool(false),
1055 )));
1056 }
1057 }
1058 Err(RuleNotApplicable)
1059 }
1060 _ => Err(RuleNotApplicable),
1061 }
1062}
1063
1064#[register_rule(("Minion", 4090))]
1074fn not_constraint_to_reify(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
1075 if !matches!(expr, Expr::Not(_,c) if !matches!(**c, Expr::Atomic(_,_))) {
1076 return Err(RuleNotApplicable);
1077 }
1078
1079 let Expr::Not(m, e) = expr else {
1080 unreachable!();
1081 };
1082
1083 extra_check! {
1084 if !is_flat(e) {
1085 return Err(RuleNotApplicable);
1086 }
1087 };
1088
1089 Ok(Reduction::pure(Expr::MinionReify(
1090 m.clone(),
1091 e.clone(),
1092 Atom::Literal(Lit::Bool(false)),
1093 )))
1094}
1095
1096#[register_rule(("Minion", 4400))]
1105fn bool_eq_to_reify(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
1106 let (atom, e): (Atom, Box<Expr>) = match expr {
1107 Expr::AuxDeclaration(_, name, e) => Ok((name.clone().into(), e.clone())),
1108 Expr::Eq(_, a, b) => match (a.as_ref(), b.as_ref()) {
1109 (Expr::Atomic(_, atom), _) => Ok((atom.clone(), b.clone())),
1110 (_, Expr::Atomic(_, atom)) => Ok((atom.clone(), a.clone())),
1111 _ => Err(RuleNotApplicable),
1112 },
1113
1114 _ => Err(RuleNotApplicable),
1115 }?;
1116
1117 let Some(ReturnType::Bool) = e.as_ref().return_type() else {
1120 return Err(RuleNotApplicable);
1121 };
1122
1123 Ok(Reduction::pure(Expr::MinionReify(
1124 Metadata::new(),
1125 e.clone(),
1126 atom,
1127 )))
1128}