Skip to main content

alkahest_cas/kernel/
expr.rs

1use crate::kernel::domain::Domain;
2use std::fmt;
3use std::hash::{Hash, Hasher};
4
5/// Opaque index into an [`crate::kernel::ExprPool`]. `Copy` — expressions are values, not owned objects.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
7pub struct ExprId(pub(crate) u32);
8
9// ---------------------------------------------------------------------------
10// Atom wrappers — rug types lack Hash, so we provide it via string encoding.
11// String encoding is O(n·log n) but correct and simple for v0.1.
12// ---------------------------------------------------------------------------
13
14/// Arbitrary-precision integer atom.
15#[derive(Debug, Clone)]
16pub struct BigInt(pub rug::Integer);
17
18impl PartialEq for BigInt {
19    fn eq(&self, other: &Self) -> bool {
20        self.0 == other.0
21    }
22}
23impl Eq for BigInt {}
24
25impl Hash for BigInt {
26    fn hash<H: Hasher>(&self, state: &mut H) {
27        self.0.to_string_radix(16).hash(state);
28    }
29}
30
31impl fmt::Display for BigInt {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        write!(f, "{}", self.0)
34    }
35}
36
37/// Arbitrary-precision rational atom. Stored in canonical reduced form by rug.
38#[derive(Debug, Clone)]
39pub struct BigRat(pub rug::Rational);
40
41impl PartialEq for BigRat {
42    fn eq(&self, other: &Self) -> bool {
43        self.0 == other.0
44    }
45}
46impl Eq for BigRat {}
47
48impl Hash for BigRat {
49    fn hash<H: Hasher>(&self, state: &mut H) {
50        self.0.numer().to_string_radix(16).hash(state);
51        self.0.denom().to_string_radix(16).hash(state);
52    }
53}
54
55impl fmt::Display for BigRat {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        if *self.0.denom() == 1 {
58            write!(f, "{}", self.0.numer())
59        } else {
60            write!(f, "{}/{}", self.0.numer(), self.0.denom())
61        }
62    }
63}
64
65/// Arbitrary-precision floating-point atom. `prec` (precision in bits) is
66/// part of structural identity: `Float(1.0, 53) != Float(1.0, 64)`.
67#[derive(Debug, Clone)]
68pub struct BigFloat {
69    pub inner: rug::Float,
70    pub prec: u32,
71}
72
73impl PartialEq for BigFloat {
74    fn eq(&self, other: &Self) -> bool {
75        if self.prec != other.prec {
76            return false;
77        }
78        match (self.inner.is_nan(), other.inner.is_nan()) {
79            (true, true) => true,
80            (false, false) => self.inner == other.inner,
81            _ => false,
82        }
83    }
84}
85impl Eq for BigFloat {}
86
87impl Hash for BigFloat {
88    fn hash<H: Hasher>(&self, state: &mut H) {
89        self.prec.hash(state);
90        if self.inner.is_nan() {
91            "nan".hash(state);
92        } else {
93            // Exact hex representation; None → enough digits for exact round-trip.
94            self.inner.to_string_radix(16, None).hash(state);
95        }
96    }
97}
98
99impl fmt::Display for BigFloat {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        write!(f, "{}", self.inner)
102    }
103}
104
105// ---------------------------------------------------------------------------
106// Predicate — symbolic boolean conditions for Piecewise expressions
107// ---------------------------------------------------------------------------
108
109/// Kind of a symbolic predicate.
110///
111/// Predicates are stored in the intern table as
112/// `ExprData::Predicate { kind, args }`.  The `args` field holds the
113/// operands as `ExprId` nodes.
114///
115/// | Kind | Arity | Meaning |
116/// |------|-------|---------|
117/// | `Lt` | 2     | `args[0]` < `args[1]` |
118/// | `Le` | 2     | `args[0]` ≤ `args[1]` |
119/// | `Gt` | 2     | `args[0]` > `args[1]` |
120/// | `Ge` | 2     | `args[0]` ≥ `args[1]` |
121/// | `Eq` | 2     | `args[0]` = `args[1]` (symbolic equality) |
122/// | `Ne` | 2     | `args[0]` ≠ `args[1]` |
123/// | `And`| n     | conjunction of n predicate ExprIds |
124/// | `Or` | n     | disjunction of n predicate ExprIds |
125/// | `Not`| 1     | negation |
126/// | `True` | 0   | always-true |
127/// | `False`| 0   | always-false |
128#[derive(Debug, Clone, PartialEq, Eq, Hash)]
129pub enum PredicateKind {
130    Lt,
131    Le,
132    Gt,
133    Ge,
134    Eq,
135    Ne,
136    And,
137    Or,
138    Not,
139    True,
140    False,
141}
142
143impl fmt::Display for PredicateKind {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        let s = match self {
146            PredicateKind::Lt => "<",
147            PredicateKind::Le => "≤",
148            PredicateKind::Gt => ">",
149            PredicateKind::Ge => "≥",
150            PredicateKind::Eq => "=",
151            PredicateKind::Ne => "≠",
152            PredicateKind::And => "∧",
153            PredicateKind::Or => "∨",
154            PredicateKind::Not => "¬",
155            PredicateKind::True => "True",
156            PredicateKind::False => "False",
157        };
158        write!(f, "{s}")
159    }
160}
161
162// ---------------------------------------------------------------------------
163// Expression data — the structural content stored in the intern table.
164// ---------------------------------------------------------------------------
165
166/// Structural content of an expression node.
167///
168/// All compound nodes hold [`ExprId`] children, not owned sub-trees.
169/// This keeps `ExprData` small and allows sharing via the intern table.
170#[derive(Debug, Clone, PartialEq, Eq, Hash)]
171pub enum ExprData {
172    // Atoms
173    Symbol {
174        name: String,
175        domain: Domain,
176        /// When `false`, this generator does not commute under multiplication; see V3-2.
177        commutative: bool,
178    },
179    Integer(BigInt),
180    Rational(BigRat),
181    Float(BigFloat),
182    // Compound (n-ary for Add/Mul; binary for Pow; variadic for Func)
183    Add(Vec<ExprId>),
184    Mul(Vec<ExprId>),
185    Pow {
186        base: ExprId,
187        exp: ExprId,
188    },
189    Func {
190        name: String,
191        args: Vec<ExprId>,
192    },
193    // PA-9 — symbolic conditionals
194    /// A piecewise expression: evaluates to `value_i` when `cond_i` holds,
195    /// and to `default` when no condition matches.
196    ///
197    /// Conditions are `ExprData::Predicate` nodes stored in the pool.
198    /// Branches are tried in order; the first matching condition wins.
199    Piecewise {
200        branches: Vec<(ExprId /* cond */, ExprId /* value */)>,
201        default: ExprId,
202    },
203    /// A symbolic predicate (boolean condition over symbolic reals).
204    Predicate {
205        kind: PredicateKind,
206        args: Vec<ExprId>,
207    },
208    /// Universal quantification (`∀ var . body`).  Used by first-order logic (V3-3).
209    Forall {
210        var: ExprId,
211        body: ExprId,
212    },
213    /// Existential quantification (`∃ var . body`).
214    Exists {
215        var: ExprId,
216        body: ExprId,
217    },
218    /// Landau big-O remainder: `O(arg)` as a symbolic order bound (V2-15 series API).
219    BigO(ExprId),
220}