1use anyhow::Result;
2use std::fmt::{Debug, Display};
3use std::ops::{BitAnd, BitOr, Not};
4use z3::ast::{Ast, Bool};
5
6use super::measure::MeasureVariant;
7use super::{
8 context::{LayoutContext, Z3BuildContext},
9 measure::Measure,
10};
11
12#[derive(Copy, Clone)]
14pub struct Prop<'a> {
15 pub ctx: &'a LayoutContext,
16 pub(super) variant: &'a PropVariant<'a>,
17 pub(super) weight: u32,
18}
19
20impl<'a> Debug for Prop<'a> {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 write!(f, "Prop({}) {{ {:?} }}", self.weight, self.variant)
23 }
24}
25
26#[derive(Copy, Clone, Debug)]
27pub enum PropVariant<'a> {
28 Eq(Measure<'a>, Measure<'a>),
29 Lt(Measure<'a>, Measure<'a>),
30 Le(Measure<'a>, Measure<'a>),
31 Gt(Measure<'a>, Measure<'a>),
32 Ge(Measure<'a>, Measure<'a>),
33 Or(Prop<'a>, Prop<'a>),
34 And(Prop<'a>, Prop<'a>),
35 Not(Prop<'a>),
36}
37
38#[allow(dead_code)]
39impl<'a> Prop<'a> {
40 pub fn with_weight(mut self, weight: u32) -> Self {
41 self.weight = weight;
42 self
43 }
44
45 pub fn select(self, left: Measure<'a>, right: Measure<'a>) -> Measure<'a> {
46 Measure {
47 ctx: self.ctx,
48 variant: self
49 .ctx
50 .alloc
51 .alloc(MeasureVariant::Select(self, left, right)),
52 }
53 }
54
55 pub fn build_z3<'ctx>(self, build_ctx: &mut Z3BuildContext<'ctx>) -> Result<Bool<'ctx>> {
56 let key = self.variant as *const _ as usize;
57 if let Some(x) = build_ctx.prop_cache.get(&key) {
58 return Ok(x.clone());
59 }
60 let res = self.do_build_z3(build_ctx)?;
61 build_ctx.prop_cache.insert(key, res.clone());
62 Ok(res)
63 }
64
65 fn do_build_z3<'ctx>(self, build_ctx: &mut Z3BuildContext<'ctx>) -> Result<Bool<'ctx>> {
66 use PropVariant as V;
67 let z3_ctx = build_ctx.z3_ctx;
68 Ok(match *self.variant {
69 V::Eq(left, right) => left.build_z3(build_ctx)?._eq(&right.build_z3(build_ctx)?),
70 V::Lt(left, right) => left.build_z3(build_ctx)?.lt(&right.build_z3(build_ctx)?),
71 V::Le(left, right) => left.build_z3(build_ctx)?.le(&right.build_z3(build_ctx)?),
72 V::Gt(left, right) => left.build_z3(build_ctx)?.gt(&right.build_z3(build_ctx)?),
73 V::Ge(left, right) => left.build_z3(build_ctx)?.ge(&right.build_z3(build_ctx)?),
74 V::Or(left, right) => Bool::or(
75 z3_ctx,
76 &[&left.build_z3(build_ctx)?, &right.build_z3(build_ctx)?],
77 ),
78 V::And(left, right) => Bool::and(
79 z3_ctx,
80 &[&left.build_z3(build_ctx)?, &right.build_z3(build_ctx)?],
81 ),
82 V::Not(that) => that.build_z3(build_ctx)?.not(),
83 })
84 }
85}
86
87impl<'a> Display for Prop<'a> {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 match self.variant {
90 PropVariant::Eq(l, r) => write!(f, "{} == {}", l, r),
91 PropVariant::Lt(l, r) => write!(f, "{} < {}", l, r),
92 PropVariant::Le(l, r) => write!(f, "{} <= {}", l, r),
93 PropVariant::Gt(l, r) => write!(f, "{} > {}", l, r),
94 PropVariant::Ge(l, r) => write!(f, "{} >= {}", l, r),
95 PropVariant::Or(l, r) => write!(f, "({}) or ({})", l, r),
96 PropVariant::And(l, r) => write!(f, "({}) and ({})", l, r),
97 PropVariant::Not(x) => write!(f, "not ({})", x),
98 }
99 }
100}
101
102impl<'a> BitOr for Prop<'a> {
103 type Output = Self;
104 fn bitor(self, that: Prop<'a>) -> Self {
105 Self {
106 ctx: self.ctx,
107 variant: self.ctx.alloc.alloc(PropVariant::Or(self, that)),
108 weight: 10,
109 }
110 }
111}
112
113impl<'a> BitAnd for Prop<'a> {
114 type Output = Self;
115 fn bitand(self, that: Prop<'a>) -> Self {
116 Self {
117 ctx: self.ctx,
118 variant: self.ctx.alloc.alloc(PropVariant::And(self, that)),
119 weight: 10,
120 }
121 }
122}
123
124impl<'a> Not for Prop<'a> {
125 type Output = Self;
126 fn not(self) -> Self {
127 Self {
128 ctx: self.ctx,
129 variant: self.ctx.alloc.alloc(PropVariant::Not(self)),
130 weight: 10,
131 }
132 }
133}