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