#![allow(clippy::should_implement_trait)]
use alloc::boxed::Box;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Iv {
pub lo: u64,
pub hi: u64,
}
impl Iv {
pub const fn point(v: u64) -> Iv {
Iv { lo: v, hi: v }
}
pub const fn new(lo: u64, hi: u64) -> Iv {
if lo <= hi {
Iv { lo, hi }
} else {
Iv { lo: hi, hi: lo }
}
}
pub const fn full() -> Iv {
Iv {
lo: 0,
hi: u64::MAX,
}
}
pub const fn contains(&self, v: u64) -> bool {
self.lo <= v && v <= self.hi
}
fn add(self, o: Iv) -> Iv {
match (self.lo.checked_add(o.lo), self.hi.checked_add(o.hi)) {
(Some(lo), Some(hi)) => Iv { lo, hi },
_ => Iv::full(), }
}
fn sub(self, o: Iv) -> Iv {
match (self.lo.checked_sub(o.hi), self.hi.checked_sub(o.lo)) {
(Some(lo), Some(hi)) => Iv { lo, hi },
_ => Iv::full(),
}
}
fn mul(self, o: Iv) -> Iv {
match (self.lo.checked_mul(o.lo), self.hi.checked_mul(o.hi)) {
(Some(lo), Some(hi)) => Iv { lo, hi },
_ => Iv::full(),
}
}
fn shl(self, k: u32) -> Iv {
if k >= 64 {
return if self.lo == 0 && self.hi == 0 {
Iv::point(0)
} else {
Iv::full()
};
}
if self.hi <= (u64::MAX >> k) {
Iv {
lo: self.lo << k,
hi: self.hi << k,
}
} else {
Iv::full()
}
}
fn shr(self, k: u32) -> Iv {
if k >= 64 {
return Iv::point(0);
}
Iv {
lo: self.lo >> k,
hi: self.hi >> k,
} }
fn bitand(self, mask: u64) -> Iv {
let hi = if mask < self.hi { mask } else { self.hi };
Iv { lo: 0, hi }
}
fn bitor(self, mask: u64) -> Iv {
let lo = if mask > self.lo { mask } else { self.lo };
Iv { lo, hi: u64::MAX }
}
fn rem(self, m: u64) -> Iv {
if m == 0 {
return Iv::full(); }
if self.hi < m {
self } else {
Iv { lo: 0, hi: m - 1 }
}
}
}
#[derive(Clone, Debug)]
pub enum Expr {
Var,
Const(u64),
Add(Box<Expr>, Box<Expr>),
Sub(Box<Expr>, Box<Expr>),
Mul(Box<Expr>, Box<Expr>),
Shl(Box<Expr>, u32),
Shr(Box<Expr>, u32),
And(Box<Expr>, u64),
Or(Box<Expr>, u64),
Rem(Box<Expr>, u64),
}
impl Expr {
pub fn var() -> Expr {
Expr::Var
}
pub fn c(v: u64) -> Expr {
Expr::Const(v)
}
pub fn add(self, o: Expr) -> Expr {
Expr::Add(Box::new(self), Box::new(o))
}
pub fn sub(self, o: Expr) -> Expr {
Expr::Sub(Box::new(self), Box::new(o))
}
pub fn mul(self, o: Expr) -> Expr {
Expr::Mul(Box::new(self), Box::new(o))
}
pub fn shl(self, k: u32) -> Expr {
Expr::Shl(Box::new(self), k)
}
pub fn shr(self, k: u32) -> Expr {
Expr::Shr(Box::new(self), k)
}
pub fn and(self, m: u64) -> Expr {
Expr::And(Box::new(self), m)
}
pub fn or(self, m: u64) -> Expr {
Expr::Or(Box::new(self), m)
}
pub fn rem(self, m: u64) -> Expr {
Expr::Rem(Box::new(self), m)
}
fn eval_iv(&self, x_iv: Iv) -> Iv {
match self {
Expr::Var => x_iv,
Expr::Const(v) => Iv::point(*v),
Expr::Add(a, b) => a.eval_iv(x_iv).add(b.eval_iv(x_iv)),
Expr::Sub(a, b) => a.eval_iv(x_iv).sub(b.eval_iv(x_iv)),
Expr::Mul(a, b) => a.eval_iv(x_iv).mul(b.eval_iv(x_iv)),
Expr::Shl(a, k) => a.eval_iv(x_iv).shl(*k),
Expr::Shr(a, k) => a.eval_iv(x_iv).shr(*k),
Expr::And(a, m) => a.eval_iv(x_iv).bitand(*m),
Expr::Or(a, m) => a.eval_iv(x_iv).bitor(*m),
Expr::Rem(a, m) => a.eval_iv(x_iv).rem(*m),
}
}
fn eval_at(&self, x: u64) -> u64 {
match self {
Expr::Var => x,
Expr::Const(v) => *v,
Expr::Add(a, b) => a.eval_at(x).wrapping_add(b.eval_at(x)),
Expr::Sub(a, b) => a.eval_at(x).wrapping_sub(b.eval_at(x)),
Expr::Mul(a, b) => a.eval_at(x).wrapping_mul(b.eval_at(x)),
Expr::Shl(a, k) => a.eval_at(x).wrapping_shl(*k),
Expr::Shr(a, k) => a.eval_at(x).wrapping_shr(*k),
Expr::And(a, m) => a.eval_at(x) & *m,
Expr::Or(a, m) => a.eval_at(x) | *m,
Expr::Rem(a, m) => {
if *m == 0 {
0
} else {
a.eval_at(x) % *m
}
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Tri {
True,
False,
Unknown,
}
impl Tri {
fn not(self) -> Tri {
match self {
Tri::True => Tri::False,
Tri::False => Tri::True,
Tri::Unknown => Tri::Unknown,
}
}
fn and(self, o: Tri) -> Tri {
match (self, o) {
(Tri::False, _) | (_, Tri::False) => Tri::False,
(Tri::True, Tri::True) => Tri::True,
_ => Tri::Unknown,
}
}
fn or(self, o: Tri) -> Tri {
match (self, o) {
(Tri::True, _) | (_, Tri::True) => Tri::True,
(Tri::False, Tri::False) => Tri::False,
_ => Tri::Unknown,
}
}
}
#[derive(Clone, Debug)]
pub enum Prop {
Le(Expr, Expr),
Lt(Expr, Expr),
Ge(Expr, Expr),
Gt(Expr, Expr),
Eq(Expr, Expr),
Ne(Expr, Expr),
And(Box<Prop>, Box<Prop>),
Or(Box<Prop>, Box<Prop>),
Not(Box<Prop>),
Implies(Box<Prop>, Box<Prop>),
}
impl Prop {
pub fn and(self, o: Prop) -> Prop {
Prop::And(Box::new(self), Box::new(o))
}
pub fn or(self, o: Prop) -> Prop {
Prop::Or(Box::new(self), Box::new(o))
}
pub fn not(self) -> Prop {
Prop::Not(Box::new(self))
}
pub fn implies(self, o: Prop) -> Prop {
Prop::Implies(Box::new(self), Box::new(o))
}
fn eval_iv(&self, x_iv: Iv) -> Tri {
match self {
Prop::Le(a, b) => cmp_le(a.eval_iv(x_iv), b.eval_iv(x_iv)),
Prop::Lt(a, b) => cmp_lt(a.eval_iv(x_iv), b.eval_iv(x_iv)),
Prop::Ge(a, b) => cmp_le(b.eval_iv(x_iv), a.eval_iv(x_iv)),
Prop::Gt(a, b) => cmp_lt(b.eval_iv(x_iv), a.eval_iv(x_iv)),
Prop::Eq(a, b) => cmp_eq(a.eval_iv(x_iv), b.eval_iv(x_iv)),
Prop::Ne(a, b) => cmp_eq(a.eval_iv(x_iv), b.eval_iv(x_iv)).not(),
Prop::And(p, q) => p.eval_iv(x_iv).and(q.eval_iv(x_iv)),
Prop::Or(p, q) => p.eval_iv(x_iv).or(q.eval_iv(x_iv)),
Prop::Not(p) => p.eval_iv(x_iv).not(),
Prop::Implies(p, q) => p.eval_iv(x_iv).not().or(q.eval_iv(x_iv)),
}
}
fn eval_at(&self, x: u64) -> bool {
match self {
Prop::Le(a, b) => a.eval_at(x) <= b.eval_at(x),
Prop::Lt(a, b) => a.eval_at(x) < b.eval_at(x),
Prop::Ge(a, b) => a.eval_at(x) >= b.eval_at(x),
Prop::Gt(a, b) => a.eval_at(x) > b.eval_at(x),
Prop::Eq(a, b) => a.eval_at(x) == b.eval_at(x),
Prop::Ne(a, b) => a.eval_at(x) != b.eval_at(x),
Prop::And(p, q) => p.eval_at(x) && q.eval_at(x),
Prop::Or(p, q) => p.eval_at(x) || q.eval_at(x),
Prop::Not(p) => !p.eval_at(x),
Prop::Implies(p, q) => !p.eval_at(x) || q.eval_at(x),
}
}
}
fn cmp_le(a: Iv, b: Iv) -> Tri {
if a.hi <= b.lo {
Tri::True
} else if a.lo > b.hi {
Tri::False
} else {
Tri::Unknown
}
}
fn cmp_lt(a: Iv, b: Iv) -> Tri {
if a.hi < b.lo {
Tri::True
} else if a.lo >= b.hi {
Tri::False
} else {
Tri::Unknown
}
}
fn cmp_eq(a: Iv, b: Iv) -> Tri {
if a.lo == a.hi && b.lo == b.hi && a.lo == b.lo {
Tri::True
} else if a.hi < b.lo || b.hi < a.lo {
Tri::False } else {
Tri::Unknown
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SymVerdict {
Proven,
Refuted { witness: u64 },
Unknown,
}
pub fn prove_forall(domain: Iv, prop: &Prop) -> SymVerdict {
match prop.eval_iv(domain) {
Tri::True => SymVerdict::Proven,
Tri::False => {
if !prop.eval_at(domain.lo) {
SymVerdict::Refuted { witness: domain.lo }
} else {
probe_witnesses(domain, prop)
}
}
Tri::Unknown => probe_witnesses(domain, prop),
}
}
fn probe_witnesses(domain: Iv, prop: &Prop) -> SymVerdict {
let mut candidates = [
domain.lo,
domain.hi,
domain.lo.wrapping_add(1),
domain.hi.wrapping_sub(1),
domain
.lo
.wrapping_add((domain.hi.wrapping_sub(domain.lo)) / 2),
0,
u64::MAX,
1,
u64::MAX >> 1,
1u64 << 63,
];
for w in candidates.iter_mut() {
if domain.contains(*w) && !prop.eval_at(*w) {
return SymVerdict::Refuted { witness: *w };
}
}
SymVerdict::Unknown
}