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::And(..) => 6,
68 BvTerm::Or(..) => 7,
69 BvTerm::Xor(..) => 8,
70 BvTerm::Shl(..) => 9,
71 BvTerm::Lshr(..) => 10,
72 BvTerm::Ashr(..) => 11,
73 BvTerm::Rotr(..) => 12,
74 BvTerm::Extract { .. } => 13,
75 BvTerm::Concat(..) => 14,
76 BvTerm::ZeroExt { .. } => 15,
77 BvTerm::SignExt { .. } => 16,
78 BvTerm::Ite { .. } => 17,
79 }
80}
81
82fn bool_rank(t: &BoolTerm) -> u8 {
83 match t {
84 BoolTerm::Eq(..) => 0,
85 BoolTerm::Ne(..) => 1,
86 BoolTerm::Ult(..) => 2,
87 BoolTerm::Ule(..) => 3,
88 BoolTerm::Ugt(..) => 4,
89 BoolTerm::Uge(..) => 5,
90 BoolTerm::Slt(..) => 6,
91 BoolTerm::Sle(..) => 7,
92 BoolTerm::Sgt(..) => 8,
93 BoolTerm::Sge(..) => 9,
94 BoolTerm::Not(..) => 10,
95 BoolTerm::And(..) => 11,
96 BoolTerm::Or(..) => 12,
97 }
98}
99
100fn ord_bv(a: &BvTerm, b: &BvTerm) -> Ordering {
102 bv_rank(a).cmp(&bv_rank(b)).then_with(|| match (a, b) {
103 (
104 BvTerm::Const {
105 value: va,
106 sort: sa,
107 },
108 BvTerm::Const {
109 value: vb,
110 sort: sb,
111 },
112 ) => sa.width.cmp(&sb.width).then(va.cmp(vb)),
113 (BvTerm::Var { name: na, sort: sa }, BvTerm::Var { name: nb, sort: sb }) => {
114 sa.width.cmp(&sb.width).then_with(|| na.cmp(nb))
115 }
116 (BvTerm::Add(a1, a2), BvTerm::Add(b1, b2))
117 | (BvTerm::Sub(a1, a2), BvTerm::Sub(b1, b2))
118 | (BvTerm::Mul(a1, a2), BvTerm::Mul(b1, b2))
119 | (BvTerm::Udiv(a1, a2), BvTerm::Udiv(b1, b2))
120 | (BvTerm::And(a1, a2), BvTerm::And(b1, b2))
121 | (BvTerm::Or(a1, a2), BvTerm::Or(b1, b2))
122 | (BvTerm::Xor(a1, a2), BvTerm::Xor(b1, b2))
123 | (BvTerm::Shl(a1, a2), BvTerm::Shl(b1, b2))
124 | (BvTerm::Lshr(a1, a2), BvTerm::Lshr(b1, b2))
125 | (BvTerm::Ashr(a1, a2), BvTerm::Ashr(b1, b2))
126 | (BvTerm::Rotr(a1, a2), BvTerm::Rotr(b1, b2))
127 | (BvTerm::Concat(a1, a2), BvTerm::Concat(b1, b2)) => {
128 ord_bv(a1, b1).then_with(|| ord_bv(a2, b2))
129 }
130 (
131 BvTerm::Extract {
132 hi: ha,
133 lo: la,
134 arg: aa,
135 },
136 BvTerm::Extract {
137 hi: hb,
138 lo: lb,
139 arg: ab,
140 },
141 ) => ha.cmp(hb).then(la.cmp(lb)).then_with(|| ord_bv(aa, ab)),
142 (BvTerm::ZeroExt { by: ba, arg: aa }, BvTerm::ZeroExt { by: bb, arg: ab })
143 | (BvTerm::SignExt { by: ba, arg: aa }, BvTerm::SignExt { by: bb, arg: ab }) => {
144 ba.cmp(bb).then_with(|| ord_bv(aa, ab))
145 }
146 (
147 BvTerm::Ite {
148 cond: ca,
149 then_: ta,
150 else_: ea,
151 },
152 BvTerm::Ite {
153 cond: cb,
154 then_: tb,
155 else_: eb,
156 },
157 ) => ord_bool(ca, cb)
158 .then_with(|| ord_bv(ta, tb))
159 .then_with(|| ord_bv(ea, eb)),
160 _ => unreachable!("ord_bv: rank-equal variants are exhaustively matched"),
162 })
163}
164
165fn ord_bool(a: &BoolTerm, b: &BoolTerm) -> Ordering {
167 bool_rank(a).cmp(&bool_rank(b)).then_with(|| match (a, b) {
168 (BoolTerm::Eq(a1, a2), BoolTerm::Eq(b1, b2))
169 | (BoolTerm::Ne(a1, a2), BoolTerm::Ne(b1, b2))
170 | (BoolTerm::Ult(a1, a2), BoolTerm::Ult(b1, b2))
171 | (BoolTerm::Ule(a1, a2), BoolTerm::Ule(b1, b2))
172 | (BoolTerm::Ugt(a1, a2), BoolTerm::Ugt(b1, b2))
173 | (BoolTerm::Uge(a1, a2), BoolTerm::Uge(b1, b2))
174 | (BoolTerm::Slt(a1, a2), BoolTerm::Slt(b1, b2))
175 | (BoolTerm::Sle(a1, a2), BoolTerm::Sle(b1, b2))
176 | (BoolTerm::Sgt(a1, a2), BoolTerm::Sgt(b1, b2))
177 | (BoolTerm::Sge(a1, a2), BoolTerm::Sge(b1, b2)) => {
178 ord_bv(a1, b1).then_with(|| ord_bv(a2, b2))
179 }
180 (BoolTerm::Not(a1), BoolTerm::Not(b1)) => ord_bool(a1, b1),
181 (BoolTerm::And(a1, a2), BoolTerm::And(b1, b2))
182 | (BoolTerm::Or(a1, a2), BoolTerm::Or(b1, b2)) => {
183 ord_bool(a1, b1).then_with(|| ord_bool(a2, b2))
184 }
185 _ => unreachable!("ord_bool: rank-equal variants are exhaustively matched"),
186 })
187}
188
189fn canonical_pair(a: BvTerm, b: BvTerm) -> (Box<BvTerm>, Box<BvTerm>) {
192 if ord_bv(&a, &b) == Ordering::Greater {
193 (Box::new(b), Box::new(a))
194 } else {
195 (Box::new(a), Box::new(b))
196 }
197}
198
199fn canonicalize_bv(t: &BvTerm) -> BvTerm {
206 let bin = |a: &BvTerm, b: &BvTerm| (Box::new(canonicalize_bv(a)), Box::new(canonicalize_bv(b)));
207 let comm = |a: &BvTerm, b: &BvTerm| canonical_pair(canonicalize_bv(a), canonicalize_bv(b));
208 match t {
209 BvTerm::Const { .. } | BvTerm::Var { .. } => t.clone(),
210 BvTerm::Add(a, b) => {
211 let (a, b) = comm(a, b);
212 BvTerm::Add(a, b)
213 }
214 BvTerm::Mul(a, b) => {
215 let (a, b) = comm(a, b);
216 BvTerm::Mul(a, b)
217 }
218 BvTerm::And(a, b) => {
219 let (a, b) = comm(a, b);
220 BvTerm::And(a, b)
221 }
222 BvTerm::Or(a, b) => {
223 let (a, b) = comm(a, b);
224 BvTerm::Or(a, b)
225 }
226 BvTerm::Xor(a, b) => {
227 let (a, b) = comm(a, b);
228 BvTerm::Xor(a, b)
229 }
230 BvTerm::Sub(a, b) => {
231 let (a, b) = bin(a, b);
232 BvTerm::Sub(a, b)
233 }
234 BvTerm::Udiv(a, b) => {
235 let (a, b) = bin(a, b);
236 BvTerm::Udiv(a, b)
237 }
238 BvTerm::Shl(a, b) => {
239 let (a, b) = bin(a, b);
240 BvTerm::Shl(a, b)
241 }
242 BvTerm::Lshr(a, b) => {
243 let (a, b) = bin(a, b);
244 BvTerm::Lshr(a, b)
245 }
246 BvTerm::Ashr(a, b) => {
247 let (a, b) = bin(a, b);
248 BvTerm::Ashr(a, b)
249 }
250 BvTerm::Rotr(a, b) => {
251 let (a, b) = bin(a, b);
252 BvTerm::Rotr(a, b)
253 }
254 BvTerm::Concat(a, b) => {
255 let (a, b) = bin(a, b);
256 BvTerm::Concat(a, b)
257 }
258 BvTerm::Extract { hi, lo, arg } => BvTerm::Extract {
259 hi: *hi,
260 lo: *lo,
261 arg: Box::new(canonicalize_bv(arg)),
262 },
263 BvTerm::ZeroExt { by, arg } => BvTerm::ZeroExt {
264 by: *by,
265 arg: Box::new(canonicalize_bv(arg)),
266 },
267 BvTerm::SignExt { by, arg } => BvTerm::SignExt {
268 by: *by,
269 arg: Box::new(canonicalize_bv(arg)),
270 },
271 BvTerm::Ite { cond, then_, else_ } => BvTerm::Ite {
272 cond: Box::new(canonicalize_bool(cond)),
273 then_: Box::new(canonicalize_bv(then_)),
274 else_: Box::new(canonicalize_bv(else_)),
275 },
276 }
277}
278
279fn canonicalize_bool(t: &BoolTerm) -> BoolTerm {
280 let bin = |a: &BvTerm, b: &BvTerm| (Box::new(canonicalize_bv(a)), Box::new(canonicalize_bv(b)));
281 let comm = |a: &BvTerm, b: &BvTerm| canonical_pair(canonicalize_bv(a), canonicalize_bv(b));
282 match t {
283 BoolTerm::Eq(a, b) => {
284 let (a, b) = comm(a, b);
285 BoolTerm::Eq(a, b)
286 }
287 BoolTerm::Ne(a, b) => {
288 let (a, b) = comm(a, b);
289 BoolTerm::Ne(a, b)
290 }
291 BoolTerm::Ult(a, b) => {
292 let (a, b) = bin(a, b);
293 BoolTerm::Ult(a, b)
294 }
295 BoolTerm::Ule(a, b) => {
296 let (a, b) = bin(a, b);
297 BoolTerm::Ule(a, b)
298 }
299 BoolTerm::Ugt(a, b) => {
300 let (a, b) = bin(a, b);
301 BoolTerm::Ugt(a, b)
302 }
303 BoolTerm::Uge(a, b) => {
304 let (a, b) = bin(a, b);
305 BoolTerm::Uge(a, b)
306 }
307 BoolTerm::Slt(a, b) => {
308 let (a, b) = bin(a, b);
309 BoolTerm::Slt(a, b)
310 }
311 BoolTerm::Sle(a, b) => {
312 let (a, b) = bin(a, b);
313 BoolTerm::Sle(a, b)
314 }
315 BoolTerm::Sgt(a, b) => {
316 let (a, b) = bin(a, b);
317 BoolTerm::Sgt(a, b)
318 }
319 BoolTerm::Sge(a, b) => {
320 let (a, b) = bin(a, b);
321 BoolTerm::Sge(a, b)
322 }
323 BoolTerm::Not(a) => BoolTerm::Not(Box::new(canonicalize_bool(a))),
324 BoolTerm::And(a, b) => BoolTerm::And(
325 Box::new(canonicalize_bool(a)),
326 Box::new(canonicalize_bool(b)),
327 ),
328 BoolTerm::Or(a, b) => BoolTerm::Or(
329 Box::new(canonicalize_bool(a)),
330 Box::new(canonicalize_bool(b)),
331 ),
332 }
333}
334
335impl BV {
340 #[cfg_attr(not(feature = "z3-solver"), allow(dead_code))]
342 pub(crate) fn term(&self) -> &BvTerm {
343 &self.term
344 }
345
346 pub(crate) fn same_term(&self, other: &BV) -> bool {
353 self.width == other.width && ord_bv(&self.term, &other.term) == Ordering::Equal
354 }
355
356 pub(crate) fn var_name(&self) -> Option<&str> {
358 match &self.term {
359 BvTerm::Var { name, .. } => Some(name),
360 _ => None,
361 }
362 }
363
364 pub fn new_const(name: impl Into<String>, width: u32) -> Self {
366 Self {
367 term: BvTerm::Var {
368 name: name.into(),
369 sort: Sort::new(width),
370 },
371 width,
372 }
373 }
374
375 pub fn from_i64(value: i64, width: u32) -> Self {
377 Self {
378 term: BvTerm::Const {
379 value: (value as u64 as u128) & mask(width),
380 sort: Sort::new(width),
381 },
382 width,
383 }
384 }
385
386 pub fn from_u64(value: u64, width: u32) -> Self {
388 Self {
389 term: BvTerm::Const {
390 value: (value as u128) & mask(width),
391 sort: Sort::new(width),
392 },
393 width,
394 }
395 }
396
397 pub fn get_size(&self) -> u32 {
399 self.width
400 }
401
402 fn binop(
403 &self,
404 other: impl Borrow<BV>,
405 f: impl FnOnce(Box<BvTerm>, Box<BvTerm>) -> BvTerm,
406 ) -> BV {
407 let other = other.borrow();
408 BV {
409 term: f(Box::new(self.term.clone()), Box::new(other.term.clone())),
410 width: self.width,
411 }
412 }
413
414 fn commutative(
415 &self,
416 other: impl Borrow<BV>,
417 f: impl FnOnce(Box<BvTerm>, Box<BvTerm>) -> BvTerm,
418 ) -> BV {
419 let other = other.borrow();
420 let (a, b) = canonical_pair(self.term.clone(), other.term.clone());
421 BV {
422 term: f(a, b),
423 width: self.width,
424 }
425 }
426
427 pub fn bvadd(&self, other: impl Borrow<BV>) -> BV {
431 self.commutative(other, BvTerm::Add)
432 }
433
434 pub fn bvsub(&self, other: impl Borrow<BV>) -> BV {
436 self.binop(other, BvTerm::Sub)
437 }
438
439 pub fn bvmul(&self, other: impl Borrow<BV>) -> BV {
441 self.commutative(other, BvTerm::Mul)
442 }
443
444 pub fn bvudiv(&self, other: impl Borrow<BV>) -> BV {
446 self.binop(other, BvTerm::Udiv)
447 }
448
449 pub fn bvsdiv(&self, other: impl Borrow<BV>) -> BV {
451 BV {
452 term: canonicalize_bv(&ordeal::lowering::bvsdiv(
453 self.term.clone(),
454 other.borrow().term.clone(),
455 self.width,
456 )),
457 width: self.width,
458 }
459 }
460
461 pub fn bvurem(&self, other: impl Borrow<BV>) -> BV {
463 BV {
464 term: canonicalize_bv(&ordeal::lowering::bvurem(
465 self.term.clone(),
466 other.borrow().term.clone(),
467 self.width,
468 )),
469 width: self.width,
470 }
471 }
472
473 pub fn bvsrem(&self, other: impl Borrow<BV>) -> BV {
475 BV {
476 term: canonicalize_bv(&ordeal::lowering::bvsrem(
477 self.term.clone(),
478 other.borrow().term.clone(),
479 self.width,
480 )),
481 width: self.width,
482 }
483 }
484
485 pub fn bvneg(&self) -> BV {
487 BV {
488 term: canonicalize_bv(&ordeal::lowering::bvneg(self.term.clone(), self.width)),
489 width: self.width,
490 }
491 }
492
493 pub fn bvand(&self, other: impl Borrow<BV>) -> BV {
497 self.commutative(other, BvTerm::And)
498 }
499
500 pub fn bvor(&self, other: impl Borrow<BV>) -> BV {
502 self.commutative(other, BvTerm::Or)
503 }
504
505 pub fn bvxor(&self, other: impl Borrow<BV>) -> BV {
507 self.commutative(other, BvTerm::Xor)
508 }
509
510 pub fn bvnot(&self) -> BV {
512 BV {
513 term: canonicalize_bv(&ordeal::lowering::bvnot(self.term.clone(), self.width)),
514 width: self.width,
515 }
516 }
517
518 pub fn bvshl(&self, other: impl Borrow<BV>) -> BV {
522 self.binop(other, BvTerm::Shl)
523 }
524
525 pub fn bvlshr(&self, other: impl Borrow<BV>) -> BV {
527 self.binop(other, BvTerm::Lshr)
528 }
529
530 pub fn bvashr(&self, other: impl Borrow<BV>) -> BV {
532 self.binop(other, BvTerm::Ashr)
533 }
534
535 pub fn bvrotl(&self, other: impl Borrow<BV>) -> BV {
537 BV {
538 term: canonicalize_bv(&ordeal::lowering::bvrotl(
539 self.term.clone(),
540 other.borrow().term.clone(),
541 self.width,
542 )),
543 width: self.width,
544 }
545 }
546
547 pub fn bvrotr(&self, other: impl Borrow<BV>) -> BV {
549 self.binop(other, BvTerm::Rotr)
550 }
551
552 pub fn extract(&self, hi: u32, lo: u32) -> BV {
556 BV {
557 term: BvTerm::Extract {
558 hi,
559 lo,
560 arg: Box::new(self.term.clone()),
561 },
562 width: hi - lo + 1,
563 }
564 }
565
566 pub fn concat(&self, other: impl Borrow<BV>) -> BV {
568 let other = other.borrow();
569 BV {
570 term: BvTerm::Concat(Box::new(self.term.clone()), Box::new(other.term.clone())),
571 width: self.width + other.width,
572 }
573 }
574
575 pub fn zero_ext(&self, by: u32) -> BV {
577 BV {
578 term: BvTerm::ZeroExt {
579 by,
580 arg: Box::new(self.term.clone()),
581 },
582 width: self.width + by,
583 }
584 }
585
586 pub fn sign_ext(&self, by: u32) -> BV {
588 BV {
589 term: BvTerm::SignExt {
590 by,
591 arg: Box::new(self.term.clone()),
592 },
593 width: self.width + by,
594 }
595 }
596
597 fn cmp_op(
600 &self,
601 other: impl Borrow<BV>,
602 f: impl FnOnce(Box<BvTerm>, Box<BvTerm>) -> BoolTerm,
603 ) -> Bool {
604 Bool {
605 term: f(
606 Box::new(self.term.clone()),
607 Box::new(other.borrow().term.clone()),
608 ),
609 }
610 }
611
612 pub fn eq(&self, other: impl Borrow<BV>) -> Bool {
614 let (a, b) = canonical_pair(self.term.clone(), other.borrow().term.clone());
615 Bool {
616 term: BoolTerm::Eq(a, b),
617 }
618 }
619
620 pub fn ne(&self, other: impl Borrow<BV>) -> Bool {
622 let (a, b) = canonical_pair(self.term.clone(), other.borrow().term.clone());
623 Bool {
624 term: BoolTerm::Ne(a, b),
625 }
626 }
627
628 pub fn bvult(&self, other: impl Borrow<BV>) -> Bool {
630 self.cmp_op(other, BoolTerm::Ult)
631 }
632
633 pub fn bvule(&self, other: impl Borrow<BV>) -> Bool {
635 self.cmp_op(other, BoolTerm::Ule)
636 }
637
638 pub fn bvugt(&self, other: impl Borrow<BV>) -> Bool {
640 self.cmp_op(other, BoolTerm::Ugt)
641 }
642
643 pub fn bvuge(&self, other: impl Borrow<BV>) -> Bool {
645 self.cmp_op(other, BoolTerm::Uge)
646 }
647
648 pub fn bvslt(&self, other: impl Borrow<BV>) -> Bool {
650 self.cmp_op(other, BoolTerm::Slt)
651 }
652
653 pub fn bvsle(&self, other: impl Borrow<BV>) -> Bool {
655 self.cmp_op(other, BoolTerm::Sle)
656 }
657
658 pub fn bvsgt(&self, other: impl Borrow<BV>) -> Bool {
660 self.cmp_op(other, BoolTerm::Sgt)
661 }
662
663 pub fn bvsge(&self, other: impl Borrow<BV>) -> Bool {
665 self.cmp_op(other, BoolTerm::Sge)
666 }
667
668 pub fn simplify(&self) -> BV {
674 self.clone()
675 }
676
677 pub fn as_i64(&self) -> Option<i64> {
680 self.eval_closed().and_then(|v| i64::try_from(v).ok())
681 }
682
683 pub fn as_u64(&self) -> Option<u64> {
685 self.eval_closed().and_then(|v| u64::try_from(v).ok())
686 }
687
688 fn eval_closed(&self) -> Option<u128> {
689 ordeal::eval::eval_bv(&self.term, &ordeal::eval::Env::new()).ok()
690 }
691}
692
693impl Bool {
698 pub(crate) fn term(&self) -> &BoolTerm {
700 &self.term
701 }
702
703 pub(crate) fn same_term(&self, other: &Bool) -> bool {
705 ord_bool(&self.term, &other.term) == Ordering::Equal
706 }
707
708 pub(crate) fn from_ordeal(term: BoolTerm) -> Bool {
712 Bool { term }
713 }
714
715 pub fn new_const(name: impl Into<String>) -> Self {
720 let var = BvTerm::Var {
721 name: name.into(),
722 sort: Sort::new(1),
723 };
724 Self {
725 term: BoolTerm::Ne(
726 Box::new(var),
727 Box::new(BvTerm::Const {
728 value: 0,
729 sort: Sort::new(1),
730 }),
731 ),
732 }
733 }
734
735 pub fn from_bool(value: bool) -> Self {
737 Self::literal(value)
738 }
739
740 pub fn eq(&self, other: impl Borrow<Bool>) -> Bool {
743 let a = self.term.clone();
744 let b = other.borrow().term.clone();
745 Bool {
746 term: BoolTerm::Or(
747 Box::new(BoolTerm::And(Box::new(a.clone()), Box::new(b.clone()))),
748 Box::new(BoolTerm::And(
749 Box::new(BoolTerm::Not(Box::new(a))),
750 Box::new(BoolTerm::Not(Box::new(b))),
751 )),
752 ),
753 }
754 }
755
756 pub fn not(&self) -> Bool {
758 Bool {
759 term: BoolTerm::Not(Box::new(self.term.clone())),
760 }
761 }
762
763 pub fn and(values: &[&Bool]) -> Bool {
765 Self::fold(values, BoolTerm::And, true)
766 }
767
768 pub fn or(values: &[&Bool]) -> Bool {
770 Self::fold(values, BoolTerm::Or, false)
771 }
772
773 fn fold(
774 values: &[&Bool],
775 f: impl Fn(Box<BoolTerm>, Box<BoolTerm>) -> BoolTerm,
776 empty: bool,
777 ) -> Bool {
778 let mut iter = values.iter();
779 let Some(first) = iter.next() else {
780 return Self::literal(empty);
781 };
782 let mut acc = first.term.clone();
783 for v in iter {
784 acc = f(Box::new(acc), Box::new(v.term.clone()));
785 }
786 Bool { term: acc }
787 }
788
789 fn literal(value: bool) -> Bool {
792 let zero = || {
793 Box::new(BvTerm::Const {
794 value: 0,
795 sort: Sort::new(1),
796 })
797 };
798 Bool {
799 term: if value {
800 BoolTerm::Eq(zero(), zero())
801 } else {
802 BoolTerm::Ne(zero(), zero())
803 },
804 }
805 }
806
807 pub fn ite(&self, then_: impl Borrow<BV>, else_: impl Borrow<BV>) -> BV {
810 let then_ = then_.borrow();
811 let else_ = else_.borrow();
812 BV {
813 term: BvTerm::Ite {
814 cond: Box::new(self.term.clone()),
815 then_: Box::new(then_.term.clone()),
816 else_: Box::new(else_.term.clone()),
817 },
818 width: then_.width,
819 }
820 }
821
822 pub fn simplify(&self) -> Bool {
825 self.clone()
826 }
827
828 pub fn as_bool(&self) -> Option<bool> {
830 ordeal::eval::eval_bool(&self.term, &ordeal::eval::Env::new()).ok()
831 }
832}
833
834fn fmt_bv(t: &BvTerm, f: &mut fmt::Formatter<'_>) -> fmt::Result {
839 match t {
840 BvTerm::Const { value, sort } => write!(f, "(_ bv{} {})", value, sort.width),
841 BvTerm::Var { name, .. } => write!(f, "{}", name),
842 BvTerm::Add(a, b) => fmt_bin(f, "bvadd", a, b),
843 BvTerm::Sub(a, b) => fmt_bin(f, "bvsub", a, b),
844 BvTerm::Mul(a, b) => fmt_bin(f, "bvmul", a, b),
845 BvTerm::Udiv(a, b) => fmt_bin(f, "bvudiv", a, b),
846 BvTerm::And(a, b) => fmt_bin(f, "bvand", a, b),
847 BvTerm::Or(a, b) => fmt_bin(f, "bvor", a, b),
848 BvTerm::Xor(a, b) => fmt_bin(f, "bvxor", a, b),
849 BvTerm::Shl(a, b) => fmt_bin(f, "bvshl", a, b),
850 BvTerm::Lshr(a, b) => fmt_bin(f, "bvlshr", a, b),
851 BvTerm::Ashr(a, b) => fmt_bin(f, "bvashr", a, b),
852 BvTerm::Rotr(a, b) => fmt_bin(f, "bvrotr", a, b),
853 BvTerm::Extract { hi, lo, arg } => {
854 write!(f, "((_ extract {} {}) ", hi, lo)?;
855 fmt_bv(arg, f)?;
856 write!(f, ")")
857 }
858 BvTerm::Concat(a, b) => fmt_bin(f, "concat", a, b),
859 BvTerm::ZeroExt { by, arg } => {
860 write!(f, "((_ zero_extend {}) ", by)?;
861 fmt_bv(arg, f)?;
862 write!(f, ")")
863 }
864 BvTerm::SignExt { by, arg } => {
865 write!(f, "((_ sign_extend {}) ", by)?;
866 fmt_bv(arg, f)?;
867 write!(f, ")")
868 }
869 BvTerm::Ite { cond, then_, else_ } => {
870 write!(f, "(ite ")?;
871 fmt_bool(cond, f)?;
872 write!(f, " ")?;
873 fmt_bv(then_, f)?;
874 write!(f, " ")?;
875 fmt_bv(else_, f)?;
876 write!(f, ")")
877 }
878 }
879}
880
881fn fmt_bin(f: &mut fmt::Formatter<'_>, op: &str, a: &BvTerm, b: &BvTerm) -> fmt::Result {
882 write!(f, "({} ", op)?;
883 fmt_bv(a, f)?;
884 write!(f, " ")?;
885 fmt_bv(b, f)?;
886 write!(f, ")")
887}
888
889fn fmt_bool(t: &BoolTerm, f: &mut fmt::Formatter<'_>) -> fmt::Result {
890 let bin = |f: &mut fmt::Formatter<'_>, op: &str, a: &BvTerm, b: &BvTerm| -> fmt::Result {
891 write!(f, "({} ", op)?;
892 fmt_bv(a, f)?;
893 write!(f, " ")?;
894 fmt_bv(b, f)?;
895 write!(f, ")")
896 };
897 match t {
898 BoolTerm::Eq(a, b) => bin(f, "=", a, b),
899 BoolTerm::Ne(a, b) => bin(f, "distinct", a, b),
900 BoolTerm::Ult(a, b) => bin(f, "bvult", a, b),
901 BoolTerm::Ule(a, b) => bin(f, "bvule", a, b),
902 BoolTerm::Ugt(a, b) => bin(f, "bvugt", a, b),
903 BoolTerm::Uge(a, b) => bin(f, "bvuge", a, b),
904 BoolTerm::Slt(a, b) => bin(f, "bvslt", a, b),
905 BoolTerm::Sle(a, b) => bin(f, "bvsle", a, b),
906 BoolTerm::Sgt(a, b) => bin(f, "bvsgt", a, b),
907 BoolTerm::Sge(a, b) => bin(f, "bvsge", a, b),
908 BoolTerm::Not(a) => {
909 write!(f, "(not ")?;
910 fmt_bool(a, f)?;
911 write!(f, ")")
912 }
913 BoolTerm::And(a, b) => {
914 write!(f, "(and ")?;
915 fmt_bool(a, f)?;
916 write!(f, " ")?;
917 fmt_bool(b, f)?;
918 write!(f, ")")
919 }
920 BoolTerm::Or(a, b) => {
921 write!(f, "(or ")?;
922 fmt_bool(a, f)?;
923 write!(f, " ")?;
924 fmt_bool(b, f)?;
925 write!(f, ")")
926 }
927 }
928}
929
930impl fmt::Display for BV {
931 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
932 fmt_bv(&self.term, f)
933 }
934}
935
936impl fmt::Display for Bool {
937 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
938 fmt_bool(&self.term, f)
939 }
940}
941
942#[cfg(test)]
943mod tests {
944 use super::*;
945
946 #[test]
947 fn const_folding_matches_z3_idiom() {
948 let a = BV::from_i64(40, 32);
949 let b = BV::from_i64(2, 32);
950 assert_eq!(a.bvadd(&b).simplify().as_i64(), Some(42));
951 assert_eq!(a.bvsub(&b).simplify().as_i64(), Some(38));
952 assert_eq!(BV::from_i64(-1, 32).as_i64(), Some(0xFFFF_FFFF));
954 }
955
956 #[test]
957 fn commutative_construction_is_canonical() {
958 let x = BV::new_const("x", 32);
959 let y = BV::new_const("y", 32);
960 assert_eq!(x.bvmul(&y).to_string(), y.bvmul(&x).to_string());
962 assert_eq!(x.bvadd(&y).to_string(), y.bvadd(&x).to_string());
963 assert_eq!(x.eq(&y).to_string(), y.eq(&x).to_string());
964 assert_ne!(x.bvsub(&y).to_string(), y.bvsub(&x).to_string());
966 }
967
968 #[test]
969 fn display_uses_smtlib_mnemonics() {
970 let x = BV::new_const("x", 32);
971 let s = x.bvadd(BV::from_i64(1, 32)).to_string();
972 assert!(s.contains("bvadd"), "{s}");
973 }
974}