use crate::env_builder::*;
use oxilean_kernel::{Declaration, Environment, Expr, Level, Name};
use super::functions::*;
use std::fmt;
#[derive(Debug, Clone, Default)]
pub struct EqualityDatabase {
entries: Vec<(Name, Name, Expr)>,
}
impl EqualityDatabase {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, lhs: Name, rhs: Name, proof: Expr) {
self.entries.push((lhs, rhs, proof));
}
pub fn lookup(&self, lhs: &Name, rhs: &Name) -> Option<Expr> {
for (l, r, p) in &self.entries {
if l == lhs && r == rhs {
return Some(p.clone());
}
if l == rhs && r == lhs {
return Some(app(
app(app(app(var("Eq.symm"), var("_")), var("_")), var("_")),
p.clone(),
));
}
}
None
}
pub fn lookup_trans(&self, a: &Name, c: &Name) -> Option<Expr> {
for (l1, r1, p1) in &self.entries {
if l1 == a {
for (l2, r2, p2) in &self.entries {
if l2 == r1 && r2 == c {
return Some(eq_trans(
var("_"),
var(l1.to_string().as_str()),
var(r1.to_string().as_str()),
var(r2.to_string().as_str()),
p1.clone(),
p2.clone(),
));
}
}
}
}
None
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &(Name, Name, Expr)> {
self.entries.iter()
}
pub fn clear(&mut self) {
self.entries.clear();
}
}
#[allow(dead_code)]
pub struct SetoidMorphism<A, B> {
pub func: fn(A) -> B,
pub respects: fn(&A, &A, bool) -> bool,
}
#[allow(dead_code)]
impl<A: Clone, B> SetoidMorphism<A, B> {
pub fn new(func: fn(A) -> B, respects: fn(&A, &A, bool) -> bool) -> Self {
Self { func, respects }
}
pub fn apply(&self, a: A) -> B {
(self.func)(a)
}
pub fn verify_respects(&self, a1: &A, a2: &A, are_equiv: bool) -> bool {
(self.respects)(a1, a2, are_equiv)
}
}
#[derive(Debug, Clone)]
pub struct EqRewriteRule {
pub name: oxilean_kernel::Name,
pub lhs: oxilean_kernel::Expr,
pub rhs: oxilean_kernel::Expr,
pub reversible: bool,
}
impl EqRewriteRule {
pub fn new(
name: oxilean_kernel::Name,
lhs: oxilean_kernel::Expr,
rhs: oxilean_kernel::Expr,
) -> Self {
Self {
name,
lhs,
rhs,
reversible: false,
}
}
pub fn make_reversible(mut self) -> Self {
self.reversible = true;
self
}
pub fn reversed(&self) -> Option<Self> {
if self.reversible {
Some(Self {
name: self.name.clone(),
lhs: self.rhs.clone(),
rhs: self.lhs.clone(),
reversible: true,
})
} else {
None
}
}
pub fn matches(&self, expr: &oxilean_kernel::Expr) -> bool {
&self.lhs == expr
}
pub fn apply(&self, expr: &oxilean_kernel::Expr) -> Option<oxilean_kernel::Expr> {
if self.matches(expr) {
Some(self.rhs.clone())
} else {
None
}
}
}
#[allow(dead_code)]
pub struct SetoidInstance<T> {
pub carrier: Vec<T>,
pub equiv: fn(&T, &T) -> bool,
}
#[allow(dead_code)]
impl<T> SetoidInstance<T> {
pub fn new(carrier: Vec<T>, equiv: fn(&T, &T) -> bool) -> Self {
Self { carrier, equiv }
}
pub fn are_equiv(&self, a: &T, b: &T) -> bool {
(self.equiv)(a, b)
}
pub fn check_refl(&self) -> bool {
self.carrier.iter().all(|a| (self.equiv)(a, a))
}
pub fn check_symm(&self) -> bool {
for a in &self.carrier {
for b in &self.carrier {
if (self.equiv)(a, b) && !(self.equiv)(b, a) {
return false;
}
}
}
true
}
pub fn check_trans(&self) -> bool {
for a in &self.carrier {
for b in &self.carrier {
for c in &self.carrier {
if (self.equiv)(a, b) && (self.equiv)(b, c) && !(self.equiv)(a, c) {
return false;
}
}
}
}
true
}
}
#[derive(Debug, Clone)]
pub struct EqChain {
pub ty: Expr,
pub steps: Vec<PropEq>,
}
impl EqChain {
pub fn new(ty: Expr) -> Self {
Self {
ty,
steps: Vec::new(),
}
}
pub fn push(&mut self, step: PropEq) {
if let Some(last) = self.steps.last() {
assert_eq!(last.rhs, step.lhs, "equality chain is not connected");
}
self.steps.push(step);
}
pub fn collapse(self) -> Option<PropEq> {
let mut iter = self.steps.into_iter();
let first = iter.next()?;
iter.try_fold(first, |acc, step| acc.trans(step))
}
pub fn is_empty(&self) -> bool {
self.steps.is_empty()
}
pub fn len(&self) -> usize {
self.steps.len()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DecisionResult<P> {
IsTrue(P),
IsFalse(String),
}
impl<P> DecisionResult<P> {
pub fn is_true(&self) -> bool {
matches!(self, DecisionResult::IsTrue(_))
}
pub fn is_false(&self) -> bool {
matches!(self, DecisionResult::IsFalse(_))
}
pub fn into_option(self) -> Option<P> {
match self {
DecisionResult::IsTrue(p) => Some(p),
DecisionResult::IsFalse(_) => None,
}
}
pub fn map<Q>(self, f: impl FnOnce(P) -> Q) -> DecisionResult<Q> {
match self {
DecisionResult::IsTrue(p) => DecisionResult::IsTrue(f(p)),
DecisionResult::IsFalse(msg) => DecisionResult::IsFalse(msg),
}
}
pub fn and<Q>(self, other: DecisionResult<Q>) -> DecisionResult<(P, Q)> {
match (self, other) {
(DecisionResult::IsTrue(p), DecisionResult::IsTrue(q)) => {
DecisionResult::IsTrue((p, q))
}
(DecisionResult::IsFalse(msg), _) | (_, DecisionResult::IsFalse(msg)) => {
DecisionResult::IsFalse(msg)
}
}
}
pub fn or(self, other: DecisionResult<P>) -> DecisionResult<P> {
match self {
DecisionResult::IsTrue(_) => self,
DecisionResult::IsFalse(_) => other,
}
}
}
impl<P: std::fmt::Display> DecisionResult<P> {
pub fn display(&self) -> String {
match self {
DecisionResult::IsTrue(p) => format!("isTrue ({p})"),
DecisionResult::IsFalse(msg) => format!("isFalse ({msg})"),
}
}
}
#[allow(dead_code)]
pub struct LeibnizEq<A, B> {
pub coerce: fn(A) -> B,
}
#[allow(dead_code)]
impl<T> LeibnizEq<T, T> {
pub fn refl() -> Self {
Self { coerce: |x| x }
}
pub fn apply(&self, a: T) -> T {
(self.coerce)(a)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EqualityWitness<T> {
pub value: T,
}
impl<T: PartialEq + Clone> EqualityWitness<T> {
pub fn try_new(a: &T, b: &T) -> Option<Self> {
if a == b {
Some(EqualityWitness { value: a.clone() })
} else {
None
}
}
pub fn cast<'a>(&self, a: &'a T) -> &'a T {
a
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PropEq {
pub ty: Expr,
pub lhs: Expr,
pub rhs: Expr,
}
impl PropEq {
pub fn new(ty: Expr, lhs: Expr, rhs: Expr) -> Self {
Self { ty, lhs, rhs }
}
pub fn refl(ty: Expr, a: Expr) -> Self {
Self::new(ty, a.clone(), a)
}
pub fn is_refl(&self) -> bool {
self.lhs == self.rhs
}
pub fn symm(self) -> Self {
Self::new(self.ty, self.rhs, self.lhs)
}
pub fn trans(self, other: Self) -> Option<Self> {
if self.rhs == other.lhs && self.ty == other.ty {
Some(Self::new(self.ty, self.lhs, other.rhs))
} else {
None
}
}
pub fn display(&self) -> String {
format!("{:?} = {:?}", self.lhs, self.rhs)
}
}
#[allow(dead_code)]
pub struct DecidableEqInstance<T> {
pub type_name: &'static str,
pub decide: fn(&T, &T) -> DecisionResult<()>,
}
#[allow(dead_code)]
impl<T: PartialEq> DecidableEqInstance<T> {
pub fn for_type(type_name: &'static str) -> Self {
Self {
type_name,
decide: |a, b| {
if a == b {
DecisionResult::IsTrue(())
} else {
DecisionResult::IsFalse("values differ".to_string())
}
},
}
}
pub fn decide_eq(&self, a: &T, b: &T) -> DecisionResult<()> {
(self.decide)(a, b)
}
pub fn is_eq(&self, a: &T, b: &T) -> bool {
self.decide_eq(a, b).is_true()
}
}
#[derive(Debug, Clone)]
pub struct EqBuilder {
ty: oxilean_kernel::Expr,
current: oxilean_kernel::Expr,
steps: Vec<(oxilean_kernel::Expr, oxilean_kernel::Expr)>,
}
impl EqBuilder {
pub fn start(ty: oxilean_kernel::Expr, start: oxilean_kernel::Expr) -> Self {
Self {
ty,
current: start,
steps: Vec::new(),
}
}
pub fn step(mut self, next: oxilean_kernel::Expr, proof: oxilean_kernel::Expr) -> Self {
self.steps.push((next.clone(), proof));
self.current = next;
self
}
pub fn build(self) -> Option<PropEq> {
if self.steps.is_empty() {
return None;
}
let first_rhs = self.steps[0].0.clone();
let mut chain = PropEq::new(self.ty.clone(), self.current.clone(), first_rhs);
for (rhs, _proof) in self.steps.into_iter().skip(1) {
let next_eq = PropEq::new(self.ty.clone(), chain.rhs.clone(), rhs);
chain = chain.trans(next_eq)?;
}
Some(chain)
}
pub fn current(&self) -> &oxilean_kernel::Expr {
&self.current
}
pub fn num_steps(&self) -> usize {
self.steps.len()
}
}
#[derive(Debug, Clone, Default)]
pub struct RewriteRuleDb {
rules: Vec<EqRewriteRule>,
}
impl RewriteRuleDb {
pub fn new() -> Self {
Self::default()
}
pub fn add(&mut self, rule: EqRewriteRule) {
self.rules.push(rule);
}
pub fn find_match(&self, expr: &oxilean_kernel::Expr) -> Option<&EqRewriteRule> {
self.rules.iter().find(|r| r.matches(expr))
}
pub fn apply_all(&self, expr: &oxilean_kernel::Expr) -> Vec<oxilean_kernel::Expr> {
self.rules.iter().filter_map(|r| r.apply(expr)).collect()
}
pub fn len(&self) -> usize {
self.rules.len()
}
pub fn is_empty(&self) -> bool {
self.rules.is_empty()
}
pub fn remove(&mut self, name: &oxilean_kernel::Name) {
self.rules.retain(|r| &r.name != name);
}
}
#[allow(dead_code)]
pub struct HeterogeneousEq<A, B> {
pub left: A,
pub right: B,
pub justification: &'static str,
}
#[allow(dead_code)]
impl<A, B> HeterogeneousEq<A, B> {
pub fn new(left: A, right: B, justification: &'static str) -> Self {
Self {
left,
right,
justification,
}
}
}
#[allow(dead_code)]
impl<A: Clone + PartialEq> HeterogeneousEq<A, A> {
pub fn is_homogeneous_eq(&self) -> bool {
self.left == self.right
}
pub fn to_homogeneous(&self) -> Option<EqualityWitness<A>> {
EqualityWitness::try_new(&self.left, &self.right)
}
}