Skip to main content

oxilean_std/term_rewriting/
functions.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};
6use std::collections::{HashMap, HashSet, VecDeque};
7
8use super::types::{
9    DependencyPairGraph, EGraph, KnuthBendixData, NarrowingSystem, PolynomialInterpretation,
10    ReductionStrategy, RewritingLogicTheory, Rule, Srs, Strategy, StringRewritingSystem,
11    Substitution, Term, TreeAutomaton, Trs,
12};
13
14pub fn app(f: Expr, a: Expr) -> Expr {
15    Expr::App(Box::new(f), Box::new(a))
16}
17pub fn app2(f: Expr, a: Expr, b: Expr) -> Expr {
18    app(app(f, a), b)
19}
20pub fn app3(f: Expr, a: Expr, b: Expr, c: Expr) -> Expr {
21    app(app2(f, a, b), c)
22}
23pub fn cst(s: &str) -> Expr {
24    Expr::Const(Name::str(s), vec![])
25}
26pub fn prop() -> Expr {
27    Expr::Sort(Level::zero())
28}
29pub fn type0() -> Expr {
30    Expr::Sort(Level::succ(Level::zero()))
31}
32pub fn pi(bi: BinderInfo, name: &str, dom: Expr, body: Expr) -> Expr {
33    Expr::Pi(bi, Name::str(name), Box::new(dom), Box::new(body))
34}
35pub fn arrow(a: Expr, b: Expr) -> Expr {
36    pi(BinderInfo::Default, "_", a, b)
37}
38pub fn bvar(n: u32) -> Expr {
39    Expr::BVar(n)
40}
41pub fn nat_ty() -> Expr {
42    cst("Nat")
43}
44pub fn bool_ty() -> Expr {
45    cst("Bool")
46}
47pub fn string_ty() -> Expr {
48    cst("String")
49}
50pub fn list_nat() -> Expr {
51    app(cst("List"), nat_ty())
52}
53pub fn option_ty(t: Expr) -> Expr {
54    app(cst("Option"), t)
55}
56/// `Term : Type` β€” the type of rewriting terms (first-order terms over signature)
57pub fn term_ty() -> Expr {
58    type0()
59}
60/// `Signature : Type` β€” function symbols with arities
61pub fn signature_ty() -> Expr {
62    type0()
63}
64/// `Variable : Type` β€” the type of term variables
65pub fn variable_ty() -> Expr {
66    type0()
67}
68/// `Substitution : Type` β€” a mapping from variables to terms
69pub fn substitution_ty() -> Expr {
70    arrow(variable_ty(), term_ty())
71}
72/// `RewriteRule : Type` β€” a pair (lhs, rhs) of terms
73pub fn rewrite_rule_ty() -> Expr {
74    type0()
75}
76/// `TRS : Type` β€” a term rewriting system (a set of rewrite rules)
77pub fn trs_ty() -> Expr {
78    type0()
79}
80/// `Reduction : Term β†’ Term β†’ Prop` β€” one-step reduction relation
81pub fn reduction_ty() -> Expr {
82    arrow(term_ty(), arrow(term_ty(), prop()))
83}
84/// `ReflTransClosure : (Term β†’ Term β†’ Prop) β†’ Term β†’ Term β†’ Prop`
85pub fn refl_trans_closure_ty() -> Expr {
86    arrow(
87        arrow(term_ty(), arrow(term_ty(), prop())),
88        arrow(term_ty(), arrow(term_ty(), prop())),
89    )
90}
91/// `Confluence : TRS β†’ Prop`
92pub fn confluence_ty() -> Expr {
93    arrow(trs_ty(), prop())
94}
95/// `LocalConfluence : TRS β†’ Prop`
96pub fn local_confluence_ty() -> Expr {
97    arrow(trs_ty(), prop())
98}
99/// `StrongNormalization : TRS β†’ Prop` (every reduction sequence terminates)
100pub fn strong_normalization_ty() -> Expr {
101    arrow(trs_ty(), prop())
102}
103/// `WeakNormalization : TRS β†’ Prop` (every term has a normal form)
104pub fn weak_normalization_ty() -> Expr {
105    arrow(trs_ty(), prop())
106}
107/// `NormalForm : TRS β†’ Term β†’ Prop`
108pub fn normal_form_ty() -> Expr {
109    arrow(trs_ty(), arrow(term_ty(), prop()))
110}
111/// `CriticalPair : TRS β†’ Term β†’ Term β†’ Prop`
112pub fn critical_pair_ty() -> Expr {
113    arrow(trs_ty(), arrow(term_ty(), arrow(term_ty(), prop())))
114}
115/// `Orthogonal : TRS β†’ Prop` β€” left-linear with no critical pairs
116pub fn orthogonal_ty() -> Expr {
117    arrow(trs_ty(), prop())
118}
119/// `LeftLinear : TRS β†’ Prop`
120pub fn left_linear_ty() -> Expr {
121    arrow(trs_ty(), prop())
122}
123/// `GroundTRS : TRS β†’ Prop` β€” all rules are ground (no variables)
124pub fn ground_trs_ty() -> Expr {
125    arrow(trs_ty(), prop())
126}
127/// `Position : Type` β€” a path in a term tree (list of natural numbers)
128pub fn position_ty() -> Expr {
129    list_nat()
130}
131/// `Subterm : Term β†’ Position β†’ Term` β€” subterm at a position
132pub fn subterm_ty() -> Expr {
133    arrow(term_ty(), arrow(position_ty(), term_ty()))
134}
135/// `Replace : Term β†’ Position β†’ Term β†’ Term` β€” replace subterm at position
136pub fn replace_ty() -> Expr {
137    arrow(term_ty(), arrow(position_ty(), arrow(term_ty(), term_ty())))
138}
139/// `Unifier : Term β†’ Term β†’ Substitution β†’ Prop`
140pub fn unifier_ty() -> Expr {
141    arrow(
142        term_ty(),
143        arrow(term_ty(), arrow(substitution_ty(), prop())),
144    )
145}
146/// `MGU : Term β†’ Term β†’ Substitution β†’ Prop` β€” most general unifier
147pub fn mgu_ty() -> Expr {
148    arrow(
149        term_ty(),
150        arrow(term_ty(), arrow(substitution_ty(), prop())),
151    )
152}
153/// `KBCompletion : TRS β†’ TRS β†’ Prop` β€” Knuth-Bendix completion result
154pub fn kb_completion_ty() -> Expr {
155    arrow(trs_ty(), arrow(trs_ty(), prop()))
156}
157/// `WordProblem : TRS β†’ Term β†’ Term β†’ Bool` β€” decidability of equality
158pub fn word_problem_ty() -> Expr {
159    arrow(trs_ty(), arrow(term_ty(), arrow(term_ty(), bool_ty())))
160}
161/// `StringRewritingSystem : Type` β€” SRS over an alphabet
162pub fn srs_ty() -> Expr {
163    type0()
164}
165/// `ReductionStrategy : Type` β€” innermost / outermost / parallel
166pub fn reduction_strategy_ty() -> Expr {
167    type0()
168}
169/// `EquationalUnification : Term β†’ Term β†’ Term β†’ Prop`
170pub fn equational_unification_ty() -> Expr {
171    arrow(term_ty(), arrow(term_ty(), arrow(term_ty(), prop())))
172}
173/// Newman's Lemma: locally confluent + strongly normalizing ⟹ confluent
174/// `NewmansLemma : βˆ€ (R : TRS), LocalConfluence R β†’ StrongNormalization R β†’ Confluence R`
175pub fn newmans_lemma_ty() -> Expr {
176    pi(
177        BinderInfo::Default,
178        "R",
179        trs_ty(),
180        arrow(
181            app(cst("LocalConfluence"), bvar(0)),
182            arrow(
183                app(cst("StrongNormalization"), bvar(1)),
184                app(cst("Confluence"), bvar(2)),
185            ),
186        ),
187    )
188}
189/// Church-Rosser: confluence ↔ Church-Rosser property
190/// `ChurchRosser : βˆ€ R : TRS, Confluence R ↔ ChurchRosserProp R`
191pub fn church_rosser_ty() -> Expr {
192    pi(
193        BinderInfo::Default,
194        "R",
195        trs_ty(),
196        app2(
197            cst("Iff"),
198            app(cst("Confluence"), bvar(0)),
199            app(cst("ChurchRosserProp"), bvar(0)),
200        ),
201    )
202}
203/// Orthogonal TRS are confluent
204/// `OrthogonalConfluent : βˆ€ R : TRS, Orthogonal R β†’ Confluence R`
205pub fn orthogonal_confluent_ty() -> Expr {
206    pi(
207        BinderInfo::Default,
208        "R",
209        trs_ty(),
210        arrow(
211            app(cst("Orthogonal"), bvar(0)),
212            app(cst("Confluence"), bvar(0)),
213        ),
214    )
215}
216/// RΓ©dei's theorem: finitely generated commutative monoids are finitely presented
217pub fn redei_theorem_ty() -> Expr {
218    prop()
219}
220/// Ground confluence decidability
221pub fn ground_confluence_decidable_ty() -> Expr {
222    pi(
223        BinderInfo::Default,
224        "R",
225        trs_ty(),
226        arrow(
227            app(cst("GroundTRS"), bvar(0)),
228            app(cst("Decidable"), app(cst("Confluence"), bvar(1))),
229        ),
230    )
231}
232/// Unique normal forms: confluent ⟹ unique normal forms
233pub fn unique_normal_forms_ty() -> Expr {
234    pi(
235        BinderInfo::Default,
236        "R",
237        trs_ty(),
238        arrow(
239            app(cst("Confluence"), bvar(0)),
240            pi(
241                BinderInfo::Default,
242                "t",
243                term_ty(),
244                pi(
245                    BinderInfo::Default,
246                    "u",
247                    term_ty(),
248                    pi(
249                        BinderInfo::Default,
250                        "v",
251                        term_ty(),
252                        arrow(
253                            app3(cst("ReducesTo"), bvar(3), bvar(2), bvar(1)),
254                            arrow(
255                                app3(cst("ReducesTo"), bvar(4), bvar(3), bvar(0)),
256                                app2(cst("Eq"), bvar(2), bvar(1)),
257                            ),
258                        ),
259                    ),
260                ),
261            ),
262        ),
263    )
264}
265/// Knuth-Bendix completeness: KB produces a complete system when it terminates
266pub fn kb_completeness_ty() -> Expr {
267    prop()
268}
269/// Populate `env` with all TRS kernel declarations.
270pub fn build_term_rewriting_env(env: &mut Environment) -> Result<(), String> {
271    for (name, ty) in [
272        ("TrsTerm", term_ty()),
273        ("TrsSignature", signature_ty()),
274        ("TrsVariable", variable_ty()),
275        ("TrsSubstitution", substitution_ty()),
276        ("TrsRewriteRule", rewrite_rule_ty()),
277        ("TRS", trs_ty()),
278        ("TrsPosition", position_ty()),
279        ("SRS", srs_ty()),
280        ("ReductionStrategy", reduction_strategy_ty()),
281    ] {
282        env.add(Declaration::Axiom {
283            name: Name::str(name),
284            univ_params: vec![],
285            ty,
286        })
287        .ok();
288    }
289    for (name, ty) in [
290        ("Confluence", confluence_ty()),
291        ("LocalConfluence", local_confluence_ty()),
292        ("StrongNormalization", strong_normalization_ty()),
293        ("WeakNormalization", weak_normalization_ty()),
294        ("NormalForm", normal_form_ty()),
295        ("CriticalPair", critical_pair_ty()),
296        ("Orthogonal", orthogonal_ty()),
297        ("LeftLinear", left_linear_ty()),
298        ("GroundTRS", ground_trs_ty()),
299        ("Unifier", unifier_ty()),
300        ("MGU", mgu_ty()),
301        ("KBCompletion", kb_completion_ty()),
302        ("EquationalUnification", equational_unification_ty()),
303        ("ChurchRosserProp", arrow(trs_ty(), prop())),
304        (
305            "ReducesTo",
306            arrow(trs_ty(), arrow(term_ty(), arrow(term_ty(), prop()))),
307        ),
308        (
309            "EquivalentUnder",
310            arrow(trs_ty(), arrow(term_ty(), arrow(term_ty(), prop()))),
311        ),
312    ] {
313        env.add(Declaration::Axiom {
314            name: Name::str(name),
315            univ_params: vec![],
316            ty,
317        })
318        .ok();
319    }
320    for (name, ty) in [
321        ("trsSubterm", subterm_ty()),
322        ("trsReplace", replace_ty()),
323        (
324            "trsApplySubst",
325            arrow(substitution_ty(), arrow(term_ty(), term_ty())),
326        ),
327        (
328            "trsUnify",
329            arrow(term_ty(), arrow(term_ty(), option_ty(substitution_ty()))),
330        ),
331        (
332            "trsMGU",
333            arrow(term_ty(), arrow(term_ty(), option_ty(substitution_ty()))),
334        ),
335        (
336            "trsNormalForm",
337            arrow(trs_ty(), arrow(term_ty(), term_ty())),
338        ),
339        (
340            "trsReduceInnermost",
341            arrow(trs_ty(), arrow(term_ty(), term_ty())),
342        ),
343        (
344            "trsReduceOutermost",
345            arrow(trs_ty(), arrow(term_ty(), term_ty())),
346        ),
347    ] {
348        env.add(Declaration::Axiom {
349            name: Name::str(name),
350            univ_params: vec![],
351            ty,
352        })
353        .ok();
354    }
355    for (name, ty) in [
356        ("NewmansLemma", newmans_lemma_ty()),
357        ("ChurchRosserThm", church_rosser_ty()),
358        ("OrthogonalConfluentThm", orthogonal_confluent_ty()),
359        ("RedeiTheorem", redei_theorem_ty()),
360        (
361            "GroundConfluenceDecidable",
362            ground_confluence_decidable_ty(),
363        ),
364        ("UniqueNormalForms", unique_normal_forms_ty()),
365        ("KBCompletenessThm", kb_completeness_ty()),
366        ("TerminationImpliesWN", prop()),
367        ("SNImpliesWN", prop()),
368        ("ConfluenceImpliesLocalConfluence", prop()),
369        ("LeftLinearNoCPConfluent", prop()),
370        ("CriticalPairLemma", prop()),
371        ("CommutativityWordProblem", prop()),
372        ("TotalTerminationCriterion", prop()),
373        ("KBTermination", prop()),
374        ("SubstitutionLemma", prop()),
375        ("PositionInduction", prop()),
376        ("ReplaceLemma", prop()),
377        ("DepthTermination", prop()),
378        ("LexicographicPathOrder", prop()),
379        ("RecursivePathOrder", prop()),
380        ("PolynomialInterpretationThm", prop()),
381        ("EquationalTheoryAxiom", arrow(trs_ty(), prop())),
382        (
383            "RewritingModuloE",
384            arrow(
385                trs_ty(),
386                arrow(trs_ty(), arrow(term_ty(), arrow(term_ty(), prop()))),
387            ),
388        ),
389        (
390            "ACRewriting",
391            arrow(trs_ty(), arrow(term_ty(), arrow(term_ty(), prop()))),
392        ),
393        (
394            "ACUnification",
395            arrow(term_ty(), arrow(term_ty(), option_ty(substitution_ty()))),
396        ),
397        ("BEquationalAxiom", arrow(trs_ty(), prop())),
398        ("HigherOrderTRS", arrow(trs_ty(), prop())),
399        ("CombReductionSystem", arrow(trs_ty(), prop())),
400        ("HRSConfluence", arrow(trs_ty(), prop())),
401        ("LambdaCalculusEncoding", arrow(term_ty(), term_ty())),
402        ("BetaReduction", arrow(term_ty(), option_ty(term_ty()))),
403        (
404            "DependencyPair",
405            arrow(trs_ty(), arrow(term_ty(), arrow(term_ty(), prop()))),
406        ),
407        ("DependencyGraph", arrow(trs_ty(), type0())),
408        (
409            "DependencyPairMethod",
410            arrow(trs_ty(), arrow(trs_ty(), prop())),
411        ),
412        ("SccsTermination", arrow(trs_ty(), prop())),
413        ("DPChain", arrow(trs_ty(), arrow(term_ty(), prop()))),
414        (
415            "ReductionOrdering",
416            arrow(arrow(term_ty(), arrow(term_ty(), prop())), prop()),
417        ),
418        ("PolynomialOrder", arrow(nat_ty(), arrow(trs_ty(), prop()))),
419        ("RecursivePathOrdering", arrow(trs_ty(), prop())),
420        ("KnuthBendixOrder", arrow(trs_ty(), prop())),
421        ("WeightFunction", arrow(term_ty(), nat_ty())),
422        (
423            "SimplificationOrder",
424            arrow(arrow(term_ty(), arrow(term_ty(), prop())), prop()),
425        ),
426        (
427            "NarrowingStep",
428            arrow(trs_ty(), arrow(term_ty(), arrow(term_ty(), prop()))),
429        ),
430        (
431            "LazyNarrowing",
432            arrow(trs_ty(), arrow(term_ty(), option_ty(substitution_ty()))),
433        ),
434        (
435            "BasicNarrowing",
436            arrow(trs_ty(), arrow(term_ty(), option_ty(substitution_ty()))),
437        ),
438        ("NarrowingCompleteness", arrow(trs_ty(), prop())),
439        (
440            "NarrowingUnification",
441            arrow(term_ty(), arrow(term_ty(), option_ty(substitution_ty()))),
442        ),
443        ("TreeAutomaton", type0()),
444        ("RegularTreeLanguage", arrow(cst("TreeAutomaton"), type0())),
445        (
446            "TreeAutomataIntersection",
447            arrow(
448                cst("TreeAutomaton"),
449                arrow(cst("TreeAutomaton"), cst("TreeAutomaton")),
450            ),
451        ),
452        (
453            "TreeAutomataUnion",
454            arrow(
455                cst("TreeAutomaton"),
456                arrow(cst("TreeAutomaton"), cst("TreeAutomaton")),
457            ),
458        ),
459        (
460            "TreeAutomataComplementation",
461            arrow(cst("TreeAutomaton"), cst("TreeAutomaton")),
462        ),
463        (
464            "TRSPreservesRegularity",
465            arrow(trs_ty(), arrow(cst("TreeAutomaton"), prop())),
466        ),
467        (
468            "TreeLanguageMembership",
469            arrow(term_ty(), arrow(cst("TreeAutomaton"), prop())),
470        ),
471        (
472            "CongruenceClosure",
473            arrow(trs_ty(), arrow(term_ty(), arrow(term_ty(), prop()))),
474        ),
475        ("GroundCongruenceClosure", arrow(trs_ty(), prop())),
476        (
477            "NelsonOppenCombination",
478            arrow(trs_ty(), arrow(trs_ty(), prop())),
479        ),
480        ("SharingCongruence", arrow(trs_ty(), prop())),
481        ("HuetsCompletion", arrow(trs_ty(), arrow(trs_ty(), prop()))),
482        ("PetersonStickel", arrow(trs_ty(), arrow(trs_ty(), prop()))),
483        (
484            "OrderedCompletion",
485            arrow(trs_ty(), arrow(trs_ty(), prop())),
486        ),
487        ("CompletionTerminates", arrow(trs_ty(), prop())),
488        ("RosensLemma", prop()),
489        (
490            "ModularConfluence",
491            arrow(trs_ty(), arrow(trs_ty(), prop())),
492        ),
493        ("ParallelClosure", arrow(trs_ty(), prop())),
494        (
495            "ConfluentIntersection",
496            arrow(trs_ty(), arrow(trs_ty(), prop())),
497        ),
498        ("RewritingLogicTheory", type0()),
499        (
500            "ConcurrentRewrite",
501            arrow(
502                cst("RewritingLogicTheory"),
503                arrow(term_ty(), arrow(term_ty(), prop())),
504            ),
505        ),
506        (
507            "RewritingLogicSoundness",
508            arrow(cst("RewritingLogicTheory"), prop()),
509        ),
510        (
511            "MaudeSystemAxiom",
512            arrow(cst("RewritingLogicTheory"), prop()),
513        ),
514        ("SufficientCompleteness", arrow(trs_ty(), prop())),
515        (
516            "GroundReducibility",
517            arrow(trs_ty(), arrow(term_ty(), prop())),
518        ),
519        (
520            "InductiveTheorem",
521            arrow(trs_ty(), arrow(term_ty(), prop())),
522        ),
523        ("ConstructorSystem", arrow(trs_ty(), prop())),
524        (
525            "ACMatching",
526            arrow(term_ty(), arrow(term_ty(), option_ty(substitution_ty()))),
527        ),
528        (
529            "ACUMatching",
530            arrow(term_ty(), arrow(term_ty(), option_ty(substitution_ty()))),
531        ),
532        ("FreeAlgebraConstraint", arrow(trs_ty(), prop())),
533        (
534            "MatchingModuloAxiom",
535            arrow(trs_ty(), arrow(term_ty(), arrow(term_ty(), prop()))),
536        ),
537    ] {
538        env.add(Declaration::Axiom {
539            name: Name::str(name),
540            univ_params: vec![],
541            ty,
542        })
543        .ok();
544    }
545    Ok(())
546}
547/// Robinson's unification algorithm (syntactic).
548///
549/// Returns `Some(mgu)` if `s` and `t` unify, `None` otherwise.
550pub fn unify(s: &Term, t: &Term) -> Option<Substitution> {
551    let mut equations: Vec<(Term, Term)> = vec![(s.clone(), t.clone())];
552    let mut subst = Substitution::new();
553    while let Some((lhs, rhs)) = equations.pop() {
554        let lhs = lhs.apply(&subst);
555        let rhs = rhs.apply(&subst);
556        if lhs == rhs {
557            continue;
558        }
559        match (&lhs, &rhs) {
560            (Term::Var(x), _) => {
561                if rhs.contains(&Term::Var(*x)) {
562                    return None;
563                }
564                let bind = Substitution {
565                    map: HashMap::from([(*x, rhs.clone())]),
566                };
567                let mut new_map = HashMap::new();
568                for (&v, t) in &subst.map {
569                    new_map.insert(v, t.apply(&bind));
570                }
571                new_map.insert(*x, rhs.clone());
572                subst.map = new_map;
573            }
574            (_, Term::Var(y)) => {
575                if lhs.contains(&Term::Var(*y)) {
576                    return None;
577                }
578                let bind = Substitution {
579                    map: HashMap::from([(*y, lhs.clone())]),
580                };
581                let mut new_map = HashMap::new();
582                for (&v, t) in &subst.map {
583                    new_map.insert(v, t.apply(&bind));
584                }
585                new_map.insert(*y, lhs.clone());
586                subst.map = new_map;
587            }
588            (Term::Fun(f, fa), Term::Fun(g, ga)) => {
589                if f != g || fa.len() != ga.len() {
590                    return None;
591                }
592                for (a, b) in fa.iter().zip(ga.iter()) {
593                    equations.push((a.clone(), b.clone()));
594                }
595            }
596        }
597    }
598    Some(subst)
599}
600/// Checks whether two terms are unifiable.
601pub fn unifiable(s: &Term, t: &Term) -> bool {
602    unify(s, t).is_some()
603}
604/// Computes all critical pairs of two rules (or a rule with itself).
605///
606/// A critical pair arises when the lhs of one rule (after renaming) overlaps
607/// with a non-variable subterm of the lhs of another rule.
608///
609/// Returns a list of `(left_result, right_result)` pairs that must converge
610/// for the TRS to be locally confluent.
611pub fn critical_pairs(r1: &Rule, r2: &Rule, offset: u32) -> Vec<(Term, Term)> {
612    let r1 = r1.clone();
613    let r2 = r2.rename(offset);
614    let mut pairs = Vec::new();
615    fn non_var_positions(t: &Term) -> Vec<Vec<usize>> {
616        match t {
617            Term::Var(_) => vec![],
618            Term::Fun(_, args) => {
619                let mut out = vec![vec![]];
620                for (i, a) in args.iter().enumerate() {
621                    for mut p in non_var_positions(a) {
622                        let mut full = vec![i];
623                        full.append(&mut p);
624                        out.push(full);
625                    }
626                }
627                out
628            }
629        }
630    }
631    for pos in non_var_positions(&r1.lhs) {
632        if let Some(sub) = r1.lhs.subterm_at(&pos) {
633            if let Some(sigma) = unify(sub, &r2.lhs) {
634                let left = r1.lhs.replace_at(&pos, r2.rhs.apply(&sigma)).apply(&sigma);
635                let right = r1.rhs.apply(&sigma);
636                if left != right {
637                    pairs.push((left, right));
638                }
639            }
640        }
641    }
642    pairs
643}
644/// Checks whether all critical pairs of a TRS converge (local confluence).
645///
646/// Each critical pair `(s, t)` is checked by normalizing both sides and
647/// verifying they reach the same normal form (up to `limit` steps).
648pub fn check_local_confluence(trs: &Trs, limit: usize) -> bool {
649    let n = trs.rules.len();
650    for i in 0..n {
651        for j in 0..n {
652            let pairs = critical_pairs(
653                &trs.rules[i],
654                &trs.rules[j],
655                (i * 1000 + j * 1000 + 2000) as u32,
656            );
657            for (s, t) in pairs {
658                let ns = trs.normalize_innermost(&s, limit);
659                let nt = trs.normalize_innermost(&t, limit);
660                if ns != nt {
661                    return false;
662                }
663            }
664        }
665    }
666    true
667}
668/// Ordering function type: returns `Ordering` for two terms.
669pub type TermOrdering = fn(&Term, &Term) -> std::cmp::Ordering;
670/// Apply one reduction step using the given strategy.
671pub fn reduce_step(trs: &Trs, term: &Term, strategy: Strategy) -> Option<Term> {
672    match strategy {
673        Strategy::Innermost | Strategy::Lazy => trs.reduce_innermost(term),
674        Strategy::Outermost | Strategy::Parallel => trs.reduce_outermost(term),
675    }
676}
677#[cfg(test)]
678mod tests {
679    use super::*;
680    fn f(args: Vec<Term>) -> Term {
681        Term::Fun("f".into(), args)
682    }
683    fn g(args: Vec<Term>) -> Term {
684        Term::Fun("g".into(), args)
685    }
686    fn a() -> Term {
687        Term::Fun("a".into(), vec![])
688    }
689    fn b() -> Term {
690        Term::Fun("b".into(), vec![])
691    }
692    fn x0() -> Term {
693        Term::Var(0)
694    }
695    fn x1() -> Term {
696        Term::Var(1)
697    }
698    /// Verify that the kernel environment builds without errors.
699    #[test]
700    fn test_build_env() {
701        let mut env = Environment::new();
702        let result = build_term_rewriting_env(&mut env);
703        assert!(result.is_ok());
704    }
705    /// Test basic unification: f(x0, a) =? f(b, y1).
706    #[test]
707    fn test_unification_basic() {
708        let s = f(vec![x0(), a()]);
709        let t = f(vec![b(), x1()]);
710        let mgu = unify(&s, &t).expect("should unify");
711        let s2 = s.apply(&mgu);
712        let t2 = t.apply(&mgu);
713        assert_eq!(s2, t2);
714    }
715    /// Unification should fail when occurs-check fires.
716    #[test]
717    fn test_unification_occurs_check() {
718        let result = unify(&x0(), &f(vec![x0()]));
719        assert!(result.is_none(), "occurs check should prevent self-loop");
720    }
721    /// Unification of incompatible function symbols should fail.
722    #[test]
723    fn test_unification_clash() {
724        let result = unify(&f(vec![a()]), &g(vec![a()]));
725        assert!(result.is_none());
726    }
727    /// Test innermost normalization on a simple TRS: f(f(x)) β†’ f(x)
728    #[test]
729    fn test_innermost_normalization() {
730        let mut trs = Trs::new();
731        trs.add_rule(Rule::new(f(vec![f(vec![x0()])]), f(vec![x0()])));
732        let term = f(vec![f(vec![f(vec![a()])])]);
733        let nf = trs.normalize_innermost(&term, 10);
734        assert_eq!(nf, f(vec![a()]));
735    }
736    /// Test SRS word problem: ba β†’ ab style commutation.
737    #[test]
738    fn test_srs_word_problem() {
739        let mut srs = Srs::new();
740        srs.add_rule("ba", "ab");
741        let nf = srs.normalize("bba", 10);
742        assert_eq!(nf, "abb");
743    }
744    /// Test SRS: rules that reduce to empty string.
745    #[test]
746    fn test_srs_reduction_to_empty() {
747        let mut srs = Srs::new();
748        srs.add_rule("ab", "");
749        let nf = srs.normalize("aabb", 10);
750        assert_eq!(nf, "");
751    }
752    /// Test critical pair detection for a trivial confluent system.
753    #[test]
754    fn test_critical_pairs_confluent() {
755        let r1 = Rule::new(f(vec![a()]), b());
756        let r2 = Rule::new(f(vec![a()]), b());
757        let pairs = critical_pairs(&r1, &r2, 100);
758        for (s, t) in &pairs {
759            assert_eq!(s, t, "non-trivial critical pair: {} β‰  {}", s, t);
760        }
761    }
762    /// Test local confluence check on a known confluent system.
763    #[test]
764    fn test_local_confluence() {
765        let mut trs = Trs::new();
766        trs.add_rule(Rule::new(f(vec![a()]), b()));
767        assert!(check_local_confluence(&trs, 20));
768    }
769    /// Test DependencyPairGraph construction from a simple TRS.
770    #[test]
771    fn test_dependency_pair_graph_from_trs() {
772        let mut trs = Trs::new();
773        trs.add_rule(Rule::new(
774            Term::Fun("f".into(), vec![Term::Var(0)]),
775            Term::Fun("g".into(), vec![Term::Var(0)]),
776        ));
777        trs.add_rule(Rule::new(Term::Fun("g".into(), vec![Term::Var(0)]), a()));
778        let graph = DependencyPairGraph::from_trs(&trs);
779        assert!(!graph.pairs.is_empty());
780    }
781    /// Test that a simple TRS with no recursion has all-trivial SCCs.
782    #[test]
783    fn test_dependency_pair_graph_trivial_sccs() {
784        let mut trs = Trs::new();
785        trs.add_rule(Rule::new(Term::Fun("f".into(), vec![Term::Var(0)]), a()));
786        let graph = DependencyPairGraph::from_trs(&trs);
787        assert!(graph.all_sccs_trivial());
788    }
789    /// Test PolynomialInterpretation orients a simple decreasing rule.
790    #[test]
791    fn test_polynomial_interpretation_termination() {
792        let mut interp = PolynomialInterpretation::new();
793        interp.set("f", vec![1, 1]);
794        let rule = Rule::new(Term::Fun("f".into(), vec![Term::Var(0)]), Term::Var(0));
795        assert!(interp.orients_rule(&rule, 5));
796    }
797    /// Test PolynomialInterpretation fails for a non-decreasing rule.
798    #[test]
799    fn test_polynomial_interpretation_fails() {
800        let mut interp = PolynomialInterpretation::new();
801        interp.set("f", vec![1, 1]);
802        let rule = Rule::new(
803            Term::Fun("f".into(), vec![Term::Var(0)]),
804            Term::Fun("f".into(), vec![Term::Var(0)]),
805        );
806        assert!(!interp.orients_rule(&rule, 3));
807    }
808    /// Test NarrowingSystem: simple one-step narrowing.
809    #[test]
810    fn test_narrowing_step() {
811        let mut trs = Trs::new();
812        trs.add_rule(Rule::new(f(vec![a()]), b()));
813        let mut ns = NarrowingSystem::new(trs);
814        let term = f(vec![Term::Var(0)]);
815        let steps = ns.narrow_step(&term);
816        assert!(!steps.is_empty());
817        assert!(steps.iter().any(|(_, t)| *t == b()));
818    }
819    /// Test TreeAutomaton accepts a simple term.
820    #[test]
821    fn test_tree_automaton_accepts() {
822        let mut ta = TreeAutomaton::new(2);
823        ta.add_final(1);
824        ta.add_transition("a", vec![], 0);
825        ta.add_transition("b", vec![], 0);
826        ta.add_transition("f", vec![0], 1);
827        assert!(ta.accepts(&f(vec![a()])));
828        assert!(!ta.accepts(&a()));
829    }
830    /// Test TreeAutomaton non-empty language check.
831    #[test]
832    fn test_tree_automaton_nonempty() {
833        let mut ta = TreeAutomaton::new(2);
834        ta.add_final(1);
835        ta.add_transition("a", vec![], 0);
836        ta.add_transition("f", vec![0], 1);
837        assert!(!ta.is_empty());
838    }
839    /// Test DependencyPairGraph SCC detection on a cycle.
840    #[test]
841    fn test_dependency_pair_graph_cycle() {
842        let mut graph = DependencyPairGraph::new();
843        let i = graph.add_pair("f", "g");
844        let j = graph.add_pair("g", "f");
845        graph.add_edge(i, j);
846        graph.add_edge(j, i);
847        assert!(!graph.all_sccs_trivial());
848    }
849    /// Test that PolynomialInterpretation proves termination of f(f(x)) β†’ f(x).
850    #[test]
851    fn test_polynomial_interpretation_proves_termination() {
852        let mut interp = PolynomialInterpretation::new();
853        interp.set("f", vec![1, 1]);
854        let mut trs = Trs::new();
855        trs.add_rule(Rule::new(f(vec![f(vec![x0()])]), f(vec![x0()])));
856        assert!(interp.proves_termination(&trs, 5));
857    }
858    /// Test TreeAutomaton empty language detection.
859    #[test]
860    fn test_tree_automaton_empty_language() {
861        let mut ta = TreeAutomaton::new(2);
862        ta.add_final(1);
863        assert!(ta.is_empty());
864    }
865    /// Test NarrowingSystem basic_narrow with depth 0 returns input term.
866    #[test]
867    fn test_narrowing_depth_zero() {
868        let trs = Trs::new();
869        let mut ns = NarrowingSystem::new(trs);
870        let term = f(vec![a()]);
871        let results = ns.basic_narrow(&term, 0);
872        assert_eq!(results.len(), 1);
873        assert_eq!(results[0].1, term);
874    }
875}
876#[cfg(test)]
877mod tests_term_rewriting_ext {
878    use super::*;
879    #[test]
880    fn test_string_rewriting() {
881        let mut srs = StringRewritingSystem::new(vec!['a', 'b']);
882        srs.add_rule("aa", "a");
883        srs.add_rule("bb", "b");
884        let result = srs.normalize("aabb", 10);
885        assert_eq!(result, "ab", "aabb -> ab after normalizing");
886    }
887    #[test]
888    fn test_string_rewriting_no_rule() {
889        let srs = StringRewritingSystem::new(vec!['a', 'b']);
890        let result = srs.normalize("abc", 10);
891        assert_eq!(result, "abc");
892        assert_eq!(srs.num_rules(), 0);
893    }
894    #[test]
895    fn test_egraph_union_find() {
896        let mut eg = EGraph::new();
897        let n1 = eg.add_node("x");
898        let n2 = eg.add_node("y");
899        let n3 = eg.add_node("x+1");
900        assert!(!eg.are_equal(n1, n2));
901        eg.union(n1, n2);
902        assert!(eg.are_equal(n1, n2));
903        assert!(!eg.are_equal(n1, n3));
904        assert_eq!(eg.num_classes(), 3);
905    }
906    #[test]
907    fn test_rewriting_logic_theory() {
908        let mut th = RewritingLogicTheory::new();
909        th.add_sort("State");
910        th.add_equation("0 + x", "x");
911        th.add_rw_rule("step", "s β†’ t", "t");
912        assert!(!th.is_equational());
913        assert_eq!(th.signature_size(), 2);
914        assert!(th.entailment_description().contains("1 sorts"));
915    }
916    #[test]
917    fn test_string_rewriting_equal_modulo() {
918        let mut srs = StringRewritingSystem::new(vec!['a', 'b']);
919        srs.add_rule("ab", "ba");
920        assert!(srs.are_equal_modulo("ab", "ba", 5));
921    }
922}
923#[cfg(test)]
924mod tests_term_rewriting_ext2 {
925    use super::*;
926    #[test]
927    fn test_knuth_bendix() {
928        let mut kb = KnuthBendixData::new("LPO");
929        kb.add_oriented_rule("i(i(x))", "x");
930        kb.add_oriented_rule("e * x", "x");
931        kb.add_critical_pair("i(i(x))", "x");
932        kb.mark_confluent();
933        assert!(kb.is_confluent);
934        assert_eq!(kb.num_rules(), 2);
935        assert!(kb.description().contains("confluent=true"));
936    }
937}
938#[cfg(test)]
939mod tests_term_rewriting_ext3 {
940    use super::*;
941    #[test]
942    fn test_reduction_strategy() {
943        let s = ReductionStrategy::LeftmostOutermost;
944        assert!(s.is_complete());
945        assert!(s.normalizing_for_orthogonal());
946        assert!(s.name().contains("Normal"));
947        let li = ReductionStrategy::LeftmostInnermost;
948        assert!(!li.is_complete());
949        assert_eq!(li.lambda_calculus_analog(), "Call-by-value");
950        let needed = ReductionStrategy::Needed;
951        assert!(needed.is_complete());
952        assert_eq!(needed.lambda_calculus_analog(), "Call-by-need (lazy)");
953    }
954}