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::metadata::Metadata;
12use crate::rule_engine::{
13 register_rule, register_rule_set, ApplicationError, ApplicationResult, Reduction,
14};
15use crate::rules::extra_check;
16
17use crate::solver::SolverFamily;
18use uniplate::Uniplate;
19use ApplicationError::*;
20
21use super::utils::{is_flat, to_aux_var};
22
23register_rule_set!("Minion", ("Base"), (SolverFamily::Minion));
24
25#[register_rule(("Minion", 4200))]
26fn introduce_producteq(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
27 let val: Atom;
29 let product: Expr;
30
31 match expr.clone() {
32 Expr::Eq(_m, a, b) => {
33 let a_atom: Option<&Atom> = (&a).try_into().ok();
34 let b_atom: Option<&Atom> = (&b).try_into().ok();
35
36 if let Some(f) = a_atom {
37 val = f.clone();
39 product = *b;
40 } else if let Some(f) = b_atom {
41 val = f.clone();
43 product = *a;
44 } else {
45 return Err(RuleNotApplicable);
46 }
47 }
48 Expr::AuxDeclaration(_m, name, e) => {
49 val = name.into();
50 product = *e;
51 }
52 _ => {
53 return Err(RuleNotApplicable);
54 }
55 }
56
57 if !(matches!(product, Expr::Product(_, _,))) {
58 return Err(RuleNotApplicable);
59 }
60
61 let Expr::Product(_, mut factors) = product else {
62 return Err(RuleNotApplicable);
63 };
64
65 if factors.len() < 2 {
66 return Err(RuleNotApplicable);
67 }
68
69 #[allow(clippy::unwrap_used)] let x: Atom = factors
77 .pop()
78 .unwrap()
79 .try_into()
80 .or(Err(RuleNotApplicable))?;
81
82 #[allow(clippy::unwrap_used)] let mut y: Atom = factors
84 .pop()
85 .unwrap()
86 .try_into()
87 .or(Err(RuleNotApplicable))?;
88
89 let mut symbols = symbols.clone();
90 let mut new_tops: Vec<Expr> = vec![];
91
92 while let Some(next_factor) = factors.pop() {
94 let next_factor_atom: Atom = next_factor.clone().try_into().or(Err(RuleNotApplicable))?;
97
98 let aux_var = symbols.gensym();
99 let aux_domain = Expr::Product(Metadata::new(), vec![y.clone().into(), next_factor])
102 .domain_of(&symbols)
103 .ok_or(ApplicationError::DomainError)?;
104
105 symbols.insert(Rc::new(Declaration::new_var(aux_var.clone(), aux_domain)));
106
107 let new_top_expr =
108 Expr::FlatProductEq(Metadata::new(), y, next_factor_atom, aux_var.clone().into());
109
110 new_tops.push(new_top_expr);
111 y = aux_var.into();
112 }
113
114 Ok(Reduction::new(
115 Expr::FlatProductEq(Metadata::new(), x, y, val),
116 new_tops,
117 symbols,
118 ))
119}
120
121#[register_rule(("Minion", 4600))]
181fn introduce_weighted_sumleq_sumgeq(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
182 enum EqualityKind {
215 Eq,
216 Leq,
217 Geq,
218 }
219
220 fn match_sum_total(
227 a: Expr,
228 b: Expr,
229 equality_kind: EqualityKind,
230 ) -> Result<(Vec<Expr>, Atom, EqualityKind), ApplicationError> {
231 match (a, b, equality_kind) {
232 (Expr::Sum(_, sum_terms), Expr::Atomic(_, total), EqualityKind::Leq) => {
233 Ok((sum_terms, total, EqualityKind::Leq))
234 }
235 (Expr::Atomic(_, total), Expr::Sum(_, sum_terms), EqualityKind::Leq) => {
236 Ok((sum_terms, total, EqualityKind::Geq))
237 }
238 (Expr::Sum(_, sum_terms), Expr::Atomic(_, total), EqualityKind::Geq) => {
239 Ok((sum_terms, total, EqualityKind::Geq))
240 }
241 (Expr::Atomic(_, total), Expr::Sum(_, sum_terms), EqualityKind::Geq) => {
242 Ok((sum_terms, total, EqualityKind::Leq))
243 }
244 (Expr::Sum(_, sum_terms), Expr::Atomic(_, total), EqualityKind::Eq) => {
245 Ok((sum_terms, total, EqualityKind::Eq))
246 }
247 (Expr::Atomic(_, total), Expr::Sum(_, sum_terms), EqualityKind::Eq) => {
248 Ok((sum_terms, total, EqualityKind::Eq))
249 }
250 _ => Err(RuleNotApplicable),
251 }
252 }
253
254 let (sum_exprs, total, equality_kind) = match expr.clone() {
255 Expr::Leq(_, a, b) => Ok(match_sum_total(*a, *b, EqualityKind::Leq)?),
256 Expr::Geq(_, a, b) => Ok(match_sum_total(*a, *b, EqualityKind::Geq)?),
257 Expr::Eq(_, a, b) => Ok(match_sum_total(*a, *b, EqualityKind::Eq)?),
258 Expr::AuxDeclaration(_, n, a) => {
259 let total: Atom = n.into();
260 if let Expr::Sum(_, sum_terms) = *a {
261 Ok((sum_terms, total, EqualityKind::Eq))
262 } else {
263 Err(RuleNotApplicable)
264 }
265 }
266 _ => Err(RuleNotApplicable),
267 }?;
268
269 let mut new_top_exprs: Vec<Expr> = vec![];
270 let mut symbols = symbols.clone();
271
272 let mut coefficients: Vec<Lit> = vec![];
273 let mut vars: Vec<Atom> = vec![];
274
275 let mut found_non_one_coeff = false;
277
278 for expr in sum_exprs {
280 let (coeff, var): (Lit, Atom) = match expr {
281 Expr::Atomic(_, atom) => (Lit::Int(1), atom),
283
284 Expr::Product(_, factors)
290 if factors.len() > 1 && matches!(factors[0], Expr::Atomic(_, Atom::Literal(_))) =>
291 {
292 match &factors[..] {
293 [Expr::Atomic(_, Atom::Literal(c)), Expr::Atomic(_, atom)] => {
295 (c.clone(), atom.clone())
296 }
297
298 [Expr::Atomic(_, Atom::Literal(c)), e1] => {
300 #[allow(clippy::unwrap_used)] let aux_var_info = to_aux_var(e1, &symbols).unwrap();
302
303 symbols = aux_var_info.symbols();
304 let var = aux_var_info.as_atom();
305 new_top_exprs.push(aux_var_info.top_level_expr());
306 (c.clone(), var)
307 }
308
309 [Expr::Atomic(_, Atom::Literal(c)), ref rest @ ..] => {
311 let e1 = Expr::Product(Metadata::new(), rest.to_vec());
312
313 #[allow(clippy::unwrap_used)] let aux_var_info = to_aux_var(&e1, &symbols).unwrap();
315
316 symbols = aux_var_info.symbols();
317 let var = aux_var_info.as_atom();
318 new_top_exprs.push(aux_var_info.top_level_expr());
319 (c.clone(), var)
320 }
321
322 _ => unreachable!(),
323 }
324 }
325
326 Expr::Neg(_, e) => {
330 let v: Atom = if let Some(aux_var_info) = to_aux_var(&e, &symbols) {
332 symbols = aux_var_info.symbols();
333 new_top_exprs.push(aux_var_info.top_level_expr());
334 aux_var_info.as_atom()
335 } else {
336 #[allow(clippy::unwrap_used)]
338 e.try_into().unwrap()
339 };
340
341 (Lit::Int(-1), v)
342 }
343
344 e => {
348 let aux_var_info = to_aux_var(&e, &symbols).ok_or(RuleNotApplicable)?;
350
351 symbols = aux_var_info.symbols();
352 let var = aux_var_info.as_atom();
353 new_top_exprs.push(aux_var_info.top_level_expr());
354 (Lit::Int(1), var)
355 }
356 };
357
358 let coeff_num: i32 = coeff.clone().try_into().or(Err(RuleNotApplicable))?;
359 found_non_one_coeff |= coeff_num != 1;
360 coefficients.push(coeff);
361 vars.push(var);
362 }
363
364 let use_weighted_sum = found_non_one_coeff;
365 let new_expr: Expr = match (equality_kind, use_weighted_sum) {
367 (EqualityKind::Eq, true) => Expr::And(
368 Metadata::new(),
369 vec![
370 Expr::FlatWeightedSumLeq(
371 Metadata::new(),
372 coefficients.clone(),
373 vars.clone(),
374 total.clone(),
375 ),
376 Expr::FlatWeightedSumGeq(Metadata::new(), coefficients, vars, total),
377 ],
378 ),
379 (EqualityKind::Eq, false) => Expr::And(
380 Metadata::new(),
381 vec![
382 Expr::FlatSumLeq(Metadata::new(), vars.clone(), total.clone()),
383 Expr::FlatSumGeq(Metadata::new(), vars, total),
384 ],
385 ),
386 (EqualityKind::Leq, true) => {
387 Expr::FlatWeightedSumLeq(Metadata::new(), coefficients, vars, total)
388 }
389 (EqualityKind::Leq, false) => Expr::FlatSumLeq(Metadata::new(), vars, total),
390 (EqualityKind::Geq, true) => {
391 Expr::FlatWeightedSumGeq(Metadata::new(), coefficients, vars, total)
392 }
393 (EqualityKind::Geq, false) => Expr::FlatSumGeq(Metadata::new(), vars, total),
394 };
395
396 Ok(Reduction::new(new_expr, new_top_exprs, symbols))
397}
398
399#[register_rule(("Minion", 4200))]
400fn introduce_diveq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
401 let val: Atom;
403 let div: Expr;
404 let meta: Metadata;
405
406 match expr.clone() {
407 Expr::Eq(m, a, b) => {
408 meta = m;
409
410 let a_atom: Option<&Atom> = (&a).try_into().ok();
411 let b_atom: Option<&Atom> = (&b).try_into().ok();
412
413 if let Some(f) = a_atom {
414 val = f.clone();
416 div = *b;
417 } else if let Some(f) = b_atom {
418 val = f.clone();
420 div = *a;
421 } else {
422 return Err(RuleNotApplicable);
423 }
424 }
425 Expr::AuxDeclaration(m, name, e) => {
426 meta = m;
427 val = name.into();
428 div = *e;
429 }
430 _ => {
431 return Err(RuleNotApplicable);
432 }
433 }
434
435 if !(matches!(div, Expr::SafeDiv(_, _, _))) {
436 return Err(RuleNotApplicable);
437 }
438
439 let children = div.children();
440 let a: &Atom = (&children[0]).try_into().or(Err(RuleNotApplicable))?;
441 let b: &Atom = (&children[1]).try_into().or(Err(RuleNotApplicable))?;
442
443 Ok(Reduction::pure(Expr::MinionDivEqUndefZero(
444 meta.clone_dirty(),
445 a.clone(),
446 b.clone(),
447 val,
448 )))
449}
450
451#[register_rule(("Minion", 4200))]
452fn introduce_modeq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
453 let val: Atom;
455 let div: Expr;
456 let meta: Metadata;
457
458 match expr.clone() {
459 Expr::Eq(m, a, b) => {
460 meta = m;
461 let a_atom: Option<&Atom> = (&a).try_into().ok();
462 let b_atom: Option<&Atom> = (&b).try_into().ok();
463
464 if let Some(f) = a_atom {
465 val = f.clone();
467 div = *b;
468 } else if let Some(f) = b_atom {
469 val = f.clone();
471 div = *a;
472 } else {
473 return Err(RuleNotApplicable);
474 }
475 }
476 Expr::AuxDeclaration(m, name, e) => {
477 meta = m;
478 val = name.into();
479 div = *e;
480 }
481 _ => {
482 return Err(RuleNotApplicable);
483 }
484 }
485
486 if !(matches!(div, Expr::SafeMod(_, _, _))) {
487 return Err(RuleNotApplicable);
488 }
489
490 let children = div.children();
491 let a: &Atom = (&children[0]).try_into().or(Err(RuleNotApplicable))?;
492 let b: &Atom = (&children[1]).try_into().or(Err(RuleNotApplicable))?;
493
494 Ok(Reduction::pure(Expr::MinionModuloEqUndefZero(
495 meta.clone_dirty(),
496 a.clone(),
497 b.clone(),
498 val,
499 )))
500}
501
502#[register_rule(("Minion", 4400))]
503fn introduce_abseq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
504 let (x, abs_y): (Atom, Expr) = match expr.clone() {
505 Expr::Eq(_, a, b) => {
506 let a_atom: Option<&Atom> = (&*a).try_into().ok();
507 let b_atom: Option<&Atom> = (&*b).try_into().ok();
508
509 if let Some(a_atom) = a_atom {
510 Ok((a_atom.clone(), *b))
511 } else if let Some(b_atom) = b_atom {
512 Ok((b_atom.clone(), *a))
513 } else {
514 Err(RuleNotApplicable)
515 }
516 }
517
518 Expr::AuxDeclaration(_, a, b) => Ok((a.into(), *b)),
519
520 _ => Err(RuleNotApplicable),
521 }?;
522
523 let Expr::Abs(_, y) = abs_y else {
524 return Err(RuleNotApplicable);
525 };
526
527 let y: Atom = (*y).try_into().or(Err(RuleNotApplicable))?;
528
529 Ok(Reduction::pure(Expr::FlatAbsEq(Metadata::new(), x, y)))
530}
531
532#[register_rule(("Minion", 4200))]
534fn introduce_poweq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
535 let (a, b, total) = match expr.clone() {
536 Expr::Eq(_, e1, e2) => match (*e1, *e2) {
537 (Expr::Atomic(_, total), Expr::SafePow(_, a, b)) => Ok((a, b, total)),
538 (Expr::SafePow(_, a, b), Expr::Atomic(_, total)) => Ok((a, b, total)),
539 _ => Err(RuleNotApplicable),
540 },
541 _ => Err(RuleNotApplicable),
542 }?;
543
544 let a: Atom = (*a).try_into().or(Err(RuleNotApplicable))?;
545 let b: Atom = (*b).try_into().or(Err(RuleNotApplicable))?;
546
547 Ok(Reduction::pure(Expr::MinionPow(
548 Metadata::new(),
549 a,
550 b,
551 total,
552 )))
553}
554
555#[register_rule(("Minion", 4400))]
563fn introduce_minuseq_from_eq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
564 let Expr::Eq(_, a, b) = expr else {
565 return Err(RuleNotApplicable);
566 };
567
568 fn try_get_atoms(a: &Expr, b: &Expr) -> Option<(Atom, Atom)> {
569 let a: &Atom = a.try_into().ok()?;
570 let Expr::Neg(_, b) = b else {
571 return None;
572 };
573
574 let b: &Atom = b.try_into().ok()?;
575
576 Some((a.clone(), b.clone()))
577 }
578
579 let a = *a.clone();
580 let b = *b.clone();
581
582 let Some((x, y)) = try_get_atoms(&a, &b).or_else(|| try_get_atoms(&b, &a)) else {
584 return Err(RuleNotApplicable);
585 };
586
587 Ok(Reduction::pure(Expr::FlatMinusEq(Metadata::new(), x, y)))
588}
589
590#[register_rule(("Minion", 4400))]
598fn introduce_minuseq_from_aux_decl(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
599 let Expr::AuxDeclaration(_, a, b) = expr else {
602 return Err(RuleNotApplicable);
603 };
604
605 let a = Atom::Reference(a.clone());
606
607 let Expr::Neg(_, b) = (**b).clone() else {
608 return Err(RuleNotApplicable);
609 };
610
611 let Ok(b) = b.try_into() else {
612 return Err(RuleNotApplicable);
613 };
614
615 Ok(Reduction::pure(Expr::FlatMinusEq(Metadata::new(), a, b)))
616}
617
618#[register_rule(("Minion", 4400))]
628fn introduce_reifyimply_ineq_from_imply(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
629 let Expr::Imply(_, x, y) = expr else {
630 return Err(RuleNotApplicable);
631 };
632
633 let x_atom: &Atom = x.as_ref().try_into().or(Err(RuleNotApplicable))?;
634
635 if let Ok(y_atom) = TryInto::<&Atom>::try_into(y.as_ref()) {
639 Ok(Reduction::pure(Expr::FlatIneq(
640 Metadata::new(),
641 x_atom.clone(),
642 y_atom.clone(),
643 0.into(),
644 )))
645 } else {
646 Ok(Reduction::pure(Expr::MinionReifyImply(
647 Metadata::new(),
648 y.clone(),
649 x_atom.clone(),
650 )))
651 }
652}
653
654#[register_rule(("Minion", 4200))]
677fn flatten_imply(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
678 let Expr::Imply(meta, x, y) = expr else {
679 return Err(RuleNotApplicable);
680 };
681
682 let aux_var_info = to_aux_var(x.as_ref(), symbols).ok_or(RuleNotApplicable)?;
684
685 let symbols = aux_var_info.symbols();
686 let new_x = aux_var_info.as_expr();
687
688 Ok(Reduction::new(
689 Expr::Imply(meta.clone(), Box::new(new_x), y.clone()),
690 vec![aux_var_info.top_level_expr()],
691 symbols,
692 ))
693}
694
695#[register_rule(("Minion", 4200))]
696fn flatten_generic(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
697 if !matches!(
698 expr,
699 Expr::SafeDiv(_, _, _)
700 | Expr::Neq(_, _, _)
701 | Expr::SafeMod(_, _, _)
702 | Expr::SafePow(_, _, _)
703 | Expr::Leq(_, _, _)
704 | Expr::Geq(_, _, _)
705 | Expr::Abs(_, _)
706 | Expr::Product(_, _)
707 | Expr::Neg(_, _)
708 | Expr::Not(_, _)
709 ) {
710 return Err(RuleNotApplicable);
711 }
712
713 let mut children = expr.children();
714
715 let mut symbols = symbols.clone();
716 let mut num_changed = 0;
717 let mut new_tops: Vec<Expr> = vec![];
718
719 for child in children.iter_mut() {
720 if let Some(aux_var_info) = to_aux_var(child, &symbols) {
721 symbols = aux_var_info.symbols();
722 new_tops.push(aux_var_info.top_level_expr());
723 *child = aux_var_info.as_expr();
724 num_changed += 1;
725 }
726 }
727
728 if num_changed == 0 {
729 return Err(RuleNotApplicable);
730 }
731
732 let expr = expr.with_children(children);
733
734 Ok(Reduction::new(expr, new_tops, symbols))
735}
736
737#[register_rule(("Minion", 4200))]
738fn flatten_eq(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
739 if !matches!(expr, Expr::Eq(_, _, _)) {
740 return Err(RuleNotApplicable);
741 }
742
743 let mut children = expr.children();
744 debug_assert_eq!(children.len(), 2);
745
746 let mut symbols = symbols.clone();
747 let mut num_changed = 0;
748 let mut new_tops: Vec<Expr> = vec![];
749
750 for child in children.iter_mut() {
751 if let Some(aux_var_info) = to_aux_var(child, &symbols) {
752 symbols = aux_var_info.symbols();
753 new_tops.push(aux_var_info.top_level_expr());
754 *child = aux_var_info.as_expr();
755 num_changed += 1;
756 }
757 }
758
759 if num_changed != 2 {
761 return Err(RuleNotApplicable);
762 }
763
764 let expr = expr.with_children(children);
765
766 Ok(Reduction::new(expr, new_tops, symbols))
767}
768
769#[register_rule(("Minion", 4100))]
775fn geq_to_ineq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
776 let Expr::Geq(meta, e1, e2) = expr.clone() else {
777 return Err(RuleNotApplicable);
778 };
779
780 let Expr::Atomic(_, x) = *e1 else {
781 return Err(RuleNotApplicable);
782 };
783
784 let Expr::Atomic(_, y) = *e2 else {
785 return Err(RuleNotApplicable);
786 };
787
788 Ok(Reduction::pure(Expr::FlatIneq(
789 meta.clone_dirty(),
790 y,
791 x,
792 Lit::Int(0),
793 )))
794}
795
796#[register_rule(("Minion", 4100))]
802fn leq_to_ineq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
803 let Expr::Leq(meta, e1, e2) = expr.clone() else {
804 return Err(RuleNotApplicable);
805 };
806
807 let Expr::Atomic(_, x) = *e1 else {
808 return Err(RuleNotApplicable);
809 };
810
811 let Expr::Atomic(_, y) = *e2 else {
812 return Err(RuleNotApplicable);
813 };
814
815 Ok(Reduction::pure(Expr::FlatIneq(
816 meta.clone_dirty(),
817 x,
818 y,
819 Lit::Int(0),
820 )))
821}
822
823#[register_rule(("Minion",4500))]
829fn x_leq_y_plus_k_to_ineq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
830 let Expr::Leq(meta, e1, e2) = expr.clone() else {
831 return Err(RuleNotApplicable);
832 };
833
834 let Expr::Atomic(_, x) = *e1 else {
835 return Err(RuleNotApplicable);
836 };
837
838 let Expr::Sum(_, sum_exprs) = *e2 else {
839 return Err(RuleNotApplicable);
840 };
841
842 let (y, k) = match sum_exprs.as_slice() {
843 [Expr::Atomic(_, y), Expr::Atomic(_, Atom::Literal(k))] => (y, k),
844 [Expr::Atomic(_, Atom::Literal(k)), Expr::Atomic(_, y)] => (y, k),
845 _ => {
846 return Err(RuleNotApplicable);
847 }
848 };
849
850 Ok(Reduction::pure(Expr::FlatIneq(
851 meta.clone_dirty(),
852 x,
853 y.clone(),
854 k.clone(),
855 )))
856}
857
858#[register_rule(("Minion",4800))]
862fn y_plus_k_geq_x_to_ineq(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
863 let Expr::Geq(meta, e2, e1) = expr.clone() else {
865 return Err(RuleNotApplicable);
866 };
867
868 let Expr::Atomic(_, x) = *e1 else {
869 return Err(RuleNotApplicable);
870 };
871
872 let Expr::Sum(_, sum_exprs) = *e2 else {
873 return Err(RuleNotApplicable);
874 };
875
876 let (y, k) = match sum_exprs.as_slice() {
877 [Expr::Atomic(_, y), Expr::Atomic(_, Atom::Literal(k))] => (y, k),
878 [Expr::Atomic(_, Atom::Literal(k)), Expr::Atomic(_, y)] => (y, k),
879 _ => {
880 return Err(RuleNotApplicable);
881 }
882 };
883
884 Ok(Reduction::pure(Expr::FlatIneq(
885 meta.clone_dirty(),
886 x,
887 y.clone(),
888 k.clone(),
889 )))
890}
891
892#[register_rule(("Minion", 4100))]
909fn not_literal_to_wliteral(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
910 use Domain::BoolDomain;
911 match expr {
912 Expr::Not(m, expr) => {
913 if let Expr::Atomic(_, Atom::Reference(name)) = (**expr).clone() {
914 if symbols
915 .domain(&name)
916 .is_some_and(|x| matches!(x, BoolDomain))
917 {
918 return Ok(Reduction::pure(Expr::FlatWatchedLiteral(
919 m.clone_dirty(),
920 name.clone(),
921 Lit::Bool(false),
922 )));
923 }
924 }
925 Err(RuleNotApplicable)
926 }
927 _ => Err(RuleNotApplicable),
928 }
929}
930
931#[register_rule(("Minion", 4090))]
941fn not_constraint_to_reify(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
942 if !matches!(expr, Expr::Not(_,c) if !matches!(**c, Expr::Atomic(_,_))) {
943 return Err(RuleNotApplicable);
944 }
945
946 let Expr::Not(m, e) = expr else {
947 unreachable!();
948 };
949
950 extra_check! {
951 if !is_flat(e) {
952 return Err(RuleNotApplicable);
953 }
954 };
955
956 Ok(Reduction::pure(Expr::MinionReify(
957 m.clone(),
958 e.clone(),
959 Atom::Literal(Lit::Bool(false)),
960 )))
961}
962
963#[register_rule(("Minion", 4400))]
972fn bool_eq_to_reify(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
973 let (atom, e): (Atom, Box<Expr>) = match expr {
974 Expr::AuxDeclaration(_, name, e) => Ok((name.clone().into(), e.clone())),
975 Expr::Eq(_, a, b) => match (a.as_ref(), b.as_ref()) {
976 (Expr::Atomic(_, atom), _) => Ok((atom.clone(), b.clone())),
977 (_, Expr::Atomic(_, atom)) => Ok((atom.clone(), a.clone())),
978 _ => Err(RuleNotApplicable),
979 },
980
981 _ => Err(RuleNotApplicable),
982 }?;
983
984 let Some(ReturnType::Bool) = e.as_ref().return_type() else {
987 return Err(RuleNotApplicable);
988 };
989
990 Ok(Reduction::pure(Expr::MinionReify(
991 Metadata::new(),
992 e.clone(),
993 atom,
994 )))
995}