Skip to main content

oxilean_std/tactic_lemmas/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
6
7use super::functions::*;
8use std::collections::HashMap;
9
10/// A simple linear arithmetic expression over integer variables.
11///
12/// Used by the `omega` tactic to represent linear constraints.
13#[allow(dead_code)]
14pub struct OmegaLinearExpr {
15    /// Coefficients for each variable (index → coefficient).
16    pub coeffs: Vec<i64>,
17    /// Constant term.
18    pub constant: i64,
19}
20#[allow(dead_code)]
21impl OmegaLinearExpr {
22    /// Create a new linear expression with the given coefficients and constant.
23    pub fn new(coeffs: Vec<i64>, constant: i64) -> Self {
24        Self { coeffs, constant }
25    }
26    /// Evaluate the expression at the given integer assignment.
27    pub fn eval(&self, vars: &[i64]) -> i64 {
28        self.coeffs
29            .iter()
30            .zip(vars.iter())
31            .map(|(c, v)| c * v)
32            .sum::<i64>()
33            + self.constant
34    }
35    /// Check if the constraint `self ≤ 0` is trivially satisfied (constant ≤ 0, all coeffs 0).
36    pub fn is_trivially_nonpositive(&self) -> bool {
37        self.constant <= 0 && self.coeffs.iter().all(|&c| c == 0)
38    }
39    /// Negate this linear expression.
40    pub fn negate(&self) -> Self {
41        Self {
42            coeffs: self.coeffs.iter().map(|&c| -c).collect(),
43            constant: -self.constant,
44        }
45    }
46    /// Add two linear expressions.
47    pub fn add(&self, other: &Self) -> Self {
48        let len = self.coeffs.len().max(other.coeffs.len());
49        let mut coeffs = vec![0i64; len];
50        for (i, &c) in self.coeffs.iter().enumerate() {
51            coeffs[i] += c;
52        }
53        for (i, &c) in other.coeffs.iter().enumerate() {
54            coeffs[i] += c;
55        }
56        Self {
57            coeffs,
58            constant: self.constant + other.constant,
59        }
60    }
61}
62/// A certificate for the `linarith` tactic: a non-negative linear combination of hypotheses
63/// that witnesses a contradiction.
64#[allow(dead_code)]
65pub struct LinarithCertificate {
66    /// Multipliers for each hypothesis.
67    pub multipliers: Vec<u64>,
68    /// The hypotheses as linear expressions.
69    pub hypotheses: Vec<OmegaLinearExpr>,
70}
71#[allow(dead_code)]
72impl LinarithCertificate {
73    /// Create a new certificate.
74    pub fn new(multipliers: Vec<u64>, hypotheses: Vec<OmegaLinearExpr>) -> Self {
75        Self {
76            multipliers,
77            hypotheses,
78        }
79    }
80    /// Check validity: the weighted sum of hypotheses evaluates to a contradiction.
81    /// Returns true if the combination yields constant > 0 with all-zero variable part.
82    pub fn is_valid_contradiction(&self, vars: &[i64]) -> bool {
83        if self.multipliers.len() != self.hypotheses.len() {
84            return false;
85        }
86        let combined_val: i64 = self
87            .multipliers
88            .iter()
89            .zip(self.hypotheses.iter())
90            .map(|(&m, hyp)| m as i64 * hyp.eval(vars))
91            .sum();
92        combined_val > 0
93    }
94    /// Check structural validity (all multipliers non-negative, lengths match).
95    pub fn is_structurally_valid(&self) -> bool {
96        self.multipliers.len() == self.hypotheses.len()
97    }
98}
99/// A ring normalization context for the `ring_nf` / `ring` tactic.
100///
101/// Tracks a list of ring axioms used in a normalization proof.
102#[allow(dead_code)]
103pub struct RingNfContext {
104    /// Names of ring axioms applied during normalization.
105    pub applied_axioms: Vec<String>,
106    /// The carrier type name (e.g. "Int", "Real").
107    pub carrier: String,
108}
109#[allow(dead_code)]
110impl RingNfContext {
111    /// Create a new ring normalization context.
112    pub fn new(carrier: &str) -> Self {
113        Self {
114            applied_axioms: Vec::new(),
115            carrier: carrier.to_string(),
116        }
117    }
118    /// Record that an axiom was applied.
119    pub fn apply_axiom(&mut self, axiom: &str) {
120        self.applied_axioms.push(axiom.to_string());
121    }
122    /// Check whether the context has applied any axioms.
123    pub fn is_nontrivial(&self) -> bool {
124        !self.applied_axioms.is_empty()
125    }
126    /// Get the number of axiom applications.
127    pub fn step_count(&self) -> usize {
128        self.applied_axioms.len()
129    }
130    /// Reset the normalization context.
131    pub fn reset(&mut self) {
132        self.applied_axioms.clear();
133    }
134}
135/// A simp set extended with custom lemmas and priorities.
136#[allow(dead_code)]
137pub struct ExtendedSimpSet {
138    /// Lemma names and priorities in this simp set.
139    pub lemmas: Vec<(String, u64)>,
140    /// Erased lemma names.
141    pub erased: Vec<String>,
142}
143#[allow(dead_code)]
144impl ExtendedSimpSet {
145    /// Create an empty simp set.
146    pub fn new() -> Self {
147        Self {
148            lemmas: Vec::new(),
149            erased: Vec::new(),
150        }
151    }
152    /// Add a lemma with a given priority.
153    pub fn add_lemma(&mut self, name: &str, priority: u64) {
154        self.lemmas.push((name.to_string(), priority));
155    }
156    /// Erase a lemma by name.
157    pub fn erase_lemma(&mut self, name: &str) {
158        self.erased.push(name.to_string());
159    }
160    /// Check if a lemma name is active (added but not erased).
161    pub fn is_active(&self, name: &str) -> bool {
162        let added = self.lemmas.iter().any(|(n, _)| n == name);
163        let erased = self.erased.iter().any(|n| n == name);
164        added && !erased
165    }
166    /// Sort lemmas by descending priority.
167    pub fn sorted_lemmas(&self) -> Vec<(String, u64)> {
168        let mut result: Vec<_> = self
169            .lemmas
170            .iter()
171            .filter(|(n, _)| !self.erased.contains(n))
172            .cloned()
173            .collect();
174        result.sort_by(|a, b| b.1.cmp(&a.1));
175        result
176    }
177    /// Total number of active lemmas.
178    pub fn active_count(&self) -> usize {
179        self.lemmas
180            .iter()
181            .filter(|(n, _)| !self.erased.contains(n))
182            .count()
183    }
184}
185/// A positivity checker that tracks the sign of sub-expressions.
186#[allow(dead_code)]
187pub struct PositivityChecker {
188    /// Map from expression names to sign: -1 (negative), 0 (zero), 1 (positive), 2 (nonneg).
189    pub signs: std::collections::HashMap<String, i8>,
190}
191#[allow(dead_code)]
192impl PositivityChecker {
193    /// Create a new positivity checker.
194    pub fn new() -> Self {
195        Self {
196            signs: std::collections::HashMap::new(),
197        }
198    }
199    /// Record the sign of an expression.
200    pub fn record_sign(&mut self, expr: &str, sign: i8) {
201        self.signs.insert(expr.to_string(), sign);
202    }
203    /// Check whether an expression is known nonnegative.
204    pub fn is_nonneg(&self, expr: &str) -> bool {
205        match self.signs.get(expr) {
206            Some(&s) => s >= 0,
207            None => false,
208        }
209    }
210    /// Check whether the product of two expressions is nonnegative.
211    pub fn product_nonneg(&self, a: &str, b: &str) -> bool {
212        let sa = self.signs.get(a).copied().unwrap_or(0);
213        let sb = self.signs.get(b).copied().unwrap_or(0);
214        (sa >= 0 && sb >= 0) || (sa <= 0 && sb <= 0)
215    }
216    /// Infer sign of sum.
217    pub fn sum_sign(&self, a: &str, b: &str) -> Option<i8> {
218        let sa = self.signs.get(a).copied()?;
219        let sb = self.signs.get(b).copied()?;
220        if sa >= 0 && sb >= 0 {
221            Some(1)
222        } else if sa < 0 && sb < 0 {
223            Some(-1)
224        } else {
225            None
226        }
227    }
228}
229/// A simp lemma entry: name, priority, and proof expression.
230///
231/// Represents `SimpLemmaEntry.mk : Name → Nat → Expr → SimpLemmaEntry`
232/// in the Lean 4 simp infrastructure.
233#[allow(dead_code)]
234#[derive(Clone, Debug)]
235pub struct SimpLemmaEntry {
236    /// Name of the lemma.
237    pub name: Name,
238    /// Priority (higher = applied first).
239    pub priority: u64,
240    /// The proof term for the simp lemma.
241    pub proof: Expr,
242}
243impl SimpLemmaEntry {
244    /// Create a new simp lemma entry.
245    #[allow(dead_code)]
246    pub fn mk(name: Name, priority: u64, proof: Expr) -> Self {
247        SimpLemmaEntry {
248            name,
249            priority,
250            proof,
251        }
252    }
253}
254/// A collection of simp lemmas (simp set).
255///
256/// Represents `SimpTheorems : Type` in the Lean 4 simp infrastructure.
257#[allow(dead_code)]
258#[derive(Clone, Debug, Default)]
259pub struct SimpTheorems {
260    /// The lemmas in this set, ordered by priority (highest first).
261    pub lemmas: Vec<SimpLemmaEntry>,
262}
263impl SimpTheorems {
264    /// Create an empty simp theorem set.
265    #[allow(dead_code)]
266    pub fn empty() -> Self {
267        SimpTheorems { lemmas: vec![] }
268    }
269    /// Add a simp lemma entry to the set.
270    #[allow(dead_code)]
271    pub fn add(&mut self, entry: SimpLemmaEntry) {
272        let pos = self
273            .lemmas
274            .iter()
275            .position(|e| e.priority < entry.priority)
276            .unwrap_or(self.lemmas.len());
277        self.lemmas.insert(pos, entry);
278    }
279    /// Erase a lemma by name.
280    #[allow(dead_code)]
281    pub fn erase(&mut self, name: &Name) {
282        self.lemmas.retain(|e| &e.name != name);
283    }
284    /// Get the number of lemmas.
285    #[allow(dead_code)]
286    pub fn len(&self) -> usize {
287        self.lemmas.len()
288    }
289    /// Check if empty.
290    #[allow(dead_code)]
291    pub fn is_empty(&self) -> bool {
292        self.lemmas.is_empty()
293    }
294    /// Look up a lemma by name.
295    #[allow(dead_code)]
296    pub fn get(&self, name: &Name) -> Option<&SimpLemmaEntry> {
297        self.lemmas.iter().find(|e| &e.name == name)
298    }
299}