1use ordeal::{BoolTerm, BvTerm, Sort};
25use std::borrow::Borrow;
26use std::cmp::Ordering;
27use std::fmt;
28
29fn mask(width: u32) -> u128 {
31 if width >= 128 {
32 u128::MAX
33 } else {
34 (1u128 << width) - 1
35 }
36}
37
38#[derive(Clone, Debug)]
42pub struct BV {
43 term: BvTerm,
44 width: u32,
45}
46
47#[derive(Clone, Debug)]
51pub struct Bool {
52 term: BoolTerm,
53}
54
55fn bv_rank(t: &BvTerm) -> u8 {
60 match t {
61 BvTerm::Const { .. } => 0,
62 BvTerm::Var { .. } => 1,
63 BvTerm::Add(..) => 2,
64 BvTerm::Sub(..) => 3,
65 BvTerm::Mul(..) => 4,
66 BvTerm::Udiv(..) => 5,
67 BvTerm::Urem(..) => 6,
73 BvTerm::And(..) => 7,
74 BvTerm::Or(..) => 8,
75 BvTerm::Xor(..) => 9,
76 BvTerm::Shl(..) => 10,
77 BvTerm::Lshr(..) => 11,
78 BvTerm::Ashr(..) => 12,
79 BvTerm::Rotr(..) => 13,
80 BvTerm::Extract { .. } => 14,
81 BvTerm::Concat(..) => 15,
82 BvTerm::ZeroExt { .. } => 16,
83 BvTerm::SignExt { .. } => 17,
84 BvTerm::Ite { .. } => 18,
85 other => panic!(
96 "synth-verify: unmodeled ordeal BvTerm variant {other:?} — \
97 extend term.rs + solver.rs before trusting any verdict"
98 ),
99 }
100}
101
102fn bool_rank(t: &BoolTerm) -> u8 {
103 match t {
104 BoolTerm::Eq(..) => 0,
105 BoolTerm::Ne(..) => 1,
106 BoolTerm::Ult(..) => 2,
107 BoolTerm::Ule(..) => 3,
108 BoolTerm::Ugt(..) => 4,
109 BoolTerm::Uge(..) => 5,
110 BoolTerm::Slt(..) => 6,
111 BoolTerm::Sle(..) => 7,
112 BoolTerm::Sgt(..) => 8,
113 BoolTerm::Sge(..) => 9,
114 BoolTerm::Not(..) => 10,
115 BoolTerm::And(..) => 11,
116 BoolTerm::Or(..) => 12,
117 other => panic!(
119 "synth-verify: unmodeled ordeal BoolTerm variant {other:?} — \
120 extend term.rs + solver.rs before trusting any verdict"
121 ),
122 }
123}
124
125fn ord_bv(a: &BvTerm, b: &BvTerm) -> Ordering {
127 bv_rank(a).cmp(&bv_rank(b)).then_with(|| match (a, b) {
128 (
129 BvTerm::Const {
130 value: va,
131 sort: sa,
132 },
133 BvTerm::Const {
134 value: vb,
135 sort: sb,
136 },
137 ) => sa.width.cmp(&sb.width).then(va.cmp(vb)),
138 (BvTerm::Var { name: na, sort: sa }, BvTerm::Var { name: nb, sort: sb }) => {
139 sa.width.cmp(&sb.width).then_with(|| na.cmp(nb))
140 }
141 (BvTerm::Add(a1, a2), BvTerm::Add(b1, b2))
142 | (BvTerm::Sub(a1, a2), BvTerm::Sub(b1, b2))
143 | (BvTerm::Mul(a1, a2), BvTerm::Mul(b1, b2))
144 | (BvTerm::Udiv(a1, a2), BvTerm::Udiv(b1, b2))
145 | (BvTerm::Urem(a1, a2), BvTerm::Urem(b1, b2))
146 | (BvTerm::And(a1, a2), BvTerm::And(b1, b2))
147 | (BvTerm::Or(a1, a2), BvTerm::Or(b1, b2))
148 | (BvTerm::Xor(a1, a2), BvTerm::Xor(b1, b2))
149 | (BvTerm::Shl(a1, a2), BvTerm::Shl(b1, b2))
150 | (BvTerm::Lshr(a1, a2), BvTerm::Lshr(b1, b2))
151 | (BvTerm::Ashr(a1, a2), BvTerm::Ashr(b1, b2))
152 | (BvTerm::Rotr(a1, a2), BvTerm::Rotr(b1, b2))
153 | (BvTerm::Concat(a1, a2), BvTerm::Concat(b1, b2)) => {
154 ord_bv(a1, b1).then_with(|| ord_bv(a2, b2))
155 }
156 (
157 BvTerm::Extract {
158 hi: ha,
159 lo: la,
160 arg: aa,
161 },
162 BvTerm::Extract {
163 hi: hb,
164 lo: lb,
165 arg: ab,
166 },
167 ) => ha.cmp(hb).then(la.cmp(lb)).then_with(|| ord_bv(aa, ab)),
168 (BvTerm::ZeroExt { by: ba, arg: aa }, BvTerm::ZeroExt { by: bb, arg: ab })
169 | (BvTerm::SignExt { by: ba, arg: aa }, BvTerm::SignExt { by: bb, arg: ab }) => {
170 ba.cmp(bb).then_with(|| ord_bv(aa, ab))
171 }
172 (
173 BvTerm::Ite {
174 cond: ca,
175 then_: ta,
176 else_: ea,
177 },
178 BvTerm::Ite {
179 cond: cb,
180 then_: tb,
181 else_: eb,
182 },
183 ) => ord_bool(ca, cb)
184 .then_with(|| ord_bv(ta, tb))
185 .then_with(|| ord_bv(ea, eb)),
186 _ => unreachable!("ord_bv: rank-equal variants are exhaustively matched"),
188 })
189}
190
191fn ord_bool(a: &BoolTerm, b: &BoolTerm) -> Ordering {
193 bool_rank(a).cmp(&bool_rank(b)).then_with(|| match (a, b) {
194 (BoolTerm::Eq(a1, a2), BoolTerm::Eq(b1, b2))
195 | (BoolTerm::Ne(a1, a2), BoolTerm::Ne(b1, b2))
196 | (BoolTerm::Ult(a1, a2), BoolTerm::Ult(b1, b2))
197 | (BoolTerm::Ule(a1, a2), BoolTerm::Ule(b1, b2))
198 | (BoolTerm::Ugt(a1, a2), BoolTerm::Ugt(b1, b2))
199 | (BoolTerm::Uge(a1, a2), BoolTerm::Uge(b1, b2))
200 | (BoolTerm::Slt(a1, a2), BoolTerm::Slt(b1, b2))
201 | (BoolTerm::Sle(a1, a2), BoolTerm::Sle(b1, b2))
202 | (BoolTerm::Sgt(a1, a2), BoolTerm::Sgt(b1, b2))
203 | (BoolTerm::Sge(a1, a2), BoolTerm::Sge(b1, b2)) => {
204 ord_bv(a1, b1).then_with(|| ord_bv(a2, b2))
205 }
206 (BoolTerm::Not(a1), BoolTerm::Not(b1)) => ord_bool(a1, b1),
207 (BoolTerm::And(a1, a2), BoolTerm::And(b1, b2))
208 | (BoolTerm::Or(a1, a2), BoolTerm::Or(b1, b2)) => {
209 ord_bool(a1, b1).then_with(|| ord_bool(a2, b2))
210 }
211 _ => unreachable!("ord_bool: rank-equal variants are exhaustively matched"),
212 })
213}
214
215fn canonical_pair(a: BvTerm, b: BvTerm) -> (Box<BvTerm>, Box<BvTerm>) {
218 if ord_bv(&a, &b) == Ordering::Greater {
219 (Box::new(b), Box::new(a))
220 } else {
221 (Box::new(a), Box::new(b))
222 }
223}
224
225fn canonicalize_bv(t: &BvTerm) -> BvTerm {
232 let bin = |a: &BvTerm, b: &BvTerm| (Box::new(canonicalize_bv(a)), Box::new(canonicalize_bv(b)));
233 let comm = |a: &BvTerm, b: &BvTerm| canonical_pair(canonicalize_bv(a), canonicalize_bv(b));
234 match t {
235 BvTerm::Const { .. } | BvTerm::Var { .. } => t.clone(),
236 BvTerm::Add(a, b) => {
237 let (a, b) = comm(a, b);
238 BvTerm::Add(a, b)
239 }
240 BvTerm::Mul(a, b) => {
241 let (a, b) = comm(a, b);
242 BvTerm::Mul(a, b)
243 }
244 BvTerm::And(a, b) => {
245 let (a, b) = comm(a, b);
246 BvTerm::And(a, b)
247 }
248 BvTerm::Or(a, b) => {
249 let (a, b) = comm(a, b);
250 BvTerm::Or(a, b)
251 }
252 BvTerm::Xor(a, b) => {
253 let (a, b) = comm(a, b);
254 BvTerm::Xor(a, b)
255 }
256 BvTerm::Sub(a, b) => {
257 let (a, b) = bin(a, b);
258 BvTerm::Sub(a, b)
259 }
260 BvTerm::Udiv(a, b) => {
261 let (a, b) = bin(a, b);
262 BvTerm::Udiv(a, b)
263 }
264 BvTerm::Urem(a, b) => {
265 let (a, b) = bin(a, b);
266 BvTerm::Urem(a, b)
267 }
268 BvTerm::Shl(a, b) => {
269 let (a, b) = bin(a, b);
270 BvTerm::Shl(a, b)
271 }
272 BvTerm::Lshr(a, b) => {
273 let (a, b) = bin(a, b);
274 BvTerm::Lshr(a, b)
275 }
276 BvTerm::Ashr(a, b) => {
277 let (a, b) = bin(a, b);
278 BvTerm::Ashr(a, b)
279 }
280 BvTerm::Rotr(a, b) => {
281 let (a, b) = bin(a, b);
282 BvTerm::Rotr(a, b)
283 }
284 BvTerm::Concat(a, b) => {
285 let (a, b) = bin(a, b);
286 BvTerm::Concat(a, b)
287 }
288 BvTerm::Extract { hi, lo, arg } => BvTerm::Extract {
289 hi: *hi,
290 lo: *lo,
291 arg: Box::new(canonicalize_bv(arg)),
292 },
293 BvTerm::ZeroExt { by, arg } => BvTerm::ZeroExt {
294 by: *by,
295 arg: Box::new(canonicalize_bv(arg)),
296 },
297 BvTerm::SignExt { by, arg } => BvTerm::SignExt {
298 by: *by,
299 arg: Box::new(canonicalize_bv(arg)),
300 },
301 BvTerm::Ite { cond, then_, else_ } => BvTerm::Ite {
302 cond: Box::new(canonicalize_bool(cond)),
303 then_: Box::new(canonicalize_bv(then_)),
304 else_: Box::new(canonicalize_bv(else_)),
305 },
306 other => panic!(
311 "synth-verify: unmodeled ordeal BvTerm variant {other:?} — \
312 extend term.rs + solver.rs before trusting any verdict"
313 ),
314 }
315}
316
317fn canonicalize_bool(t: &BoolTerm) -> BoolTerm {
318 let bin = |a: &BvTerm, b: &BvTerm| (Box::new(canonicalize_bv(a)), Box::new(canonicalize_bv(b)));
319 let comm = |a: &BvTerm, b: &BvTerm| canonical_pair(canonicalize_bv(a), canonicalize_bv(b));
320 match t {
321 BoolTerm::Eq(a, b) => {
322 let (a, b) = comm(a, b);
323 BoolTerm::Eq(a, b)
324 }
325 BoolTerm::Ne(a, b) => {
326 let (a, b) = comm(a, b);
327 BoolTerm::Ne(a, b)
328 }
329 BoolTerm::Ult(a, b) => {
330 let (a, b) = bin(a, b);
331 BoolTerm::Ult(a, b)
332 }
333 BoolTerm::Ule(a, b) => {
334 let (a, b) = bin(a, b);
335 BoolTerm::Ule(a, b)
336 }
337 BoolTerm::Ugt(a, b) => {
338 let (a, b) = bin(a, b);
339 BoolTerm::Ugt(a, b)
340 }
341 BoolTerm::Uge(a, b) => {
342 let (a, b) = bin(a, b);
343 BoolTerm::Uge(a, b)
344 }
345 BoolTerm::Slt(a, b) => {
346 let (a, b) = bin(a, b);
347 BoolTerm::Slt(a, b)
348 }
349 BoolTerm::Sle(a, b) => {
350 let (a, b) = bin(a, b);
351 BoolTerm::Sle(a, b)
352 }
353 BoolTerm::Sgt(a, b) => {
354 let (a, b) = bin(a, b);
355 BoolTerm::Sgt(a, b)
356 }
357 BoolTerm::Sge(a, b) => {
358 let (a, b) = bin(a, b);
359 BoolTerm::Sge(a, b)
360 }
361 BoolTerm::Not(a) => BoolTerm::Not(Box::new(canonicalize_bool(a))),
362 BoolTerm::And(a, b) => BoolTerm::And(
363 Box::new(canonicalize_bool(a)),
364 Box::new(canonicalize_bool(b)),
365 ),
366 BoolTerm::Or(a, b) => BoolTerm::Or(
367 Box::new(canonicalize_bool(a)),
368 Box::new(canonicalize_bool(b)),
369 ),
370 other => panic!(
372 "synth-verify: unmodeled ordeal BoolTerm variant {other:?} — \
373 extend term.rs + solver.rs before trusting any verdict"
374 ),
375 }
376}
377
378impl BV {
383 #[cfg_attr(not(feature = "z3-solver"), allow(dead_code))]
385 pub(crate) fn term(&self) -> &BvTerm {
386 &self.term
387 }
388
389 pub(crate) fn same_term(&self, other: &BV) -> bool {
396 self.width == other.width && ord_bv(&self.term, &other.term) == Ordering::Equal
397 }
398
399 pub(crate) fn var_name(&self) -> Option<&str> {
401 match &self.term {
402 BvTerm::Var { name, .. } => Some(name),
403 _ => None,
404 }
405 }
406
407 pub fn new_const(name: impl Into<String>, width: u32) -> Self {
409 Self {
410 term: BvTerm::Var {
411 name: name.into(),
412 sort: Sort::new(width),
413 },
414 width,
415 }
416 }
417
418 pub fn from_i64(value: i64, width: u32) -> Self {
420 Self {
421 term: BvTerm::Const {
422 value: (value as u64 as u128) & mask(width),
423 sort: Sort::new(width),
424 },
425 width,
426 }
427 }
428
429 pub fn from_u64(value: u64, width: u32) -> Self {
431 Self {
432 term: BvTerm::Const {
433 value: (value as u128) & mask(width),
434 sort: Sort::new(width),
435 },
436 width,
437 }
438 }
439
440 pub fn get_size(&self) -> u32 {
442 self.width
443 }
444
445 fn binop(
446 &self,
447 other: impl Borrow<BV>,
448 f: impl FnOnce(Box<BvTerm>, Box<BvTerm>) -> BvTerm,
449 ) -> BV {
450 let other = other.borrow();
451 BV {
452 term: f(Box::new(self.term.clone()), Box::new(other.term.clone())),
453 width: self.width,
454 }
455 }
456
457 fn commutative(
458 &self,
459 other: impl Borrow<BV>,
460 f: impl FnOnce(Box<BvTerm>, Box<BvTerm>) -> BvTerm,
461 ) -> BV {
462 let other = other.borrow();
463 let (a, b) = canonical_pair(self.term.clone(), other.term.clone());
464 BV {
465 term: f(a, b),
466 width: self.width,
467 }
468 }
469
470 pub fn bvadd(&self, other: impl Borrow<BV>) -> BV {
474 self.commutative(other, BvTerm::Add)
475 }
476
477 pub fn bvsub(&self, other: impl Borrow<BV>) -> BV {
479 self.binop(other, BvTerm::Sub)
480 }
481
482 pub fn bvmul(&self, other: impl Borrow<BV>) -> BV {
484 self.commutative(other, BvTerm::Mul)
485 }
486
487 pub fn bvudiv(&self, other: impl Borrow<BV>) -> BV {
489 self.binop(other, BvTerm::Udiv)
490 }
491
492 pub fn bvsdiv(&self, other: impl Borrow<BV>) -> BV {
494 BV {
495 term: canonicalize_bv(&ordeal::lowering::bvsdiv(
496 self.term.clone(),
497 other.borrow().term.clone(),
498 self.width,
499 )),
500 width: self.width,
501 }
502 }
503
504 pub fn bvurem(&self, other: impl Borrow<BV>) -> BV {
506 BV {
507 term: canonicalize_bv(&ordeal::lowering::bvurem(
508 self.term.clone(),
509 other.borrow().term.clone(),
510 self.width,
511 )),
512 width: self.width,
513 }
514 }
515
516 pub fn bvsrem(&self, other: impl Borrow<BV>) -> BV {
518 BV {
519 term: canonicalize_bv(&ordeal::lowering::bvsrem(
520 self.term.clone(),
521 other.borrow().term.clone(),
522 self.width,
523 )),
524 width: self.width,
525 }
526 }
527
528 pub fn bvneg(&self) -> BV {
530 BV {
531 term: canonicalize_bv(&ordeal::lowering::bvneg(self.term.clone(), self.width)),
532 width: self.width,
533 }
534 }
535
536 pub fn bvand(&self, other: impl Borrow<BV>) -> BV {
540 self.commutative(other, BvTerm::And)
541 }
542
543 pub fn bvor(&self, other: impl Borrow<BV>) -> BV {
545 self.commutative(other, BvTerm::Or)
546 }
547
548 pub fn bvxor(&self, other: impl Borrow<BV>) -> BV {
550 self.commutative(other, BvTerm::Xor)
551 }
552
553 pub fn bvnot(&self) -> BV {
555 BV {
556 term: canonicalize_bv(&ordeal::lowering::bvnot(self.term.clone(), self.width)),
557 width: self.width,
558 }
559 }
560
561 pub fn bvshl(&self, other: impl Borrow<BV>) -> BV {
565 self.binop(other, BvTerm::Shl)
566 }
567
568 pub fn bvlshr(&self, other: impl Borrow<BV>) -> BV {
570 self.binop(other, BvTerm::Lshr)
571 }
572
573 pub fn bvashr(&self, other: impl Borrow<BV>) -> BV {
575 self.binop(other, BvTerm::Ashr)
576 }
577
578 pub fn bvrotl(&self, other: impl Borrow<BV>) -> BV {
580 BV {
581 term: canonicalize_bv(&ordeal::lowering::bvrotl(
582 self.term.clone(),
583 other.borrow().term.clone(),
584 self.width,
585 )),
586 width: self.width,
587 }
588 }
589
590 pub fn bvrotr(&self, other: impl Borrow<BV>) -> BV {
592 self.binop(other, BvTerm::Rotr)
593 }
594
595 pub fn extract(&self, hi: u32, lo: u32) -> BV {
599 BV {
600 term: BvTerm::Extract {
601 hi,
602 lo,
603 arg: Box::new(self.term.clone()),
604 },
605 width: hi - lo + 1,
606 }
607 }
608
609 pub fn concat(&self, other: impl Borrow<BV>) -> BV {
611 let other = other.borrow();
612 BV {
613 term: BvTerm::Concat(Box::new(self.term.clone()), Box::new(other.term.clone())),
614 width: self.width + other.width,
615 }
616 }
617
618 pub fn zero_ext(&self, by: u32) -> BV {
620 BV {
621 term: BvTerm::ZeroExt {
622 by,
623 arg: Box::new(self.term.clone()),
624 },
625 width: self.width + by,
626 }
627 }
628
629 pub fn sign_ext(&self, by: u32) -> BV {
631 BV {
632 term: BvTerm::SignExt {
633 by,
634 arg: Box::new(self.term.clone()),
635 },
636 width: self.width + by,
637 }
638 }
639
640 fn cmp_op(
643 &self,
644 other: impl Borrow<BV>,
645 f: impl FnOnce(Box<BvTerm>, Box<BvTerm>) -> BoolTerm,
646 ) -> Bool {
647 Bool {
648 term: f(
649 Box::new(self.term.clone()),
650 Box::new(other.borrow().term.clone()),
651 ),
652 }
653 }
654
655 pub fn eq(&self, other: impl Borrow<BV>) -> Bool {
657 let (a, b) = canonical_pair(self.term.clone(), other.borrow().term.clone());
658 Bool {
659 term: BoolTerm::Eq(a, b),
660 }
661 }
662
663 pub fn ne(&self, other: impl Borrow<BV>) -> Bool {
665 let (a, b) = canonical_pair(self.term.clone(), other.borrow().term.clone());
666 Bool {
667 term: BoolTerm::Ne(a, b),
668 }
669 }
670
671 pub fn bvult(&self, other: impl Borrow<BV>) -> Bool {
673 self.cmp_op(other, BoolTerm::Ult)
674 }
675
676 pub fn bvule(&self, other: impl Borrow<BV>) -> Bool {
678 self.cmp_op(other, BoolTerm::Ule)
679 }
680
681 pub fn bvugt(&self, other: impl Borrow<BV>) -> Bool {
683 self.cmp_op(other, BoolTerm::Ugt)
684 }
685
686 pub fn bvuge(&self, other: impl Borrow<BV>) -> Bool {
688 self.cmp_op(other, BoolTerm::Uge)
689 }
690
691 pub fn bvslt(&self, other: impl Borrow<BV>) -> Bool {
693 self.cmp_op(other, BoolTerm::Slt)
694 }
695
696 pub fn bvsle(&self, other: impl Borrow<BV>) -> Bool {
698 self.cmp_op(other, BoolTerm::Sle)
699 }
700
701 pub fn bvsgt(&self, other: impl Borrow<BV>) -> Bool {
703 self.cmp_op(other, BoolTerm::Sgt)
704 }
705
706 pub fn bvsge(&self, other: impl Borrow<BV>) -> Bool {
708 self.cmp_op(other, BoolTerm::Sge)
709 }
710
711 pub fn simplify(&self) -> BV {
717 self.clone()
718 }
719
720 pub fn as_i64(&self) -> Option<i64> {
723 self.eval_closed().and_then(|v| i64::try_from(v).ok())
724 }
725
726 pub fn as_u64(&self) -> Option<u64> {
728 self.eval_closed().and_then(|v| u64::try_from(v).ok())
729 }
730
731 fn eval_closed(&self) -> Option<u128> {
732 ordeal::eval::eval_bv(&self.term, &ordeal::eval::Env::new()).ok()
733 }
734}
735
736impl Bool {
741 pub(crate) fn term(&self) -> &BoolTerm {
743 &self.term
744 }
745
746 pub(crate) fn same_term(&self, other: &Bool) -> bool {
748 ord_bool(&self.term, &other.term) == Ordering::Equal
749 }
750
751 pub(crate) fn from_ordeal(term: BoolTerm) -> Bool {
755 Bool { term }
756 }
757
758 pub fn new_const(name: impl Into<String>) -> Self {
763 let var = BvTerm::Var {
764 name: name.into(),
765 sort: Sort::new(1),
766 };
767 Self {
768 term: BoolTerm::Ne(
769 Box::new(var),
770 Box::new(BvTerm::Const {
771 value: 0,
772 sort: Sort::new(1),
773 }),
774 ),
775 }
776 }
777
778 pub fn from_bool(value: bool) -> Self {
780 Self::literal(value)
781 }
782
783 pub fn eq(&self, other: impl Borrow<Bool>) -> Bool {
786 let a = self.term.clone();
787 let b = other.borrow().term.clone();
788 Bool {
789 term: BoolTerm::Or(
790 Box::new(BoolTerm::And(Box::new(a.clone()), Box::new(b.clone()))),
791 Box::new(BoolTerm::And(
792 Box::new(BoolTerm::Not(Box::new(a))),
793 Box::new(BoolTerm::Not(Box::new(b))),
794 )),
795 ),
796 }
797 }
798
799 pub fn not(&self) -> Bool {
801 Bool {
802 term: BoolTerm::Not(Box::new(self.term.clone())),
803 }
804 }
805
806 pub fn and(values: &[&Bool]) -> Bool {
808 Self::fold(values, BoolTerm::And, true)
809 }
810
811 pub fn or(values: &[&Bool]) -> Bool {
813 Self::fold(values, BoolTerm::Or, false)
814 }
815
816 fn fold(
817 values: &[&Bool],
818 f: impl Fn(Box<BoolTerm>, Box<BoolTerm>) -> BoolTerm,
819 empty: bool,
820 ) -> Bool {
821 let mut iter = values.iter();
822 let Some(first) = iter.next() else {
823 return Self::literal(empty);
824 };
825 let mut acc = first.term.clone();
826 for v in iter {
827 acc = f(Box::new(acc), Box::new(v.term.clone()));
828 }
829 Bool { term: acc }
830 }
831
832 fn literal(value: bool) -> Bool {
835 let zero = || {
836 Box::new(BvTerm::Const {
837 value: 0,
838 sort: Sort::new(1),
839 })
840 };
841 Bool {
842 term: if value {
843 BoolTerm::Eq(zero(), zero())
844 } else {
845 BoolTerm::Ne(zero(), zero())
846 },
847 }
848 }
849
850 pub fn ite(&self, then_: impl Borrow<BV>, else_: impl Borrow<BV>) -> BV {
853 let then_ = then_.borrow();
854 let else_ = else_.borrow();
855 BV {
856 term: BvTerm::Ite {
857 cond: Box::new(self.term.clone()),
858 then_: Box::new(then_.term.clone()),
859 else_: Box::new(else_.term.clone()),
860 },
861 width: then_.width,
862 }
863 }
864
865 pub fn simplify(&self) -> Bool {
868 self.clone()
869 }
870
871 pub fn as_bool(&self) -> Option<bool> {
873 ordeal::eval::eval_bool(&self.term, &ordeal::eval::Env::new()).ok()
874 }
875}
876
877fn fmt_bv(t: &BvTerm, f: &mut fmt::Formatter<'_>) -> fmt::Result {
882 match t {
883 BvTerm::Const { value, sort } => write!(f, "(_ bv{} {})", value, sort.width),
884 BvTerm::Var { name, .. } => write!(f, "{}", name),
885 BvTerm::Add(a, b) => fmt_bin(f, "bvadd", a, b),
886 BvTerm::Sub(a, b) => fmt_bin(f, "bvsub", a, b),
887 BvTerm::Mul(a, b) => fmt_bin(f, "bvmul", a, b),
888 BvTerm::Udiv(a, b) => fmt_bin(f, "bvudiv", a, b),
889 BvTerm::Urem(a, b) => fmt_bin(f, "bvurem", a, b),
890 BvTerm::And(a, b) => fmt_bin(f, "bvand", a, b),
891 BvTerm::Or(a, b) => fmt_bin(f, "bvor", a, b),
892 BvTerm::Xor(a, b) => fmt_bin(f, "bvxor", a, b),
893 BvTerm::Shl(a, b) => fmt_bin(f, "bvshl", a, b),
894 BvTerm::Lshr(a, b) => fmt_bin(f, "bvlshr", a, b),
895 BvTerm::Ashr(a, b) => fmt_bin(f, "bvashr", a, b),
896 BvTerm::Rotr(a, b) => fmt_bin(f, "bvrotr", a, b),
897 BvTerm::Extract { hi, lo, arg } => {
898 write!(f, "((_ extract {} {}) ", hi, lo)?;
899 fmt_bv(arg, f)?;
900 write!(f, ")")
901 }
902 BvTerm::Concat(a, b) => fmt_bin(f, "concat", a, b),
903 BvTerm::ZeroExt { by, arg } => {
904 write!(f, "((_ zero_extend {}) ", by)?;
905 fmt_bv(arg, f)?;
906 write!(f, ")")
907 }
908 BvTerm::SignExt { by, arg } => {
909 write!(f, "((_ sign_extend {}) ", by)?;
910 fmt_bv(arg, f)?;
911 write!(f, ")")
912 }
913 BvTerm::Ite { cond, then_, else_ } => {
914 write!(f, "(ite ")?;
915 fmt_bool(cond, f)?;
916 write!(f, " ")?;
917 fmt_bv(then_, f)?;
918 write!(f, " ")?;
919 fmt_bv(else_, f)?;
920 write!(f, ")")
921 }
922 _ => write!(f, "<unmodeled-ordeal-bv-term>"),
927 }
928}
929
930fn fmt_bin(f: &mut fmt::Formatter<'_>, op: &str, a: &BvTerm, b: &BvTerm) -> fmt::Result {
931 write!(f, "({} ", op)?;
932 fmt_bv(a, f)?;
933 write!(f, " ")?;
934 fmt_bv(b, f)?;
935 write!(f, ")")
936}
937
938fn fmt_bool(t: &BoolTerm, f: &mut fmt::Formatter<'_>) -> fmt::Result {
939 let bin = |f: &mut fmt::Formatter<'_>, op: &str, a: &BvTerm, b: &BvTerm| -> fmt::Result {
940 write!(f, "({} ", op)?;
941 fmt_bv(a, f)?;
942 write!(f, " ")?;
943 fmt_bv(b, f)?;
944 write!(f, ")")
945 };
946 match t {
947 BoolTerm::Eq(a, b) => bin(f, "=", a, b),
948 BoolTerm::Ne(a, b) => bin(f, "distinct", a, b),
949 BoolTerm::Ult(a, b) => bin(f, "bvult", a, b),
950 BoolTerm::Ule(a, b) => bin(f, "bvule", a, b),
951 BoolTerm::Ugt(a, b) => bin(f, "bvugt", a, b),
952 BoolTerm::Uge(a, b) => bin(f, "bvuge", a, b),
953 BoolTerm::Slt(a, b) => bin(f, "bvslt", a, b),
954 BoolTerm::Sle(a, b) => bin(f, "bvsle", a, b),
955 BoolTerm::Sgt(a, b) => bin(f, "bvsgt", a, b),
956 BoolTerm::Sge(a, b) => bin(f, "bvsge", a, b),
957 BoolTerm::Not(a) => {
958 write!(f, "(not ")?;
959 fmt_bool(a, f)?;
960 write!(f, ")")
961 }
962 BoolTerm::And(a, b) => {
963 write!(f, "(and ")?;
964 fmt_bool(a, f)?;
965 write!(f, " ")?;
966 fmt_bool(b, f)?;
967 write!(f, ")")
968 }
969 BoolTerm::Or(a, b) => {
970 write!(f, "(or ")?;
971 fmt_bool(a, f)?;
972 write!(f, " ")?;
973 fmt_bool(b, f)?;
974 write!(f, ")")
975 }
976 _ => write!(f, "<unmodeled-ordeal-bool-term>"),
978 }
979}
980
981impl fmt::Display for BV {
982 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
983 fmt_bv(&self.term, f)
984 }
985}
986
987impl fmt::Display for Bool {
988 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
989 fmt_bool(&self.term, f)
990 }
991}
992
993#[cfg(test)]
994mod tests {
995 use super::*;
996
997 #[test]
998 fn const_folding_matches_z3_idiom() {
999 let a = BV::from_i64(40, 32);
1000 let b = BV::from_i64(2, 32);
1001 assert_eq!(a.bvadd(&b).simplify().as_i64(), Some(42));
1002 assert_eq!(a.bvsub(&b).simplify().as_i64(), Some(38));
1003 assert_eq!(BV::from_i64(-1, 32).as_i64(), Some(0xFFFF_FFFF));
1005 }
1006
1007 #[test]
1008 fn commutative_construction_is_canonical() {
1009 let x = BV::new_const("x", 32);
1010 let y = BV::new_const("y", 32);
1011 assert_eq!(x.bvmul(&y).to_string(), y.bvmul(&x).to_string());
1013 assert_eq!(x.bvadd(&y).to_string(), y.bvadd(&x).to_string());
1014 assert_eq!(x.eq(&y).to_string(), y.eq(&x).to_string());
1015 assert_ne!(x.bvsub(&y).to_string(), y.bvsub(&x).to_string());
1017 }
1018
1019 #[test]
1020 fn display_uses_smtlib_mnemonics() {
1021 let x = BV::new_const("x", 32);
1022 let s = x.bvadd(BV::from_i64(1, 32)).to_string();
1023 assert!(s.contains("bvadd"), "{s}");
1024 }
1025}