Skip to main content

oxilean_std/quotient_types/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
7
8/// Integers represented as equivalence classes of pairs `(pos, neg)` where
9/// the value is `pos − neg` (as natural numbers).
10#[allow(dead_code)]
11#[derive(Debug, Clone)]
12pub struct IntegerQuotient {
13    /// Representative: (m, n) represents the integer m − n.
14    pub pos: u64,
15    pub neg: u64,
16}
17#[allow(dead_code)]
18impl IntegerQuotient {
19    /// Create from a signed integer value.
20    pub fn from_i64(n: i64) -> Self {
21        if n >= 0 {
22            Self {
23                pos: n as u64,
24                neg: 0,
25            }
26        } else {
27            Self {
28                pos: 0,
29                neg: (-n) as u64,
30            }
31        }
32    }
33    /// Reduce to canonical form (either pos=0 or neg=0).
34    pub fn reduce(&self) -> Self {
35        if self.pos >= self.neg {
36            Self {
37                pos: self.pos - self.neg,
38                neg: 0,
39            }
40        } else {
41            Self {
42                pos: 0,
43                neg: self.neg - self.pos,
44            }
45        }
46    }
47    /// Value as i64.
48    pub fn to_i64(&self) -> i64 {
49        (self.pos as i64) - (self.neg as i64)
50    }
51    /// Addition: (m,n) + (m',n') = (m+m', n+n').
52    pub fn add(&self, other: &Self) -> Self {
53        Self {
54            pos: self.pos + other.pos,
55            neg: self.neg + other.neg,
56        }
57        .reduce()
58    }
59    /// Negation: -(m,n) = (n,m).
60    pub fn negate(&self) -> Self {
61        Self {
62            pos: self.neg,
63            neg: self.pos,
64        }
65    }
66    /// Multiplication: (m,n) * (m',n') = (m*m' + n*n', m*n' + n*m').
67    pub fn mul(&self, other: &Self) -> Self {
68        let pos = self.pos * other.pos + self.neg * other.neg;
69        let neg = self.pos * other.neg + self.neg * other.pos;
70        Self { pos, neg }.reduce()
71    }
72    /// Equivalence: (m,n) ~ (m',n') iff m+n' = m'+n.
73    pub fn equiv(&self, other: &Self) -> bool {
74        self.pos + other.neg == other.pos + self.neg
75    }
76}
77/// A map between quotient types that is well-defined (respects the relation).
78///
79/// Stores the underlying raw function and a proof obligation (checked lazily).
80#[allow(dead_code)]
81#[derive(Debug, Clone)]
82pub struct QuotientMap {
83    /// Name / description of the map.
84    pub name: String,
85    /// Is the map known to be well-defined on equivalence classes?
86    pub well_defined: bool,
87    /// Is the map injective on equivalence classes?
88    pub injective: bool,
89    /// Is the map surjective onto the target quotient?
90    pub surjective: bool,
91}
92#[allow(dead_code)]
93impl QuotientMap {
94    /// Create a new `QuotientMap` descriptor.
95    pub fn new(name: impl Into<String>) -> Self {
96        Self {
97            name: name.into(),
98            well_defined: false,
99            injective: false,
100            surjective: false,
101        }
102    }
103    /// Mark this map as well-defined (proved externally).
104    pub fn mark_well_defined(mut self) -> Self {
105        self.well_defined = true;
106        self
107    }
108    /// Mark this map as injective.
109    pub fn mark_injective(mut self) -> Self {
110        self.injective = true;
111        self
112    }
113    /// Mark this map as surjective.
114    pub fn mark_surjective(mut self) -> Self {
115        self.surjective = true;
116        self
117    }
118    /// Is this map bijective?
119    pub fn is_bijective(&self) -> bool {
120        self.injective && self.surjective
121    }
122    /// Is this map a quotient isomorphism?
123    pub fn is_isomorphism(&self) -> bool {
124        self.well_defined && self.is_bijective()
125    }
126}
127/// A concrete, computationally-checkable setoid over values of type `T`.
128///
129/// The equivalence relation is given by a user-supplied function.
130#[allow(dead_code)]
131pub struct ConcreteSetoid<T> {
132    /// The equivalence predicate.  Must be an equivalence relation.
133    pub equiv: Box<dyn Fn(&T, &T) -> bool>,
134}
135#[allow(dead_code)]
136impl<T: Clone> ConcreteSetoid<T> {
137    /// Create a new setoid with the given equivalence predicate.
138    pub fn new(equiv: impl Fn(&T, &T) -> bool + 'static) -> Self {
139        Self {
140            equiv: Box::new(equiv),
141        }
142    }
143    /// Check if two values are equivalent.
144    pub fn are_equiv(&self, a: &T, b: &T) -> bool {
145        (self.equiv)(a, b)
146    }
147    /// Partition a slice into equivalence classes.
148    pub fn partition(&self, xs: &[T]) -> Vec<Vec<T>> {
149        let mut classes: Vec<Vec<T>> = Vec::new();
150        for x in xs {
151            let mut found = false;
152            for cls in &mut classes {
153                if (self.equiv)(x, &cls[0]) {
154                    cls.push(x.clone());
155                    found = true;
156                    break;
157                }
158            }
159            if !found {
160                classes.push(vec![x.clone()]);
161            }
162        }
163        classes
164    }
165    /// Count the number of equivalence classes in a slice.
166    pub fn count_classes(&self, xs: &[T]) -> usize {
167        self.partition(xs).len()
168    }
169    /// Check that a function `f : T → U` respects the equivalence relation.
170    pub fn function_respects<U: PartialEq>(&self, xs: &[T], f: impl Fn(&T) -> U) -> bool {
171        for i in 0..xs.len() {
172            for j in 0..xs.len() {
173                if (self.equiv)(&xs[i], &xs[j]) && f(&xs[i]) != f(&xs[j]) {
174                    return false;
175                }
176            }
177        }
178        true
179    }
180}
181/// An element of the free group over an alphabet `char`, represented as a
182/// reduced word (alternating generators and their inverses).
183#[allow(dead_code)]
184#[derive(Debug, Clone, PartialEq, Eq)]
185pub struct FreeGroupElement {
186    /// Letters: positive = generator, negative = inverse.
187    /// E.g., `[(b'a', true), (b'b', false)]` means `a * b⁻¹`.
188    pub letters: Vec<(char, bool)>,
189}
190#[allow(dead_code)]
191impl FreeGroupElement {
192    /// The identity element (empty word).
193    pub fn identity() -> Self {
194        Self { letters: vec![] }
195    }
196    /// A single generator.
197    pub fn generator(c: char) -> Self {
198        Self {
199            letters: vec![(c, true)],
200        }
201    }
202    /// The inverse of a single generator.
203    pub fn generator_inv(c: char) -> Self {
204        Self {
205            letters: vec![(c, false)],
206        }
207    }
208    /// Concatenate and reduce (cancel adjacent inverse pairs).
209    pub fn mul(&self, other: &Self) -> Self {
210        let mut result = self.letters.clone();
211        for &letter in &other.letters {
212            if let Some(&last) = result.last() {
213                if last.0 == letter.0 && last.1 != letter.1 {
214                    result.pop();
215                    continue;
216                }
217            }
218            result.push(letter);
219        }
220        Self { letters: result }
221    }
222    /// Inverse: reverse the word and flip all polarities.
223    pub fn inverse(&self) -> Self {
224        let letters = self.letters.iter().rev().map(|&(c, b)| (c, !b)).collect();
225        Self { letters }
226    }
227    /// Length (number of letters in reduced form).
228    pub fn length(&self) -> usize {
229        self.letters.len()
230    }
231    /// Check if this is the identity.
232    pub fn is_identity(&self) -> bool {
233        self.letters.is_empty()
234    }
235}
236/// Rational numbers as equivalence classes of pairs `(numerator, denominator)`.
237/// The equivalence is (p, q) ~ (p', q') iff p * q' = p' * q.
238#[allow(dead_code)]
239#[derive(Debug, Clone)]
240pub struct RationalQuotient {
241    /// Numerator.
242    pub numer: i64,
243    /// Denominator (nonzero).
244    pub denom: i64,
245}
246#[allow(dead_code)]
247impl RationalQuotient {
248    /// Create a new rational, panicking if denom = 0.
249    pub fn new(numer: i64, denom: i64) -> Self {
250        assert!(denom != 0, "denominator must be nonzero");
251        Self { numer, denom }
252    }
253    /// Compute GCD using the Euclidean algorithm.
254    fn gcd(mut a: i64, mut b: i64) -> i64 {
255        a = a.abs();
256        b = b.abs();
257        while b != 0 {
258            let t = b;
259            b = a % b;
260            a = t;
261        }
262        a
263    }
264    /// Reduce to canonical form.
265    pub fn reduce(&self) -> Self {
266        if self.numer == 0 {
267            return Self { numer: 0, denom: 1 };
268        }
269        let g = Self::gcd(self.numer.abs(), self.denom.abs());
270        let sign = if self.denom < 0 { -1 } else { 1 };
271        Self {
272            numer: sign * self.numer / g,
273            denom: sign * self.denom / g,
274        }
275    }
276    /// Equivalence: p*q' = p'*q.
277    pub fn equiv(&self, other: &Self) -> bool {
278        self.numer * other.denom == other.numer * self.denom
279    }
280    /// Addition: p/q + p'/q' = (p*q' + p'*q) / (q*q').
281    pub fn add(&self, other: &Self) -> Self {
282        Self {
283            numer: self.numer * other.denom + other.numer * self.denom,
284            denom: self.denom * other.denom,
285        }
286        .reduce()
287    }
288    /// Multiplication: p/q * p'/q' = (p*p') / (q*q').
289    pub fn mul(&self, other: &Self) -> Self {
290        Self {
291            numer: self.numer * other.numer,
292            denom: self.denom * other.denom,
293        }
294        .reduce()
295    }
296    /// Convert to f64 for display/comparison.
297    pub fn to_f64(&self) -> f64 {
298        (self.numer as f64) / (self.denom as f64)
299    }
300}