1pub mod count;
6pub mod poly;
7
8pub mod t0;
9pub mod t1;
10pub mod t2;
11
12use js_sys::Function;
13use std::fmt;
14use std::path::PathBuf;
15
16use crate::parser::lexer::ArithCmpOp;
17use poly::{Poly, Var};
18
19use t1::{Linearized, Z3};
20
21#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
23pub enum Cmp {
24 Eq,
25 Ne,
26 Le,
27 Lt,
28 Ge,
29 Gt,
30}
31
32impl Cmp {
33 pub fn negate(self) -> Cmp {
34 match self {
35 Cmp::Eq => Cmp::Ne,
36 Cmp::Ne => Cmp::Eq,
37 Cmp::Le => Cmp::Gt,
38 Cmp::Lt => Cmp::Ge,
39 Cmp::Ge => Cmp::Lt,
40 Cmp::Gt => Cmp::Le,
41 }
42 }
43
44 fn smt(self) -> &'static str {
45 match self {
46 Cmp::Eq => "=",
47 Cmp::Ne => "distinct",
48 Cmp::Le => "<=",
49 Cmp::Lt => "<",
50 Cmp::Ge => ">=",
51 Cmp::Gt => ">",
52 }
53 }
54
55 pub fn from_lex(op: &ArithCmpOp) -> Self {
56 match op {
57 ArithCmpOp::Greater => Cmp::Gt,
58 ArithCmpOp::GreaterOrEq => Cmp::Ge,
59 ArithCmpOp::Less => Cmp::Lt,
60 ArithCmpOp::LessOrEq => Cmp::Le,
61 ArithCmpOp::Eq => Cmp::Eq,
62 ArithCmpOp::NotEq => Cmp::Ne,
63 }
64 }
65}
66
67impl fmt::Display for Cmp {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 f.write_str(match self {
70 Cmp::Eq => "==",
71 Cmp::Ne => "!=",
72 Cmp::Le => "<=",
73 Cmp::Lt => "<",
74 Cmp::Ge => ">=",
75 Cmp::Gt => ">",
76 })
77 }
78}
79
80#[derive(Clone, PartialEq, Eq, Debug)]
82pub struct Constraint {
83 pub lhs: Poly,
84 pub cmp: Cmp,
85 pub rhs: Poly,
86}
87
88impl Constraint {
89 pub fn new(lhs: Poly, cmp: Cmp, rhs: Poly) -> Self {
90 Self { lhs, cmp, rhs }
91 }
92
93 pub fn display_with(&self, names: &[&str]) -> String {
94 format!(
95 "{} {} {}",
96 self.lhs.display_with(Some(names)),
97 self.cmp,
98 self.rhs.display_with(Some(names))
99 )
100 }
101
102 pub fn negate(&self) -> Self {
103 Self {
104 lhs: self.lhs.clone(),
105 cmp: self.cmp.negate(),
106 rhs: self.rhs.clone(),
107 }
108 }
109}
110
111pub type Point = Vec<(Var, i128)>;
112
113#[derive(Clone, PartialEq, Eq, Debug)]
118pub enum Verdict {
119 Proved,
120 RefutedBy(Constraint),
121 RefutedAt(Point),
122 Unknown,
123}
124
125pub struct Solver {
127 pub z3: Z3,
128}
129
130impl Solver {
131 pub fn new_cli(cache_dir: Option<PathBuf>) -> Option<Self> {
134 Some(Self {
135 z3: Z3::new_cli(cache_dir)?,
136 })
137 }
138
139 pub fn new_wasm(callback: Function) -> Self {
141 Self {
142 z3: Z3::new_wasm(callback),
143 }
144 }
145
146 pub fn prove(
147 &mut self,
148 facts: &[Constraint],
149 goal: &Constraint,
150 names: &[String],
151 nonneg: &[Var],
152 ) -> Verdict {
153 let lin = Linearized::new(facts, goal, names, &|v| nonneg.contains(&v));
154
155 let verdict = {
156 if facts.contains(goal)
157 || t0::prove(goal.cmp, &goal.lhs, &goal.rhs, &|v| nonneg.contains(&v))
158 {
159 Verdict::Proved
160 } else {
161 if facts.contains(&goal.negate())
162 || t0::prove(goal.cmp.negate(), &goal.lhs, &goal.rhs, &|v| {
163 nonneg.contains(&v)
164 })
165 {
166 return Verdict::RefutedBy(goal.negate());
167 }
168
169 let lin_names: Vec<&str> = lin.names.iter().map(String::as_str).collect();
170
171 let z3_lia_verdict =
172 self.z3
173 .entails_lia(&lin.facts, &lin.goal, &lin.nonneg, &lin_names);
174
175 match (z3_lia_verdict, lin.pure_linear) {
176 (Verdict::Proved, _) => Verdict::Proved,
177 (Verdict::RefutedAt(p), true) => Verdict::RefutedAt(p),
178 _ => Verdict::Unknown,
179 }
180 }
181 };
182
183 if verdict != Verdict::Proved
186 && !lin.pure_linear
187 && t2::prove(&mut self.z3, facts, goal, nonneg)
188 {
189 return Verdict::Proved;
190 }
191
192 verdict
193 }
194}