Skip to main content

oxilean_std/decidable/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::Decidable;
6use super::functions::*;
7use std::fmt;
8
9/// A decision tagged with a human-readable name for debugging.
10#[derive(Debug, Clone)]
11pub struct NamedDecision {
12    /// The name of the proposition.
13    pub name: String,
14    /// The decision result.
15    pub decision: Decision<()>,
16}
17impl NamedDecision {
18    /// Create a new named decision.
19    pub fn new(name: impl Into<String>, decision: Decision<()>) -> Self {
20        Self {
21            name: name.into(),
22            decision,
23        }
24    }
25    /// Returns `true` if the decision is positive.
26    pub fn is_true(&self) -> bool {
27        self.decision.is_true()
28    }
29    /// Display a summary of the named decision.
30    pub fn summary(&self) -> String {
31        let verdict = if self.is_true() { "✓" } else { "✗" };
32        format!("[{verdict}] {}", self.name)
33    }
34}
35/// Reflection between `bool` and decidable propositions.
36///
37/// Given a `Decision<()>`, `BoolReflect` bridges the computational `Bool`
38/// world and the propositional `Prop` world.
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum BoolReflect {
41    /// The boolean is `true` and the proposition holds.
42    IsTrue,
43    /// The boolean is `false` and the proposition does not hold.
44    IsFalse,
45}
46impl BoolReflect {
47    /// Convert from a `Decision<()>`.
48    pub fn from_decision(d: &Decision<()>) -> Self {
49        if d.is_true() {
50            BoolReflect::IsTrue
51        } else {
52            BoolReflect::IsFalse
53        }
54    }
55    /// Convert to a plain `bool`.
56    pub fn to_bool(&self) -> bool {
57        *self == BoolReflect::IsTrue
58    }
59}
60/// A counter that tracks how many decisions have been made and their outcomes.
61#[derive(Clone, Debug, Default)]
62pub struct DecidableCounter {
63    /// Number of positive decisions.
64    pub true_count: usize,
65    /// Number of negative decisions.
66    pub false_count: usize,
67}
68impl DecidableCounter {
69    /// Create a new counter.
70    pub fn new() -> Self {
71        Self::default()
72    }
73    /// Record a decision.
74    pub fn record(&mut self, d: &Decision<()>) {
75        if d.is_true() {
76            self.true_count += 1;
77        } else {
78            self.false_count += 1;
79        }
80    }
81    /// Total decisions recorded.
82    pub fn total(&self) -> usize {
83        self.true_count + self.false_count
84    }
85    /// Percentage of true decisions (0.0 to 100.0).
86    pub fn true_rate(&self) -> f64 {
87        if self.total() == 0 {
88            0.0
89        } else {
90            (self.true_count as f64 / self.total() as f64) * 100.0
91        }
92    }
93    /// Whether all decisions were positive.
94    pub fn all_true(&self) -> bool {
95        self.false_count == 0 && self.total() > 0
96    }
97    /// Whether all decisions were negative.
98    pub fn all_false(&self) -> bool {
99        self.true_count == 0 && self.total() > 0
100    }
101    /// Reset the counter.
102    pub fn reset(&mut self) {
103        self.true_count = 0;
104        self.false_count = 0;
105    }
106}
107/// A decision about a proposition `P`.
108///
109/// `Decision::IsTrue` carries a witness (proof) that `P` holds.
110/// `Decision::IsFalse` carries an explanation that it does not.
111#[derive(Debug, Clone, PartialEq, Eq)]
112pub enum Decision<P> {
113    /// The proposition holds.
114    IsTrue(P),
115    /// The proposition does not hold; the string is an explanatory message.
116    IsFalse(String),
117}
118impl<P> Decision<P> {
119    /// Returns `true` if the decision is positive.
120    pub fn is_true(&self) -> bool {
121        matches!(self, Decision::IsTrue(_))
122    }
123    /// Returns `true` if the decision is negative.
124    pub fn is_false(&self) -> bool {
125        matches!(self, Decision::IsFalse(_))
126    }
127    /// Unwrap the positive proof, panicking if negative.
128    pub fn unwrap(self) -> P {
129        match self {
130            Decision::IsTrue(p) => p,
131            Decision::IsFalse(msg) => panic!("Decision::unwrap on IsFalse: {msg}"),
132        }
133    }
134    /// Convert to `Option`, dropping the negative message.
135    pub fn into_option(self) -> Option<P> {
136        match self {
137            Decision::IsTrue(p) => Some(p),
138            Decision::IsFalse(_) => None,
139        }
140    }
141    /// Map the positive witness through `f`.
142    pub fn map<Q>(self, f: impl FnOnce(P) -> Q) -> Decision<Q> {
143        match self {
144            Decision::IsTrue(p) => Decision::IsTrue(f(p)),
145            Decision::IsFalse(msg) => Decision::IsFalse(msg),
146        }
147    }
148    /// Combine with another `Decision` via conjunction.
149    pub fn and<Q>(self, other: Decision<Q>) -> Decision<(P, Q)> {
150        match (self, other) {
151            (Decision::IsTrue(p), Decision::IsTrue(q)) => Decision::IsTrue((p, q)),
152            (Decision::IsFalse(m), _) | (_, Decision::IsFalse(m)) => Decision::IsFalse(m),
153        }
154    }
155    /// Combine with another `Decision` via disjunction (left-biased).
156    pub fn or(self, other: Decision<P>) -> Decision<P> {
157        match self {
158            Decision::IsTrue(_) => self,
159            Decision::IsFalse(_) => other,
160        }
161    }
162    /// Negate the decision.
163    pub fn negate(self) -> Decision<String>
164    where
165        P: std::fmt::Debug,
166    {
167        match self {
168            Decision::IsTrue(p) => Decision::IsFalse(format!("expected false but got: {p:?}")),
169            Decision::IsFalse(msg) => Decision::IsTrue(msg),
170        }
171    }
172    /// Apply `f` if true, returning a new decision.
173    pub fn flat_map<Q>(self, f: impl FnOnce(P) -> Decision<Q>) -> Decision<Q> {
174        match self {
175            Decision::IsTrue(p) => f(p),
176            Decision::IsFalse(msg) => Decision::IsFalse(msg),
177        }
178    }
179    /// Return `default` if false.
180    pub fn unwrap_or(self, default: P) -> P {
181        match self {
182            Decision::IsTrue(p) => p,
183            Decision::IsFalse(_) => default,
184        }
185    }
186    /// Apply a fallback function if false.
187    pub fn unwrap_or_else(self, f: impl FnOnce(String) -> P) -> P {
188        match self {
189            Decision::IsTrue(p) => p,
190            Decision::IsFalse(msg) => f(msg),
191        }
192    }
193}
194impl Decision<bool> {
195    /// Convert a simple bool decision to a native `bool`.
196    pub fn to_bool(&self) -> bool {
197        match self {
198            Decision::IsTrue(b) => *b,
199            Decision::IsFalse(_) => false,
200        }
201    }
202}
203/// A sequential chain of decisions where each step can depend on the previous.
204#[derive(Clone, Debug)]
205pub struct DecisionChain {
206    steps: Vec<(String, Decision<()>)>,
207}
208impl DecisionChain {
209    /// Create an empty chain.
210    pub fn new() -> Self {
211        Self { steps: Vec::new() }
212    }
213    /// Add a step to the chain.
214    pub fn step(mut self, name: impl Into<String>, d: Decision<()>) -> Self {
215        self.steps.push((name.into(), d));
216        self
217    }
218    /// Whether all steps passed.
219    pub fn all_passed(&self) -> bool {
220        self.steps.iter().all(|(_, d)| d.is_true())
221    }
222    /// The first failing step, if any.
223    pub fn first_failure(&self) -> Option<&str> {
224        self.steps
225            .iter()
226            .find(|(_, d)| d.is_false())
227            .map(|(name, _)| name.as_str())
228    }
229    /// Number of steps.
230    pub fn len(&self) -> usize {
231        self.steps.len()
232    }
233    /// Whether the chain is empty.
234    pub fn is_empty(&self) -> bool {
235        self.steps.is_empty()
236    }
237    /// Count passing steps.
238    pub fn passed_count(&self) -> usize {
239        self.steps.iter().filter(|(_, d)| d.is_true()).count()
240    }
241    /// Count failing steps.
242    pub fn failed_count(&self) -> usize {
243        self.steps.iter().filter(|(_, d)| d.is_false()).count()
244    }
245}
246/// Decision procedure (DPLL, tableaux, resolution, etc.).
247#[allow(dead_code)]
248pub struct DecisionProcedureExt {
249    /// Name of the procedure
250    procedure_name: String,
251    /// Completeness: if formula is satisfiable, procedure finds it
252    complete: bool,
253    /// Soundness: if procedure says satisfiable, it is
254    sound: bool,
255    /// Complexity class
256    complexity: String,
257}
258/// A negation proof: evidence that `P` does not hold.
259///
260/// Wraps an explanatory message at the Rust meta-level.
261#[derive(Debug, Clone, PartialEq, Eq)]
262pub struct Not<P> {
263    /// Message explaining why `P` is false.
264    pub message: String,
265    _marker: std::marker::PhantomData<P>,
266}
267impl<P> Not<P> {
268    /// Construct a negation proof with the given message.
269    pub fn new(message: impl Into<String>) -> Self {
270        Self {
271            message: message.into(),
272            _marker: std::marker::PhantomData,
273        }
274    }
275    /// Double negation elimination: `¬¬P` is not constructively valid in
276    /// general, but we model it here as just returning the message.
277    pub fn message(&self) -> &str {
278        &self.message
279    }
280}
281/// Extended decidable proposition structure.
282#[allow(dead_code)]
283pub struct DecidablePropExt {
284    /// Name of the proposition
285    name: String,
286    /// Whether this is constructively decidable (witness-carrying)
287    constructive: bool,
288    /// Associated Boolean reflection
289    bool_reflect: Option<bool>,
290}
291/// Decidable membership for a finite set.
292#[allow(dead_code)]
293pub struct DecidableSetExt<T: Eq> {
294    elements: Vec<T>,
295    /// Decidable equality on T used for membership
296    dec_eq: std::marker::PhantomData<T>,
297}
298/// A decidable equality check wrapper.
299#[derive(Clone, Debug)]
300pub struct EqDecision<T> {
301    pub(super) lhs: T,
302    pub(super) rhs: T,
303}
304impl<T: PartialEq + std::fmt::Debug> EqDecision<T> {
305    /// Create a new equality decision.
306    pub fn new(lhs: T, rhs: T) -> Self {
307        Self { lhs, rhs }
308    }
309}
310/// A finite set with decidable membership.
311///
312/// Backed by a `Vec<T>` with no duplicates (in insertion order).
313#[derive(Debug, Clone, PartialEq, Eq)]
314pub struct FiniteSet<T: PartialEq> {
315    elems: Vec<T>,
316}
317impl<T: PartialEq> FiniteSet<T> {
318    /// Create an empty finite set.
319    pub fn new() -> Self {
320        Self { elems: Vec::new() }
321    }
322    /// Insert `x` if not already present. Returns `true` if inserted.
323    pub fn insert(&mut self, x: T) -> bool {
324        if self.elems.contains(&x) {
325            false
326        } else {
327            self.elems.push(x);
328            true
329        }
330    }
331    /// Returns `true` if `x` is in the set.
332    pub fn contains(&self, x: &T) -> bool {
333        self.elems.contains(x)
334    }
335    /// Remove `x` from the set. Returns `true` if it was present.
336    pub fn remove(&mut self, x: &T) -> bool {
337        if let Some(pos) = self.elems.iter().position(|e| e == x) {
338            self.elems.remove(pos);
339            true
340        } else {
341            false
342        }
343    }
344    /// Number of elements.
345    pub fn len(&self) -> usize {
346        self.elems.len()
347    }
348    /// Returns `true` if the set is empty.
349    pub fn is_empty(&self) -> bool {
350        self.elems.is_empty()
351    }
352    /// Iterate over elements.
353    pub fn iter(&self) -> impl Iterator<Item = &T> {
354        self.elems.iter()
355    }
356    /// Compute the union with another set.
357    pub fn union(&self, other: &Self) -> Self
358    where
359        T: Clone,
360    {
361        let mut result = self.clone();
362        for x in &other.elems {
363            result.insert(x.clone());
364        }
365        result
366    }
367    /// Compute the intersection with another set.
368    pub fn intersection(&self, other: &Self) -> Self
369    where
370        T: Clone,
371    {
372        Self {
373            elems: self
374                .elems
375                .iter()
376                .filter(|x| other.contains(x))
377                .cloned()
378                .collect(),
379        }
380    }
381    /// Compute the set difference `self \ other`.
382    pub fn difference(&self, other: &Self) -> Self
383    where
384        T: Clone,
385    {
386        Self {
387            elems: self
388                .elems
389                .iter()
390                .filter(|x| !other.contains(x))
391                .cloned()
392                .collect(),
393        }
394    }
395    /// Subset check: is `self ⊆ other`?
396    pub fn is_subset(&self, other: &Self) -> bool {
397        self.elems.iter().all(|x| other.contains(x))
398    }
399}
400/// A lookup table of named decidable propositions.
401///
402/// Useful for the elaborator to cache computed decisions.
403#[derive(Debug, Clone, Default)]
404pub struct DecisionTable {
405    entries: Vec<(String, Decision<()>)>,
406}
407impl DecisionTable {
408    /// Create an empty table.
409    pub fn new() -> Self {
410        Self::default()
411    }
412    /// Insert or overwrite a named decision.
413    pub fn insert(&mut self, name: impl Into<String>, d: Decision<()>) {
414        let name = name.into();
415        if let Some(entry) = self.entries.iter_mut().find(|(k, _)| k == &name) {
416            entry.1 = d;
417        } else {
418            self.entries.push((name, d));
419        }
420    }
421    /// Look up a named decision.
422    pub fn lookup(&self, name: &str) -> Option<&Decision<()>> {
423        self.entries.iter().find(|(k, _)| k == name).map(|(_, v)| v)
424    }
425    /// Number of entries.
426    pub fn len(&self) -> usize {
427        self.entries.len()
428    }
429    /// Returns `true` if empty.
430    pub fn is_empty(&self) -> bool {
431        self.entries.is_empty()
432    }
433    /// Iterate over all entries.
434    pub fn iter(&self) -> impl Iterator<Item = (&str, &Decision<()>)> {
435        self.entries.iter().map(|(k, v)| (k.as_str(), v))
436    }
437}
438/// A function-based decidable predicate.
439#[derive(Clone)]
440pub struct FnPred<A, F>(pub(super) F, pub(super) std::marker::PhantomData<A>)
441where
442    F: Fn(&A) -> bool;
443impl<A, F: Fn(&A) -> bool> FnPred<A, F> {
444    /// Wrap a closure as a decidable predicate.
445    pub fn new(f: F) -> Self {
446        Self(f, std::marker::PhantomData)
447    }
448}
449/// Semi-decidable (recursively enumerable) predicate.
450#[allow(dead_code)]
451pub struct SemiDecidableExt<T> {
452    /// The underlying predicate (returns Some on positive witness, diverges otherwise)
453    predicate: std::marker::PhantomData<T>,
454    /// True if this is a proper decision procedure (terminates on both yes/no)
455    is_total: bool,
456}
457/// A decidable ordering check wrapper.
458#[derive(Clone, Debug)]
459pub struct LeDecision<T> {
460    pub(super) lhs: T,
461    pub(super) rhs: T,
462}
463impl<T: PartialOrd> LeDecision<T> {
464    /// Create a new `≤` decision.
465    pub fn new(lhs: T, rhs: T) -> Self {
466        Self { lhs, rhs }
467    }
468}
469/// Halting oracle (exists only non-constructively, for axiom purposes).
470#[allow(dead_code)]
471pub struct HaltingOracleExt {
472    /// Name of the oracle
473    oracle_name: String,
474    /// This oracle is undecidable by Church-Turing thesis
475    undecidable: bool,
476}
477/// A closed integer interval `[lo, hi]`.
478#[derive(Clone, Copy, Debug, PartialEq, Eq)]
479pub struct Interval {
480    /// Lower bound (inclusive).
481    pub lo: i64,
482    /// Upper bound (inclusive).
483    pub hi: i64,
484}
485impl Interval {
486    /// Create an interval.
487    pub fn new(lo: i64, hi: i64) -> Self {
488        Self { lo, hi }
489    }
490    /// A single-element interval.
491    pub fn point(x: i64) -> Self {
492        Self { lo: x, hi: x }
493    }
494    /// Whether `x` is in the interval.
495    pub fn contains(&self, x: i64) -> bool {
496        x >= self.lo && x <= self.hi
497    }
498    /// Whether the interval is empty (lo > hi).
499    pub fn is_empty(&self) -> bool {
500        self.lo > self.hi
501    }
502    /// Length of the interval.
503    pub fn len(&self) -> u64 {
504        if self.is_empty() {
505            0
506        } else {
507            (self.hi - self.lo) as u64 + 1
508        }
509    }
510    /// Intersect with another interval.
511    pub fn intersect(&self, other: &Interval) -> Interval {
512        Interval {
513            lo: self.lo.max(other.lo),
514            hi: self.hi.min(other.hi),
515        }
516    }
517    /// Union with another interval (assumes overlapping or adjacent).
518    pub fn union(&self, other: &Interval) -> Interval {
519        Interval {
520            lo: self.lo.min(other.lo),
521            hi: self.hi.max(other.hi),
522        }
523    }
524    /// Decide whether `x` is in the interval.
525    pub fn decide_contains(&self, x: i64) -> Decision<()> {
526        if self.contains(x) {
527            Decision::IsTrue(())
528        } else {
529            Decision::IsFalse(format!("{x} not in [{}, {}]", self.lo, self.hi))
530        }
531    }
532}