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 var_name(&self) -> Option<&str> {
348 match &self.term {
349 BvTerm::Var { name, .. } => Some(name),
350 _ => None,
351 }
352 }
353
354 pub fn new_const(name: impl Into<String>, width: u32) -> Self {
356 Self {
357 term: BvTerm::Var {
358 name: name.into(),
359 sort: Sort::new(width),
360 },
361 width,
362 }
363 }
364
365 pub fn from_i64(value: i64, width: u32) -> Self {
367 Self {
368 term: BvTerm::Const {
369 value: (value as u64 as u128) & mask(width),
370 sort: Sort::new(width),
371 },
372 width,
373 }
374 }
375
376 pub fn from_u64(value: u64, width: u32) -> Self {
378 Self {
379 term: BvTerm::Const {
380 value: (value as u128) & mask(width),
381 sort: Sort::new(width),
382 },
383 width,
384 }
385 }
386
387 pub fn get_size(&self) -> u32 {
389 self.width
390 }
391
392 fn binop(
393 &self,
394 other: impl Borrow<BV>,
395 f: impl FnOnce(Box<BvTerm>, Box<BvTerm>) -> BvTerm,
396 ) -> BV {
397 let other = other.borrow();
398 BV {
399 term: f(Box::new(self.term.clone()), Box::new(other.term.clone())),
400 width: self.width,
401 }
402 }
403
404 fn commutative(
405 &self,
406 other: impl Borrow<BV>,
407 f: impl FnOnce(Box<BvTerm>, Box<BvTerm>) -> BvTerm,
408 ) -> BV {
409 let other = other.borrow();
410 let (a, b) = canonical_pair(self.term.clone(), other.term.clone());
411 BV {
412 term: f(a, b),
413 width: self.width,
414 }
415 }
416
417 pub fn bvadd(&self, other: impl Borrow<BV>) -> BV {
421 self.commutative(other, BvTerm::Add)
422 }
423
424 pub fn bvsub(&self, other: impl Borrow<BV>) -> BV {
426 self.binop(other, BvTerm::Sub)
427 }
428
429 pub fn bvmul(&self, other: impl Borrow<BV>) -> BV {
431 self.commutative(other, BvTerm::Mul)
432 }
433
434 pub fn bvudiv(&self, other: impl Borrow<BV>) -> BV {
436 self.binop(other, BvTerm::Udiv)
437 }
438
439 pub fn bvsdiv(&self, other: impl Borrow<BV>) -> BV {
441 BV {
442 term: canonicalize_bv(&ordeal::lowering::bvsdiv(
443 self.term.clone(),
444 other.borrow().term.clone(),
445 self.width,
446 )),
447 width: self.width,
448 }
449 }
450
451 pub fn bvurem(&self, other: impl Borrow<BV>) -> BV {
453 BV {
454 term: canonicalize_bv(&ordeal::lowering::bvurem(
455 self.term.clone(),
456 other.borrow().term.clone(),
457 self.width,
458 )),
459 width: self.width,
460 }
461 }
462
463 pub fn bvsrem(&self, other: impl Borrow<BV>) -> BV {
465 BV {
466 term: canonicalize_bv(&ordeal::lowering::bvsrem(
467 self.term.clone(),
468 other.borrow().term.clone(),
469 self.width,
470 )),
471 width: self.width,
472 }
473 }
474
475 pub fn bvneg(&self) -> BV {
477 BV {
478 term: canonicalize_bv(&ordeal::lowering::bvneg(self.term.clone(), self.width)),
479 width: self.width,
480 }
481 }
482
483 pub fn bvand(&self, other: impl Borrow<BV>) -> BV {
487 self.commutative(other, BvTerm::And)
488 }
489
490 pub fn bvor(&self, other: impl Borrow<BV>) -> BV {
492 self.commutative(other, BvTerm::Or)
493 }
494
495 pub fn bvxor(&self, other: impl Borrow<BV>) -> BV {
497 self.commutative(other, BvTerm::Xor)
498 }
499
500 pub fn bvnot(&self) -> BV {
502 BV {
503 term: canonicalize_bv(&ordeal::lowering::bvnot(self.term.clone(), self.width)),
504 width: self.width,
505 }
506 }
507
508 pub fn bvshl(&self, other: impl Borrow<BV>) -> BV {
512 self.binop(other, BvTerm::Shl)
513 }
514
515 pub fn bvlshr(&self, other: impl Borrow<BV>) -> BV {
517 self.binop(other, BvTerm::Lshr)
518 }
519
520 pub fn bvashr(&self, other: impl Borrow<BV>) -> BV {
522 self.binop(other, BvTerm::Ashr)
523 }
524
525 pub fn bvrotl(&self, other: impl Borrow<BV>) -> BV {
527 BV {
528 term: canonicalize_bv(&ordeal::lowering::bvrotl(
529 self.term.clone(),
530 other.borrow().term.clone(),
531 self.width,
532 )),
533 width: self.width,
534 }
535 }
536
537 pub fn bvrotr(&self, other: impl Borrow<BV>) -> BV {
539 self.binop(other, BvTerm::Rotr)
540 }
541
542 pub fn extract(&self, hi: u32, lo: u32) -> BV {
546 BV {
547 term: BvTerm::Extract {
548 hi,
549 lo,
550 arg: Box::new(self.term.clone()),
551 },
552 width: hi - lo + 1,
553 }
554 }
555
556 pub fn concat(&self, other: impl Borrow<BV>) -> BV {
558 let other = other.borrow();
559 BV {
560 term: BvTerm::Concat(Box::new(self.term.clone()), Box::new(other.term.clone())),
561 width: self.width + other.width,
562 }
563 }
564
565 pub fn zero_ext(&self, by: u32) -> BV {
567 BV {
568 term: BvTerm::ZeroExt {
569 by,
570 arg: Box::new(self.term.clone()),
571 },
572 width: self.width + by,
573 }
574 }
575
576 pub fn sign_ext(&self, by: u32) -> BV {
578 BV {
579 term: BvTerm::SignExt {
580 by,
581 arg: Box::new(self.term.clone()),
582 },
583 width: self.width + by,
584 }
585 }
586
587 fn cmp_op(
590 &self,
591 other: impl Borrow<BV>,
592 f: impl FnOnce(Box<BvTerm>, Box<BvTerm>) -> BoolTerm,
593 ) -> Bool {
594 Bool {
595 term: f(
596 Box::new(self.term.clone()),
597 Box::new(other.borrow().term.clone()),
598 ),
599 }
600 }
601
602 pub fn eq(&self, other: impl Borrow<BV>) -> Bool {
604 let (a, b) = canonical_pair(self.term.clone(), other.borrow().term.clone());
605 Bool {
606 term: BoolTerm::Eq(a, b),
607 }
608 }
609
610 pub fn ne(&self, other: impl Borrow<BV>) -> Bool {
612 let (a, b) = canonical_pair(self.term.clone(), other.borrow().term.clone());
613 Bool {
614 term: BoolTerm::Ne(a, b),
615 }
616 }
617
618 pub fn bvult(&self, other: impl Borrow<BV>) -> Bool {
620 self.cmp_op(other, BoolTerm::Ult)
621 }
622
623 pub fn bvule(&self, other: impl Borrow<BV>) -> Bool {
625 self.cmp_op(other, BoolTerm::Ule)
626 }
627
628 pub fn bvugt(&self, other: impl Borrow<BV>) -> Bool {
630 self.cmp_op(other, BoolTerm::Ugt)
631 }
632
633 pub fn bvuge(&self, other: impl Borrow<BV>) -> Bool {
635 self.cmp_op(other, BoolTerm::Uge)
636 }
637
638 pub fn bvslt(&self, other: impl Borrow<BV>) -> Bool {
640 self.cmp_op(other, BoolTerm::Slt)
641 }
642
643 pub fn bvsle(&self, other: impl Borrow<BV>) -> Bool {
645 self.cmp_op(other, BoolTerm::Sle)
646 }
647
648 pub fn bvsgt(&self, other: impl Borrow<BV>) -> Bool {
650 self.cmp_op(other, BoolTerm::Sgt)
651 }
652
653 pub fn bvsge(&self, other: impl Borrow<BV>) -> Bool {
655 self.cmp_op(other, BoolTerm::Sge)
656 }
657
658 pub fn simplify(&self) -> BV {
664 self.clone()
665 }
666
667 pub fn as_i64(&self) -> Option<i64> {
670 self.eval_closed().and_then(|v| i64::try_from(v).ok())
671 }
672
673 pub fn as_u64(&self) -> Option<u64> {
675 self.eval_closed().and_then(|v| u64::try_from(v).ok())
676 }
677
678 fn eval_closed(&self) -> Option<u128> {
679 ordeal::eval::eval_bv(&self.term, &ordeal::eval::Env::new()).ok()
680 }
681}
682
683impl Bool {
688 pub(crate) fn term(&self) -> &BoolTerm {
690 &self.term
691 }
692
693 pub fn new_const(name: impl Into<String>) -> Self {
698 let var = BvTerm::Var {
699 name: name.into(),
700 sort: Sort::new(1),
701 };
702 Self {
703 term: BoolTerm::Ne(
704 Box::new(var),
705 Box::new(BvTerm::Const {
706 value: 0,
707 sort: Sort::new(1),
708 }),
709 ),
710 }
711 }
712
713 pub fn from_bool(value: bool) -> Self {
715 Self::literal(value)
716 }
717
718 pub fn eq(&self, other: impl Borrow<Bool>) -> Bool {
721 let a = self.term.clone();
722 let b = other.borrow().term.clone();
723 Bool {
724 term: BoolTerm::Or(
725 Box::new(BoolTerm::And(Box::new(a.clone()), Box::new(b.clone()))),
726 Box::new(BoolTerm::And(
727 Box::new(BoolTerm::Not(Box::new(a))),
728 Box::new(BoolTerm::Not(Box::new(b))),
729 )),
730 ),
731 }
732 }
733
734 pub fn not(&self) -> Bool {
736 Bool {
737 term: BoolTerm::Not(Box::new(self.term.clone())),
738 }
739 }
740
741 pub fn and(values: &[&Bool]) -> Bool {
743 Self::fold(values, BoolTerm::And, true)
744 }
745
746 pub fn or(values: &[&Bool]) -> Bool {
748 Self::fold(values, BoolTerm::Or, false)
749 }
750
751 fn fold(
752 values: &[&Bool],
753 f: impl Fn(Box<BoolTerm>, Box<BoolTerm>) -> BoolTerm,
754 empty: bool,
755 ) -> Bool {
756 let mut iter = values.iter();
757 let Some(first) = iter.next() else {
758 return Self::literal(empty);
759 };
760 let mut acc = first.term.clone();
761 for v in iter {
762 acc = f(Box::new(acc), Box::new(v.term.clone()));
763 }
764 Bool { term: acc }
765 }
766
767 fn literal(value: bool) -> Bool {
770 let zero = || {
771 Box::new(BvTerm::Const {
772 value: 0,
773 sort: Sort::new(1),
774 })
775 };
776 Bool {
777 term: if value {
778 BoolTerm::Eq(zero(), zero())
779 } else {
780 BoolTerm::Ne(zero(), zero())
781 },
782 }
783 }
784
785 pub fn ite(&self, then_: impl Borrow<BV>, else_: impl Borrow<BV>) -> BV {
788 let then_ = then_.borrow();
789 let else_ = else_.borrow();
790 BV {
791 term: BvTerm::Ite {
792 cond: Box::new(self.term.clone()),
793 then_: Box::new(then_.term.clone()),
794 else_: Box::new(else_.term.clone()),
795 },
796 width: then_.width,
797 }
798 }
799
800 pub fn simplify(&self) -> Bool {
803 self.clone()
804 }
805
806 pub fn as_bool(&self) -> Option<bool> {
808 ordeal::eval::eval_bool(&self.term, &ordeal::eval::Env::new()).ok()
809 }
810}
811
812fn fmt_bv(t: &BvTerm, f: &mut fmt::Formatter<'_>) -> fmt::Result {
817 match t {
818 BvTerm::Const { value, sort } => write!(f, "(_ bv{} {})", value, sort.width),
819 BvTerm::Var { name, .. } => write!(f, "{}", name),
820 BvTerm::Add(a, b) => fmt_bin(f, "bvadd", a, b),
821 BvTerm::Sub(a, b) => fmt_bin(f, "bvsub", a, b),
822 BvTerm::Mul(a, b) => fmt_bin(f, "bvmul", a, b),
823 BvTerm::Udiv(a, b) => fmt_bin(f, "bvudiv", a, b),
824 BvTerm::And(a, b) => fmt_bin(f, "bvand", a, b),
825 BvTerm::Or(a, b) => fmt_bin(f, "bvor", a, b),
826 BvTerm::Xor(a, b) => fmt_bin(f, "bvxor", a, b),
827 BvTerm::Shl(a, b) => fmt_bin(f, "bvshl", a, b),
828 BvTerm::Lshr(a, b) => fmt_bin(f, "bvlshr", a, b),
829 BvTerm::Ashr(a, b) => fmt_bin(f, "bvashr", a, b),
830 BvTerm::Rotr(a, b) => fmt_bin(f, "bvrotr", a, b),
831 BvTerm::Extract { hi, lo, arg } => {
832 write!(f, "((_ extract {} {}) ", hi, lo)?;
833 fmt_bv(arg, f)?;
834 write!(f, ")")
835 }
836 BvTerm::Concat(a, b) => fmt_bin(f, "concat", a, b),
837 BvTerm::ZeroExt { by, arg } => {
838 write!(f, "((_ zero_extend {}) ", by)?;
839 fmt_bv(arg, f)?;
840 write!(f, ")")
841 }
842 BvTerm::SignExt { by, arg } => {
843 write!(f, "((_ sign_extend {}) ", by)?;
844 fmt_bv(arg, f)?;
845 write!(f, ")")
846 }
847 BvTerm::Ite { cond, then_, else_ } => {
848 write!(f, "(ite ")?;
849 fmt_bool(cond, f)?;
850 write!(f, " ")?;
851 fmt_bv(then_, f)?;
852 write!(f, " ")?;
853 fmt_bv(else_, f)?;
854 write!(f, ")")
855 }
856 }
857}
858
859fn fmt_bin(f: &mut fmt::Formatter<'_>, op: &str, a: &BvTerm, b: &BvTerm) -> fmt::Result {
860 write!(f, "({} ", op)?;
861 fmt_bv(a, f)?;
862 write!(f, " ")?;
863 fmt_bv(b, f)?;
864 write!(f, ")")
865}
866
867fn fmt_bool(t: &BoolTerm, f: &mut fmt::Formatter<'_>) -> fmt::Result {
868 let bin = |f: &mut fmt::Formatter<'_>, op: &str, a: &BvTerm, b: &BvTerm| -> fmt::Result {
869 write!(f, "({} ", op)?;
870 fmt_bv(a, f)?;
871 write!(f, " ")?;
872 fmt_bv(b, f)?;
873 write!(f, ")")
874 };
875 match t {
876 BoolTerm::Eq(a, b) => bin(f, "=", a, b),
877 BoolTerm::Ne(a, b) => bin(f, "distinct", a, b),
878 BoolTerm::Ult(a, b) => bin(f, "bvult", a, b),
879 BoolTerm::Ule(a, b) => bin(f, "bvule", a, b),
880 BoolTerm::Ugt(a, b) => bin(f, "bvugt", a, b),
881 BoolTerm::Uge(a, b) => bin(f, "bvuge", a, b),
882 BoolTerm::Slt(a, b) => bin(f, "bvslt", a, b),
883 BoolTerm::Sle(a, b) => bin(f, "bvsle", a, b),
884 BoolTerm::Sgt(a, b) => bin(f, "bvsgt", a, b),
885 BoolTerm::Sge(a, b) => bin(f, "bvsge", a, b),
886 BoolTerm::Not(a) => {
887 write!(f, "(not ")?;
888 fmt_bool(a, f)?;
889 write!(f, ")")
890 }
891 BoolTerm::And(a, b) => {
892 write!(f, "(and ")?;
893 fmt_bool(a, f)?;
894 write!(f, " ")?;
895 fmt_bool(b, f)?;
896 write!(f, ")")
897 }
898 BoolTerm::Or(a, b) => {
899 write!(f, "(or ")?;
900 fmt_bool(a, f)?;
901 write!(f, " ")?;
902 fmt_bool(b, f)?;
903 write!(f, ")")
904 }
905 }
906}
907
908impl fmt::Display for BV {
909 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
910 fmt_bv(&self.term, f)
911 }
912}
913
914impl fmt::Display for Bool {
915 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
916 fmt_bool(&self.term, f)
917 }
918}
919
920#[cfg(test)]
921mod tests {
922 use super::*;
923
924 #[test]
925 fn const_folding_matches_z3_idiom() {
926 let a = BV::from_i64(40, 32);
927 let b = BV::from_i64(2, 32);
928 assert_eq!(a.bvadd(&b).simplify().as_i64(), Some(42));
929 assert_eq!(a.bvsub(&b).simplify().as_i64(), Some(38));
930 assert_eq!(BV::from_i64(-1, 32).as_i64(), Some(0xFFFF_FFFF));
932 }
933
934 #[test]
935 fn commutative_construction_is_canonical() {
936 let x = BV::new_const("x", 32);
937 let y = BV::new_const("y", 32);
938 assert_eq!(x.bvmul(&y).to_string(), y.bvmul(&x).to_string());
940 assert_eq!(x.bvadd(&y).to_string(), y.bvadd(&x).to_string());
941 assert_eq!(x.eq(&y).to_string(), y.eq(&x).to_string());
942 assert_ne!(x.bvsub(&y).to_string(), y.bvsub(&x).to_string());
944 }
945
946 #[test]
947 fn display_uses_smtlib_mnemonics() {
948 let x = BV::new_const("x", 32);
949 let s = x.bvadd(BV::from_i64(1, 32)).to_string();
950 assert!(s.contains("bvadd"), "{s}");
951 }
952}