Skip to main content

oxilean_std/eq/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::env_builder::*;
6use oxilean_kernel::{Declaration, Environment, Expr, Level, Name};
7
8use super::functions::*;
9use std::fmt;
10
11/// A database of known equalities, used during elaboration.
12///
13/// This stores equalities as `(lhs_name, rhs_name, proof_term)` triples,
14/// allowing the elaborator to look up existing proofs of equality rather than
15/// re-deriving them.
16#[derive(Debug, Clone, Default)]
17pub struct EqualityDatabase {
18    entries: Vec<(Name, Name, Expr)>,
19}
20impl EqualityDatabase {
21    /// Create an empty database.
22    pub fn new() -> Self {
23        Self::default()
24    }
25    /// Register an equality `lhs = rhs` with the given proof term.
26    pub fn register(&mut self, lhs: Name, rhs: Name, proof: Expr) {
27        self.entries.push((lhs, rhs, proof));
28    }
29    /// Look up a proof of `lhs = rhs`.
30    ///
31    /// Returns the stored proof term if found, searching also the symmetric
32    /// direction and wrapping with `Eq.symm` as needed.
33    pub fn lookup(&self, lhs: &Name, rhs: &Name) -> Option<Expr> {
34        for (l, r, p) in &self.entries {
35            if l == lhs && r == rhs {
36                return Some(p.clone());
37            }
38            if l == rhs && r == lhs {
39                return Some(app(
40                    app(app(app(var("Eq.symm"), var("_")), var("_")), var("_")),
41                    p.clone(),
42                ));
43            }
44        }
45        None
46    }
47    /// Look up a transitive equality `a = c` via `a = b` and `b = c`.
48    pub fn lookup_trans(&self, a: &Name, c: &Name) -> Option<Expr> {
49        for (l1, r1, p1) in &self.entries {
50            if l1 == a {
51                for (l2, r2, p2) in &self.entries {
52                    if l2 == r1 && r2 == c {
53                        return Some(eq_trans(
54                            var("_"),
55                            var(l1.to_string().as_str()),
56                            var(r1.to_string().as_str()),
57                            var(r2.to_string().as_str()),
58                            p1.clone(),
59                            p2.clone(),
60                        ));
61                    }
62                }
63            }
64        }
65        None
66    }
67    /// Total number of registered equalities.
68    pub fn len(&self) -> usize {
69        self.entries.len()
70    }
71    /// Returns `true` if no equalities are registered.
72    pub fn is_empty(&self) -> bool {
73        self.entries.is_empty()
74    }
75    /// Iterate over all registered equality triples.
76    pub fn iter(&self) -> impl Iterator<Item = &(Name, Name, Expr)> {
77        self.entries.iter()
78    }
79    /// Clear all registered equalities.
80    pub fn clear(&mut self) {
81        self.entries.clear();
82    }
83}
84/// A setoid morphism: a function respecting a setoid equivalence.
85#[allow(dead_code)]
86pub struct SetoidMorphism<A, B> {
87    /// The underlying function.
88    pub func: fn(A) -> B,
89    /// Predicate asserting `f` respects the source equivalence.
90    pub respects: fn(&A, &A, bool) -> bool,
91}
92#[allow(dead_code)]
93impl<A: Clone, B> SetoidMorphism<A, B> {
94    /// Create a new setoid morphism.
95    pub fn new(func: fn(A) -> B, respects: fn(&A, &A, bool) -> bool) -> Self {
96        Self { func, respects }
97    }
98    /// Apply the morphism.
99    pub fn apply(&self, a: A) -> B {
100        (self.func)(a)
101    }
102    /// Verify this morphism respects equivalence for a pair.
103    pub fn verify_respects(&self, a1: &A, a2: &A, are_equiv: bool) -> bool {
104        (self.respects)(a1, a2, are_equiv)
105    }
106}
107/// An equality-based rewrite rule.
108#[derive(Debug, Clone)]
109pub struct EqRewriteRule {
110    /// The name of this rule.
111    pub name: oxilean_kernel::Name,
112    /// Left-hand side pattern.
113    pub lhs: oxilean_kernel::Expr,
114    /// Right-hand side replacement.
115    pub rhs: oxilean_kernel::Expr,
116    /// Whether this rule can be applied in reverse.
117    pub reversible: bool,
118}
119impl EqRewriteRule {
120    /// Create a new rewrite rule.
121    pub fn new(
122        name: oxilean_kernel::Name,
123        lhs: oxilean_kernel::Expr,
124        rhs: oxilean_kernel::Expr,
125    ) -> Self {
126        Self {
127            name,
128            lhs,
129            rhs,
130            reversible: false,
131        }
132    }
133    /// Mark the rule as reversible.
134    pub fn make_reversible(mut self) -> Self {
135        self.reversible = true;
136        self
137    }
138    /// The reversed rule (rhs → lhs).
139    pub fn reversed(&self) -> Option<Self> {
140        if self.reversible {
141            Some(Self {
142                name: self.name.clone(),
143                lhs: self.rhs.clone(),
144                rhs: self.lhs.clone(),
145                reversible: true,
146            })
147        } else {
148            None
149        }
150    }
151    /// Check if the rule matches a given expression (syntactically).
152    pub fn matches(&self, expr: &oxilean_kernel::Expr) -> bool {
153        &self.lhs == expr
154    }
155    /// Apply the rule to an expression, returning the rewritten form.
156    pub fn apply(&self, expr: &oxilean_kernel::Expr) -> Option<oxilean_kernel::Expr> {
157        if self.matches(expr) {
158            Some(self.rhs.clone())
159        } else {
160            None
161        }
162    }
163}
164/// A setoid: a Rust type equipped with an explicit equivalence relation.
165///
166/// Mirrors the Lean 4 `Setoid` typeclass for use at the meta level.
167#[allow(dead_code)]
168pub struct SetoidInstance<T> {
169    /// A representative finite sample of carrier elements.
170    pub carrier: Vec<T>,
171    /// The equivalence relation: `equiv(a, b)` iff `a ~ b`.
172    pub equiv: fn(&T, &T) -> bool,
173}
174#[allow(dead_code)]
175impl<T> SetoidInstance<T> {
176    /// Create a new setoid from a carrier sample and an equivalence function.
177    pub fn new(carrier: Vec<T>, equiv: fn(&T, &T) -> bool) -> Self {
178        Self { carrier, equiv }
179    }
180    /// Check whether `a` and `b` are equivalent.
181    pub fn are_equiv(&self, a: &T, b: &T) -> bool {
182        (self.equiv)(a, b)
183    }
184    /// Verify reflexivity for all carrier elements.
185    pub fn check_refl(&self) -> bool {
186        self.carrier.iter().all(|a| (self.equiv)(a, a))
187    }
188    /// Verify symmetry for all pairs in the carrier.
189    pub fn check_symm(&self) -> bool {
190        for a in &self.carrier {
191            for b in &self.carrier {
192                if (self.equiv)(a, b) && !(self.equiv)(b, a) {
193                    return false;
194                }
195            }
196        }
197        true
198    }
199    /// Verify transitivity for all triples in the carrier.
200    pub fn check_trans(&self) -> bool {
201        for a in &self.carrier {
202            for b in &self.carrier {
203                for c in &self.carrier {
204                    if (self.equiv)(a, b) && (self.equiv)(b, c) && !(self.equiv)(a, c) {
205                        return false;
206                    }
207                }
208            }
209        }
210        true
211    }
212}
213/// A chain of propositional equalities `a₀ = a₁ = … = aₙ`.
214///
215/// Useful for representing `calc`-block-style equality chains during
216/// elaboration.
217#[derive(Debug, Clone)]
218pub struct EqChain {
219    /// The type of all elements.
220    pub ty: Expr,
221    /// The sequence of steps; `steps[i].rhs == steps[i+1].lhs`.
222    pub steps: Vec<PropEq>,
223}
224impl EqChain {
225    /// Create an empty chain for a given type.
226    pub fn new(ty: Expr) -> Self {
227        Self {
228            ty,
229            steps: Vec::new(),
230        }
231    }
232    /// Extend the chain with a new equality step.
233    ///
234    /// Panics if the step's lhs does not match the last rhs.
235    pub fn push(&mut self, step: PropEq) {
236        if let Some(last) = self.steps.last() {
237            assert_eq!(last.rhs, step.lhs, "equality chain is not connected");
238        }
239        self.steps.push(step);
240    }
241    /// Collapse the chain to a single equality (if non-empty).
242    pub fn collapse(self) -> Option<PropEq> {
243        let mut iter = self.steps.into_iter();
244        let first = iter.next()?;
245        iter.try_fold(first, |acc, step| acc.trans(step))
246    }
247    /// Returns `true` if the chain is empty.
248    pub fn is_empty(&self) -> bool {
249        self.steps.is_empty()
250    }
251    /// Number of steps in the chain.
252    pub fn len(&self) -> usize {
253        self.steps.len()
254    }
255}
256/// The result of a decidable proposition.
257///
258/// This is the Rust-level analogue of Lean 4's `Decidable` type, capturing
259/// whether a proposition holds along with the computational evidence.
260#[derive(Debug, Clone, PartialEq, Eq)]
261pub enum DecisionResult<P> {
262    /// The proposition holds with proof `p`.
263    IsTrue(P),
264    /// The proposition does not hold with proof of negation.
265    IsFalse(String),
266}
267impl<P> DecisionResult<P> {
268    /// Returns `true` if the proposition holds.
269    pub fn is_true(&self) -> bool {
270        matches!(self, DecisionResult::IsTrue(_))
271    }
272    /// Returns `true` if the proposition does not hold.
273    pub fn is_false(&self) -> bool {
274        matches!(self, DecisionResult::IsFalse(_))
275    }
276    /// Convert to `Option`, keeping the proof if present.
277    pub fn into_option(self) -> Option<P> {
278        match self {
279            DecisionResult::IsTrue(p) => Some(p),
280            DecisionResult::IsFalse(_) => None,
281        }
282    }
283    /// Map the positive proof.
284    pub fn map<Q>(self, f: impl FnOnce(P) -> Q) -> DecisionResult<Q> {
285        match self {
286            DecisionResult::IsTrue(p) => DecisionResult::IsTrue(f(p)),
287            DecisionResult::IsFalse(msg) => DecisionResult::IsFalse(msg),
288        }
289    }
290    /// Combine two positive decisions with `And`.
291    pub fn and<Q>(self, other: DecisionResult<Q>) -> DecisionResult<(P, Q)> {
292        match (self, other) {
293            (DecisionResult::IsTrue(p), DecisionResult::IsTrue(q)) => {
294                DecisionResult::IsTrue((p, q))
295            }
296            (DecisionResult::IsFalse(msg), _) | (_, DecisionResult::IsFalse(msg)) => {
297                DecisionResult::IsFalse(msg)
298            }
299        }
300    }
301    /// Combine two decisions with `Or` (left bias).
302    pub fn or(self, other: DecisionResult<P>) -> DecisionResult<P> {
303        match self {
304            DecisionResult::IsTrue(_) => self,
305            DecisionResult::IsFalse(_) => other,
306        }
307    }
308}
309impl<P: std::fmt::Display> DecisionResult<P> {
310    /// Display the decision result as a human-readable string.
311    pub fn display(&self) -> String {
312        match self {
313            DecisionResult::IsTrue(p) => format!("isTrue ({p})"),
314            DecisionResult::IsFalse(msg) => format!("isFalse ({msg})"),
315        }
316    }
317}
318/// A Leibniz equality coercion at the Rust meta level.
319///
320/// `LeibnizEq<A, B>` encodes a coercion `A → B` witnessing type equality.
321#[allow(dead_code)]
322pub struct LeibnizEq<A, B> {
323    /// The coercion function.
324    pub coerce: fn(A) -> B,
325}
326#[allow(dead_code)]
327impl<T> LeibnizEq<T, T> {
328    /// Construct a trivial `LeibnizEq` by reflexivity (identity coercion).
329    pub fn refl() -> Self {
330        Self { coerce: |x| x }
331    }
332    /// Apply the coercion.
333    pub fn apply(&self, a: T) -> T {
334        (self.coerce)(a)
335    }
336}
337/// A witness that two Rust values are equal.
338///
339/// Constructed only when the equality holds, providing a type-indexed proof
340/// analogous to Lean 4's `Eq.refl`.
341#[derive(Debug, Clone, PartialEq, Eq)]
342pub struct EqualityWitness<T> {
343    /// The value for which equality was witnessed.
344    pub value: T,
345}
346impl<T: PartialEq + Clone> EqualityWitness<T> {
347    /// Attempt to construct an `EqualityWitness` for `a == b`.
348    ///
349    /// Returns `Some` only when `a == b`.
350    pub fn try_new(a: &T, b: &T) -> Option<Self> {
351        if a == b {
352            Some(EqualityWitness { value: a.clone() })
353        } else {
354            None
355        }
356    }
357    /// Use the witness to cast one reference to another.
358    ///
359    /// Because the witness guarantees equality, we can use `a` as `b`.
360    pub fn cast<'a>(&self, a: &'a T) -> &'a T {
361        a
362    }
363}
364/// Propositional equality record, carrying the type and both sides.
365///
366/// Used to represent equality propositions `a = b : α` as data structures
367/// during elaboration and pretty-printing.
368#[derive(Debug, Clone, PartialEq, Eq)]
369pub struct PropEq {
370    /// The common type of both sides.
371    pub ty: Expr,
372    /// The left-hand side.
373    pub lhs: Expr,
374    /// The right-hand side.
375    pub rhs: Expr,
376}
377impl PropEq {
378    /// Construct a new propositional equality.
379    pub fn new(ty: Expr, lhs: Expr, rhs: Expr) -> Self {
380        Self { ty, lhs, rhs }
381    }
382    /// Construct the reflexivity equality `a = a`.
383    pub fn refl(ty: Expr, a: Expr) -> Self {
384        Self::new(ty, a.clone(), a)
385    }
386    /// Returns `true` if this is a reflexivity equality (`lhs == rhs`).
387    pub fn is_refl(&self) -> bool {
388        self.lhs == self.rhs
389    }
390    /// Swap sides: `a = b` becomes `b = a`.
391    pub fn symm(self) -> Self {
392        Self::new(self.ty, self.rhs, self.lhs)
393    }
394    /// Chain two equalities: `a = b` and `b = c` become `a = c`.
395    ///
396    /// Returns `None` if the rhs of `self` differs from the lhs of `other`.
397    pub fn trans(self, other: Self) -> Option<Self> {
398        if self.rhs == other.lhs && self.ty == other.ty {
399            Some(Self::new(self.ty, self.lhs, other.rhs))
400        } else {
401            None
402        }
403    }
404    /// Display as a human-readable string.
405    pub fn display(&self) -> String {
406        format!("{:?} = {:?}", self.lhs, self.rhs)
407    }
408}
409/// A decidable equality instance wrapper for a specific Rust type.
410#[allow(dead_code)]
411pub struct DecidableEqInstance<T> {
412    /// The name of the type.
413    pub type_name: &'static str,
414    /// The decision procedure.
415    pub decide: fn(&T, &T) -> DecisionResult<()>,
416}
417#[allow(dead_code)]
418impl<T: PartialEq> DecidableEqInstance<T> {
419    /// Construct a `DecidableEqInstance` for any `PartialEq` type.
420    pub fn for_type(type_name: &'static str) -> Self {
421        Self {
422            type_name,
423            decide: |a, b| {
424                if a == b {
425                    DecisionResult::IsTrue(())
426                } else {
427                    DecisionResult::IsFalse("values differ".to_string())
428                }
429            },
430        }
431    }
432    /// Apply the decision procedure.
433    pub fn decide_eq(&self, a: &T, b: &T) -> DecisionResult<()> {
434        (self.decide)(a, b)
435    }
436    /// Return `true` iff `a == b`.
437    pub fn is_eq(&self, a: &T, b: &T) -> bool {
438        self.decide_eq(a, b).is_true()
439    }
440}
441/// A builder for equality proofs chained via transitivity.
442#[derive(Debug, Clone)]
443pub struct EqBuilder {
444    ty: oxilean_kernel::Expr,
445    current: oxilean_kernel::Expr,
446    steps: Vec<(oxilean_kernel::Expr, oxilean_kernel::Expr)>,
447}
448impl EqBuilder {
449    /// Start from an expression.
450    pub fn start(ty: oxilean_kernel::Expr, start: oxilean_kernel::Expr) -> Self {
451        Self {
452            ty,
453            current: start,
454            steps: Vec::new(),
455        }
456    }
457    /// Add a step `current = next` with the given proof.
458    pub fn step(mut self, next: oxilean_kernel::Expr, proof: oxilean_kernel::Expr) -> Self {
459        self.steps.push((next.clone(), proof));
460        self.current = next;
461        self
462    }
463    /// Build the combined propositional equality chain.
464    pub fn build(self) -> Option<PropEq> {
465        if self.steps.is_empty() {
466            return None;
467        }
468        let first_rhs = self.steps[0].0.clone();
469        let mut chain = PropEq::new(self.ty.clone(), self.current.clone(), first_rhs);
470        for (rhs, _proof) in self.steps.into_iter().skip(1) {
471            let next_eq = PropEq::new(self.ty.clone(), chain.rhs.clone(), rhs);
472            chain = chain.trans(next_eq)?;
473        }
474        Some(chain)
475    }
476    /// Current expression.
477    pub fn current(&self) -> &oxilean_kernel::Expr {
478        &self.current
479    }
480    /// Number of steps.
481    pub fn num_steps(&self) -> usize {
482        self.steps.len()
483    }
484}
485/// A database of equality rewrite rules.
486#[derive(Debug, Clone, Default)]
487pub struct RewriteRuleDb {
488    rules: Vec<EqRewriteRule>,
489}
490impl RewriteRuleDb {
491    /// Create an empty database.
492    pub fn new() -> Self {
493        Self::default()
494    }
495    /// Add a rule.
496    pub fn add(&mut self, rule: EqRewriteRule) {
497        self.rules.push(rule);
498    }
499    /// Find the first rule that matches `expr`.
500    pub fn find_match(&self, expr: &oxilean_kernel::Expr) -> Option<&EqRewriteRule> {
501        self.rules.iter().find(|r| r.matches(expr))
502    }
503    /// Apply all matching rules once (returns all results).
504    pub fn apply_all(&self, expr: &oxilean_kernel::Expr) -> Vec<oxilean_kernel::Expr> {
505        self.rules.iter().filter_map(|r| r.apply(expr)).collect()
506    }
507    /// Number of rules.
508    pub fn len(&self) -> usize {
509        self.rules.len()
510    }
511    /// Check if empty.
512    pub fn is_empty(&self) -> bool {
513        self.rules.is_empty()
514    }
515    /// Remove a rule by name.
516    pub fn remove(&mut self, name: &oxilean_kernel::Name) {
517        self.rules.retain(|r| &r.name != name);
518    }
519}
520/// A heterogeneous equality witness between values of potentially different types.
521#[allow(dead_code)]
522pub struct HeterogeneousEq<A, B> {
523    /// The left-hand value.
524    pub left: A,
525    /// The right-hand value.
526    pub right: B,
527    /// A human-readable justification.
528    pub justification: &'static str,
529}
530#[allow(dead_code)]
531impl<A, B> HeterogeneousEq<A, B> {
532    /// Construct a heterogeneous equality witness.
533    pub fn new(left: A, right: B, justification: &'static str) -> Self {
534        Self {
535            left,
536            right,
537            justification,
538        }
539    }
540}
541#[allow(dead_code)]
542impl<A: Clone + PartialEq> HeterogeneousEq<A, A> {
543    /// When both sides have the same type, check propositional equality.
544    pub fn is_homogeneous_eq(&self) -> bool {
545        self.left == self.right
546    }
547    /// Extract a homogeneous `EqualityWitness` if values are equal.
548    pub fn to_homogeneous(&self) -> Option<EqualityWitness<A>> {
549        EqualityWitness::try_new(&self.left, &self.right)
550    }
551}