Skip to main content

oxilean_std/sum/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use oxilean_kernel::{
6    BinderInfo, Declaration, Environment, Expr, InductiveEnv, InductiveType, IntroRule, Level, Name,
7};
8
9use super::types::{Coproduct, InjectionMap, Pair, Sum3, Sum4, SumChain, SumStats, Tagged};
10
11/// Build Sum type in the environment.
12pub fn build_sum_env(env: &mut Environment, ind_env: &mut InductiveEnv) -> Result<(), String> {
13    let type1 = Expr::Sort(Level::succ(Level::zero()));
14    let sum_ty = Expr::Pi(
15        BinderInfo::Default,
16        Name::str("α"),
17        Box::new(type1.clone()),
18        Box::new(Expr::Pi(
19            BinderInfo::Default,
20            Name::str("β"),
21            Box::new(type1.clone()),
22            Box::new(type1.clone()),
23        )),
24    );
25    let inl_ty = Expr::Pi(
26        BinderInfo::Implicit,
27        Name::str("α"),
28        Box::new(type1.clone()),
29        Box::new(Expr::Pi(
30            BinderInfo::Implicit,
31            Name::str("β"),
32            Box::new(type1.clone()),
33            Box::new(Expr::Pi(
34                BinderInfo::Default,
35                Name::str("a"),
36                Box::new(Expr::BVar(1)),
37                Box::new(Expr::App(
38                    Box::new(Expr::App(
39                        Box::new(Expr::Const(Name::str("Sum"), vec![])),
40                        Box::new(Expr::BVar(2)),
41                    )),
42                    Box::new(Expr::BVar(1)),
43                )),
44            )),
45        )),
46    );
47    let inr_ty = Expr::Pi(
48        BinderInfo::Implicit,
49        Name::str("α"),
50        Box::new(type1.clone()),
51        Box::new(Expr::Pi(
52            BinderInfo::Implicit,
53            Name::str("β"),
54            Box::new(type1.clone()),
55            Box::new(Expr::Pi(
56                BinderInfo::Default,
57                Name::str("b"),
58                Box::new(Expr::BVar(0)),
59                Box::new(Expr::App(
60                    Box::new(Expr::App(
61                        Box::new(Expr::Const(Name::str("Sum"), vec![])),
62                        Box::new(Expr::BVar(2)),
63                    )),
64                    Box::new(Expr::BVar(1)),
65                )),
66            )),
67        )),
68    );
69    let sum_ind = InductiveType::new(
70        Name::str("Sum"),
71        vec![],
72        2,
73        0,
74        sum_ty.clone(),
75        vec![
76            IntroRule {
77                name: Name::str("Sum.inl"),
78                ty: inl_ty.clone(),
79            },
80            IntroRule {
81                name: Name::str("Sum.inr"),
82                ty: inr_ty.clone(),
83            },
84        ],
85    );
86    ind_env.add(sum_ind).map_err(|e| format!("{}", e))?;
87    env.add(Declaration::Axiom {
88        name: Name::str("Sum"),
89        univ_params: vec![],
90        ty: sum_ty,
91    })
92    .map_err(|e| e.to_string())?;
93    env.add(Declaration::Axiom {
94        name: Name::str("Sum.inl"),
95        univ_params: vec![],
96        ty: inl_ty,
97    })
98    .map_err(|e| e.to_string())?;
99    env.add(Declaration::Axiom {
100        name: Name::str("Sum.inr"),
101        univ_params: vec![],
102        ty: inr_ty,
103    })
104    .map_err(|e| e.to_string())?;
105    build_sum_combinators(env)?;
106    Ok(())
107}
108/// Build Sum eliminator and combinator axioms.
109pub fn build_sum_combinators(env: &mut Environment) -> Result<(), String> {
110    let type1 = Expr::Sort(Level::succ(Level::zero()));
111    let elim_ty = Expr::Pi(
112        BinderInfo::Implicit,
113        Name::str("α"),
114        Box::new(type1.clone()),
115        Box::new(Expr::Pi(
116            BinderInfo::Implicit,
117            Name::str("β"),
118            Box::new(type1.clone()),
119            Box::new(Expr::Pi(
120                BinderInfo::Implicit,
121                Name::str("γ"),
122                Box::new(type1.clone()),
123                Box::new(Expr::Pi(
124                    BinderInfo::Default,
125                    Name::str("f"),
126                    Box::new(Expr::Pi(
127                        BinderInfo::Default,
128                        Name::str("_"),
129                        Box::new(Expr::BVar(2)),
130                        Box::new(Expr::BVar(1)),
131                    )),
132                    Box::new(Expr::Pi(
133                        BinderInfo::Default,
134                        Name::str("g"),
135                        Box::new(Expr::Pi(
136                            BinderInfo::Default,
137                            Name::str("_"),
138                            Box::new(Expr::BVar(2)),
139                            Box::new(Expr::BVar(2)),
140                        )),
141                        Box::new(Expr::Pi(
142                            BinderInfo::Default,
143                            Name::str("s"),
144                            Box::new(Expr::App(
145                                Box::new(Expr::App(
146                                    Box::new(Expr::Const(Name::str("Sum"), vec![])),
147                                    Box::new(Expr::BVar(4)),
148                                )),
149                                Box::new(Expr::BVar(3)),
150                            )),
151                            Box::new(Expr::BVar(3)),
152                        )),
153                    )),
154                )),
155            )),
156        )),
157    );
158    env.add(Declaration::Axiom {
159        name: Name::str("Sum.elim"),
160        univ_params: vec![],
161        ty: elim_ty,
162    })
163    .map_err(|e| e.to_string())?;
164    let map_ty = Expr::Pi(
165        BinderInfo::Implicit,
166        Name::str("α"),
167        Box::new(type1.clone()),
168        Box::new(Expr::Pi(
169            BinderInfo::Implicit,
170            Name::str("β"),
171            Box::new(type1.clone()),
172            Box::new(Expr::Pi(
173                BinderInfo::Implicit,
174                Name::str("γ"),
175                Box::new(type1.clone()),
176                Box::new(Expr::Pi(
177                    BinderInfo::Implicit,
178                    Name::str("δ"),
179                    Box::new(type1.clone()),
180                    Box::new(Expr::Pi(
181                        BinderInfo::Default,
182                        Name::str("f"),
183                        Box::new(Expr::Pi(
184                            BinderInfo::Default,
185                            Name::str("_"),
186                            Box::new(Expr::BVar(3)),
187                            Box::new(Expr::BVar(2)),
188                        )),
189                        Box::new(Expr::Pi(
190                            BinderInfo::Default,
191                            Name::str("g"),
192                            Box::new(Expr::Pi(
193                                BinderInfo::Default,
194                                Name::str("_"),
195                                Box::new(Expr::BVar(3)),
196                                Box::new(Expr::BVar(2)),
197                            )),
198                            Box::new(Expr::Pi(
199                                BinderInfo::Default,
200                                Name::str("s"),
201                                Box::new(Expr::App(
202                                    Box::new(Expr::App(
203                                        Box::new(Expr::Const(Name::str("Sum"), vec![])),
204                                        Box::new(Expr::BVar(5)),
205                                    )),
206                                    Box::new(Expr::BVar(4)),
207                                )),
208                                Box::new(Expr::App(
209                                    Box::new(Expr::App(
210                                        Box::new(Expr::Const(Name::str("Sum"), vec![])),
211                                        Box::new(Expr::BVar(4)),
212                                    )),
213                                    Box::new(Expr::BVar(3)),
214                                )),
215                            )),
216                        )),
217                    )),
218                )),
219            )),
220        )),
221    );
222    env.add(Declaration::Axiom {
223        name: Name::str("Sum.map"),
224        univ_params: vec![],
225        ty: map_ty,
226    })
227    .map_err(|e| e.to_string())?;
228    let swap_ty = Expr::Pi(
229        BinderInfo::Implicit,
230        Name::str("α"),
231        Box::new(type1.clone()),
232        Box::new(Expr::Pi(
233            BinderInfo::Implicit,
234            Name::str("β"),
235            Box::new(type1.clone()),
236            Box::new(Expr::Pi(
237                BinderInfo::Default,
238                Name::str("s"),
239                Box::new(Expr::App(
240                    Box::new(Expr::App(
241                        Box::new(Expr::Const(Name::str("Sum"), vec![])),
242                        Box::new(Expr::BVar(1)),
243                    )),
244                    Box::new(Expr::BVar(0)),
245                )),
246                Box::new(Expr::App(
247                    Box::new(Expr::App(
248                        Box::new(Expr::Const(Name::str("Sum"), vec![])),
249                        Box::new(Expr::BVar(1)),
250                    )),
251                    Box::new(Expr::BVar(2)),
252                )),
253            )),
254        )),
255    );
256    env.add(Declaration::Axiom {
257        name: Name::str("Sum.swap"),
258        univ_params: vec![],
259        ty: swap_ty,
260    })
261    .map_err(|e| e.to_string())?;
262    let is_left_ty = Expr::Pi(
263        BinderInfo::Implicit,
264        Name::str("α"),
265        Box::new(type1.clone()),
266        Box::new(Expr::Pi(
267            BinderInfo::Implicit,
268            Name::str("β"),
269            Box::new(type1.clone()),
270            Box::new(Expr::Pi(
271                BinderInfo::Default,
272                Name::str("s"),
273                Box::new(Expr::App(
274                    Box::new(Expr::App(
275                        Box::new(Expr::Const(Name::str("Sum"), vec![])),
276                        Box::new(Expr::BVar(1)),
277                    )),
278                    Box::new(Expr::BVar(0)),
279                )),
280                Box::new(Expr::Const(Name::str("Bool"), vec![])),
281            )),
282        )),
283    );
284    env.add(Declaration::Axiom {
285        name: Name::str("Sum.isLeft"),
286        univ_params: vec![],
287        ty: is_left_ty.clone(),
288    })
289    .map_err(|e| e.to_string())?;
290    env.add(Declaration::Axiom {
291        name: Name::str("Sum.isRight"),
292        univ_params: vec![],
293        ty: is_left_ty,
294    })
295    .map_err(|e| e.to_string())?;
296    Ok(())
297}
298/// Partition a vector of `Coproduct<A, B>` into `(Vec<A>, Vec<B>)`.
299pub fn partition<A, B>(xs: Vec<Coproduct<A, B>>) -> (Vec<A>, Vec<B>) {
300    let mut lefts = Vec::new();
301    let mut rights = Vec::new();
302    for x in xs {
303        match x {
304            Coproduct::Inl(a) => lefts.push(a),
305            Coproduct::Inr(b) => rights.push(b),
306        }
307    }
308    (lefts, rights)
309}
310/// Collect only the left values from a slice.
311pub fn lefts<A: Clone, B>(xs: &[Coproduct<A, B>]) -> Vec<A> {
312    xs.iter()
313        .filter_map(|c| match c {
314            Coproduct::Inl(a) => Some(a.clone()),
315            _ => None,
316        })
317        .collect()
318}
319/// Collect only the right values from a slice.
320pub fn rights<A, B: Clone>(xs: &[Coproduct<A, B>]) -> Vec<B> {
321    xs.iter()
322        .filter_map(|c| match c {
323            Coproduct::Inr(b) => Some(b.clone()),
324            _ => None,
325        })
326        .collect()
327}
328/// Map a fallible function `f: A → Result<C, E>` over a list, wrapping results in `Coproduct`.
329pub fn try_map<A, C, E>(xs: Vec<A>, f: impl Fn(A) -> Result<C, E>) -> Vec<Coproduct<E, C>> {
330    xs.into_iter()
331        .map(|a| match f(a) {
332            Ok(c) => Coproduct::Inr(c),
333            Err(e) => Coproduct::Inl(e),
334        })
335        .collect()
336}
337/// A Bifunctor trait for types `F<A, B>`.
338pub trait Bifunctor {
339    /// The left component type.
340    type Left;
341    /// The right component type.
342    type Right;
343    /// The result type after mapping both sides.
344    type Mapped<C, D>;
345    /// Map both sides of the bifunctor.
346    fn bimap_trait<C, D>(
347        self,
348        f: impl FnOnce(Self::Left) -> C,
349        g: impl FnOnce(Self::Right) -> D,
350    ) -> Self::Mapped<C, D>;
351}
352/// Distribute `Sum (A × B) (A × C) ≅ A × Sum B C`.
353pub fn distribute_left<A: Clone, B, C>(
354    s: Coproduct<Pair<A, B>, Pair<A, C>>,
355) -> Pair<A, Coproduct<B, C>> {
356    match s {
357        Coproduct::Inl(Pair { fst: a, snd: b }) => Pair {
358            fst: a,
359            snd: Coproduct::Inl(b),
360        },
361        Coproduct::Inr(Pair { fst: a, snd: c }) => Pair {
362            fst: a,
363            snd: Coproduct::Inr(c),
364        },
365    }
366}
367/// Distribute `A × Sum B C ≅ Sum (A × B) (A × C)`.
368pub fn factor_left<A: Clone, B, C>(
369    p: Pair<A, Coproduct<B, C>>,
370) -> Coproduct<Pair<A, B>, Pair<A, C>> {
371    let Pair { fst: a, snd: s } = p;
372    match s {
373        Coproduct::Inl(b) => Coproduct::Inl(Pair { fst: a, snd: b }),
374        Coproduct::Inr(c) => Coproduct::Inr(Pair { fst: a, snd: c }),
375    }
376}
377/// Build an `Expr` for `Sum.inl α β a`.
378pub fn make_sum_inl(alpha: Expr, beta: Expr, a: Expr) -> Expr {
379    let base = Expr::Const(Name::str("Sum.inl"), vec![]);
380    let e1 = Expr::App(Box::new(base), Box::new(alpha));
381    let e2 = Expr::App(Box::new(e1), Box::new(beta));
382    Expr::App(Box::new(e2), Box::new(a))
383}
384/// Build an `Expr` for `Sum.inr α β b`.
385pub fn make_sum_inr(alpha: Expr, beta: Expr, b: Expr) -> Expr {
386    let base = Expr::Const(Name::str("Sum.inr"), vec![]);
387    let e1 = Expr::App(Box::new(base), Box::new(alpha));
388    let e2 = Expr::App(Box::new(e1), Box::new(beta));
389    Expr::App(Box::new(e2), Box::new(b))
390}
391/// Build an `Expr` for `Sum α β`.
392pub fn make_sum_type(alpha: Expr, beta: Expr) -> Expr {
393    Expr::App(
394        Box::new(Expr::App(
395            Box::new(Expr::Const(Name::str("Sum"), vec![])),
396            Box::new(alpha),
397        )),
398        Box::new(beta),
399    )
400}
401/// Return all Sum-related names registered in the environment.
402pub fn registered_sum_names(env: &Environment) -> Vec<String> {
403    let candidates = [
404        "Sum",
405        "Sum.inl",
406        "Sum.inr",
407        "Sum.elim",
408        "Sum.map",
409        "Sum.swap",
410        "Sum.isLeft",
411        "Sum.isRight",
412    ];
413    candidates
414        .iter()
415        .filter(|n| env.get(&Name::str(**n)).is_some())
416        .map(|s| s.to_string())
417        .collect()
418}
419#[cfg(test)]
420mod tests {
421    use super::*;
422    fn setup_env() -> (Environment, InductiveEnv) {
423        let mut env = Environment::new();
424        let ind_env = InductiveEnv::new();
425        let type1 = Expr::Sort(Level::succ(Level::zero()));
426        env.add(Declaration::Axiom {
427            name: Name::str("Bool"),
428            univ_params: vec![],
429            ty: type1,
430        })
431        .expect("operation should succeed");
432        (env, ind_env)
433    }
434    #[test]
435    fn test_build_sum_env() {
436        let (mut env, mut ind_env) = setup_env();
437        assert!(build_sum_env(&mut env, &mut ind_env).is_ok());
438        assert!(env.get(&Name::str("Sum")).is_some());
439        assert!(env.get(&Name::str("Sum.inl")).is_some());
440        assert!(env.get(&Name::str("Sum.inr")).is_some());
441    }
442    #[test]
443    fn test_sum_inl() {
444        let (mut env, mut ind_env) = setup_env();
445        build_sum_env(&mut env, &mut ind_env).expect("build_sum_env should succeed");
446        let inl_decl = env
447            .get(&Name::str("Sum.inl"))
448            .expect("declaration 'Sum.inl' should exist in env");
449        assert!(matches!(inl_decl, Declaration::Axiom { .. }));
450    }
451    #[test]
452    fn test_sum_inr() {
453        let (mut env, mut ind_env) = setup_env();
454        build_sum_env(&mut env, &mut ind_env).expect("build_sum_env should succeed");
455        let inr_decl = env
456            .get(&Name::str("Sum.inr"))
457            .expect("declaration 'Sum.inr' should exist in env");
458        assert!(matches!(inr_decl, Declaration::Axiom { .. }));
459    }
460    #[test]
461    fn test_sum_combinators_registered() {
462        let (mut env, mut ind_env) = setup_env();
463        build_sum_env(&mut env, &mut ind_env).expect("build_sum_env should succeed");
464        assert!(env.get(&Name::str("Sum.elim")).is_some());
465        assert!(env.get(&Name::str("Sum.map")).is_some());
466        assert!(env.get(&Name::str("Sum.swap")).is_some());
467        assert!(env.get(&Name::str("Sum.isLeft")).is_some());
468        assert!(env.get(&Name::str("Sum.isRight")).is_some());
469    }
470    #[test]
471    fn test_coproduct_inl() {
472        let c: Coproduct<i32, &str> = Coproduct::inl(42);
473        assert!(c.is_left());
474        assert!(!c.is_right());
475    }
476    #[test]
477    fn test_coproduct_inr() {
478        let c: Coproduct<i32, &str> = Coproduct::inr("hello");
479        assert!(c.is_right());
480        assert!(!c.is_left());
481    }
482    #[test]
483    fn test_coproduct_elim() {
484        let l: Coproduct<i32, &str> = Coproduct::inl(5);
485        let r: Coproduct<i32, &str> = Coproduct::inr("hi");
486        assert_eq!(l.elim(|n| n * 2, |s| s.len() as i32), 10);
487        assert_eq!(r.elim(|n| n * 2, |s| s.len() as i32), 2);
488    }
489    #[test]
490    fn test_coproduct_swap() {
491        let c: Coproduct<i32, &str> = Coproduct::inl(3);
492        let swapped = c.swap();
493        assert!(swapped.is_right());
494        assert_eq!(swapped.into_right(), Some(3));
495    }
496    #[test]
497    fn test_coproduct_bimap() {
498        let c: Coproduct<i32, &str> = Coproduct::inl(5);
499        let mapped = c.bimap(|n| n * 2, |s: &str| s.len());
500        assert_eq!(mapped.into_left(), Some(10));
501        let c2: Coproduct<i32, &str> = Coproduct::inr("hello");
502        let mapped2 = c2.bimap(|n| n * 2, |s: &str| s.len());
503        assert_eq!(mapped2.into_right(), Some(5));
504    }
505    #[test]
506    fn test_coproduct_map_left() {
507        let c: Coproduct<i32, &str> = Coproduct::inl(3);
508        let m = c.map_left(|n| n + 1);
509        assert_eq!(m.into_left(), Some(4));
510    }
511    #[test]
512    fn test_coproduct_map_right() {
513        let c: Coproduct<i32, &str> = Coproduct::inr("world");
514        let m = c.map_right(|s: &str| s.to_uppercase());
515        assert_eq!(m.into_right(), Some("WORLD".to_string()));
516    }
517    #[test]
518    fn test_coproduct_into_result() {
519        let l: Coproduct<String, i32> = Coproduct::inl("err".to_string());
520        let r: Coproduct<String, i32> = Coproduct::inr(42);
521        assert!(l.into_result().is_err());
522        assert_eq!(r.into_result().expect("into_result should succeed"), 42);
523    }
524    #[test]
525    fn test_coproduct_from_result() {
526        let ok: Result<i32, String> = Ok(5);
527        let err: Result<i32, String> = Err("oops".to_string());
528        assert!(Coproduct::from_result(ok).is_right());
529        assert!(Coproduct::from_result(err).is_left());
530    }
531    #[test]
532    fn test_partition() {
533        let xs: Vec<Coproduct<i32, &str>> = vec![
534            Coproduct::inl(1),
535            Coproduct::inr("a"),
536            Coproduct::inl(2),
537            Coproduct::inr("b"),
538        ];
539        let (ls, rs) = partition(xs);
540        assert_eq!(ls, vec![1, 2]);
541        assert_eq!(rs, vec!["a", "b"]);
542    }
543    #[test]
544    fn test_lefts_rights() {
545        let xs: Vec<Coproduct<i32, &str>> =
546            vec![Coproduct::inl(10), Coproduct::inr("x"), Coproduct::inl(20)];
547        assert_eq!(lefts(&xs), vec![10, 20]);
548        assert_eq!(rights(&xs), vec!["x"]);
549    }
550    #[test]
551    fn test_try_map() {
552        let xs = vec![2, 0, 4];
553        let result = try_map(xs, |n: i32| if n == 0 { Err("zero") } else { Ok(100 / n) });
554        assert!(result[0].is_right());
555        assert!(result[1].is_left());
556        assert!(result[2].is_right());
557    }
558    #[test]
559    fn test_sum3_elim() {
560        let s: Sum3<i32, &str, bool> = Sum3::In1(7);
561        let result = s.elim(
562            |n| n * 2,
563            |s: &str| s.len() as i32,
564            |b| if b { 1 } else { 0 },
565        );
566        assert_eq!(result, 14);
567        let s2: Sum3<i32, &str, bool> = Sum3::In3(true);
568        let result2 = s2.elim(
569            |n| n * 2,
570            |s: &str| s.len() as i32,
571            |b| if b { 1 } else { 0 },
572        );
573        assert_eq!(result2, 1);
574    }
575    #[test]
576    fn test_sum3_predicates() {
577        let s1: Sum3<i32, &str, bool> = Sum3::In1(1);
578        let s2: Sum3<i32, &str, bool> = Sum3::In2("hi");
579        let s3: Sum3<i32, &str, bool> = Sum3::In3(false);
580        assert!(s1.is_first());
581        assert!(s2.is_second());
582        assert!(s3.is_third());
583    }
584    #[test]
585    fn test_pair_operations() {
586        let p = Pair::new(1, "hello");
587        let swapped = p.swap();
588        assert_eq!(swapped.fst, "hello");
589        assert_eq!(swapped.snd, 1);
590        let p2 = Pair::new(3, 4);
591        let mapped = p2.map_fst(|n| n * 10);
592        assert_eq!(mapped.fst, 30);
593        assert_eq!(mapped.snd, 4);
594    }
595    #[test]
596    fn test_pair_tuple_round_trip() {
597        let p = Pair::from_tuple((42, "test"));
598        let t = p.into_tuple();
599        assert_eq!(t, (42, "test"));
600    }
601    #[test]
602    fn test_distribute_left() {
603        let s: Coproduct<Pair<i32, &str>, Pair<i32, bool>> = Coproduct::Inl(Pair::new(5, "hello"));
604        let p = distribute_left(s);
605        assert_eq!(p.fst, 5);
606        assert!(p.snd.is_left());
607    }
608    #[test]
609    fn test_factor_left() {
610        let p = Pair::new(10i32, Coproduct::<&str, bool>::Inr(true));
611        let s = factor_left(p);
612        assert!(s.is_right());
613        let inner = s.into_right().expect("into_right should succeed");
614        assert_eq!(inner.fst, 10);
615    }
616    #[test]
617    fn test_tagged_map() {
618        let t = Tagged::new("label", 5);
619        let t2 = t.map(|n| n * 2);
620        assert_eq!(t2.tag, "label");
621        assert_eq!(t2.value, 10);
622    }
623    #[test]
624    fn test_tagged_map_tag() {
625        let t = Tagged::new(1u32, "value");
626        let t2 = t.map_tag(|n| n.to_string());
627        assert_eq!(t2.tag, "1");
628        assert_eq!(t2.value, "value");
629    }
630    #[test]
631    fn test_make_sum_type_expr() {
632        let alpha = Expr::Const(Name::str("Nat"), vec![]);
633        let beta = Expr::Const(Name::str("Bool"), vec![]);
634        let sum_ty = make_sum_type(alpha, beta);
635        assert!(matches!(sum_ty, Expr::App(_, _)));
636    }
637    #[test]
638    fn test_registered_sum_names() {
639        let (mut env, mut ind_env) = setup_env();
640        build_sum_env(&mut env, &mut ind_env).expect("build_sum_env should succeed");
641        let names = registered_sum_names(&env);
642        assert!(names.contains(&"Sum".to_string()));
643        assert!(names.contains(&"Sum.inl".to_string()));
644        assert!(names.contains(&"Sum.inr".to_string()));
645        assert!(names.len() >= 5);
646    }
647}
648/// A `Coproduct<E, A>` with error-on-left semantics.
649pub type SumResult<E, A> = Coproduct<E, A>;
650/// Lift a `Result<A, E>` into a `SumResult<E, A>`.
651pub fn lift_result<A, E>(r: Result<A, E>) -> SumResult<E, A> {
652    Coproduct::from_result(r)
653}
654/// Sequence a list of `SumResult`s, stopping at the first error.
655pub fn sequence_sum<A: Clone, E: Clone>(xs: Vec<SumResult<E, A>>) -> SumResult<E, Vec<A>> {
656    let mut results = Vec::new();
657    for x in xs {
658        match x {
659            Coproduct::Inl(e) => return Coproduct::Inl(e),
660            Coproduct::Inr(a) => results.push(a),
661        }
662    }
663    Coproduct::Inr(results)
664}
665/// Traverse a list with a fallible function, stopping at the first error.
666pub fn traverse_sum<A, B: Clone, E: Clone>(
667    xs: Vec<A>,
668    f: impl Fn(A) -> SumResult<E, B>,
669) -> SumResult<E, Vec<B>> {
670    sequence_sum(xs.into_iter().map(f).collect())
671}
672#[cfg(test)]
673mod sum_extra_tests {
674    use super::*;
675    #[test]
676    fn test_sum_chain_map_right() {
677        let c: Coproduct<String, i32> = Coproduct::inr(5);
678        let chain = SumChain::new(c).map(|n| n * 2);
679        assert!(chain.is_ok());
680        assert_eq!(chain.into_inner().into_right(), Some(10));
681    }
682    #[test]
683    fn test_sum_chain_short_circuit_left() {
684        let c: Coproduct<String, i32> = Coproduct::inl("error".to_string());
685        let chain = SumChain::new(c).map(|n: i32| n * 2);
686        assert!(chain.is_err());
687    }
688    #[test]
689    fn test_sum_chain_flat_map() {
690        let c: Coproduct<String, i32> = Coproduct::inr(5);
691        let chain = SumChain::new(c).flat_map(|n| {
692            if n > 0 {
693                Coproduct::inr(n * 10)
694            } else {
695                Coproduct::inl("negative".to_string())
696            }
697        });
698        assert!(chain.is_ok());
699        assert_eq!(chain.into_inner().into_right(), Some(50));
700    }
701    #[test]
702    fn test_sum_stats_from_slice() {
703        let xs: Vec<Coproduct<i32, &str>> =
704            vec![Coproduct::inl(1), Coproduct::inr("a"), Coproduct::inl(2)];
705        let stats = SumStats::from_slice(&xs);
706        assert_eq!(stats.left_count, 2);
707        assert_eq!(stats.right_count, 1);
708        assert_eq!(stats.total(), 3);
709    }
710    #[test]
711    fn test_sum_stats_all_left() {
712        let xs: Vec<Coproduct<i32, &str>> = vec![Coproduct::inl(1), Coproduct::inl(2)];
713        let stats = SumStats::from_slice(&xs);
714        assert!(stats.all_left());
715    }
716    #[test]
717    fn test_sum_stats_all_right() {
718        let xs: Vec<Coproduct<i32, &str>> = vec![Coproduct::inr("x"), Coproduct::inr("y")];
719        let stats = SumStats::from_slice(&xs);
720        assert!(stats.all_right());
721    }
722    #[test]
723    fn test_sum4_elim() {
724        let s: Sum4<i32, &str, bool, f32> = Sum4::In3(true);
725        let result: i32 = s.elim(|n| n, |_| 0, |b| if b { 1 } else { 0 }, |_| -1);
726        assert_eq!(result, 1);
727    }
728    #[test]
729    fn test_sum4_tag() {
730        let s: Sum4<i32, &str, bool, f32> = Sum4::In2("hi");
731        assert_eq!(s.tag(), 2);
732        assert!(s.is_second());
733    }
734    #[test]
735    fn test_lift_result_ok() {
736        let r: Result<i32, String> = Ok(42);
737        assert!(lift_result(r).is_right());
738    }
739    #[test]
740    fn test_lift_result_err() {
741        let r: Result<i32, String> = Err("oops".to_string());
742        assert!(lift_result(r).is_left());
743    }
744    #[test]
745    fn test_sequence_sum_all_ok() {
746        let xs: Vec<SumResult<String, i32>> = vec![Coproduct::inr(1), Coproduct::inr(2)];
747        let result = sequence_sum(xs);
748        assert_eq!(result.into_right(), Some(vec![1, 2]));
749    }
750    #[test]
751    fn test_sequence_sum_first_error() {
752        let xs: Vec<SumResult<String, i32>> =
753            vec![Coproduct::inr(1), Coproduct::inl("err".to_string())];
754        let result = sequence_sum(xs);
755        assert!(result.is_left());
756    }
757    #[test]
758    fn test_traverse_sum_success() {
759        let xs = vec![1i32, 2, 3];
760        let result = traverse_sum(xs, |n| -> SumResult<String, i32> { Coproduct::inr(n * 2) });
761        assert_eq!(result.into_right(), Some(vec![2, 4, 6]));
762    }
763    #[test]
764    fn test_traverse_sum_error() {
765        let xs = vec![1i32, 0, 3];
766        let result = traverse_sum(xs, |n| -> SumResult<String, i32> {
767            if n == 0 {
768                Coproduct::inl("zero".to_string())
769            } else {
770                Coproduct::inr(100 / n)
771            }
772        });
773        assert!(result.is_left());
774    }
775    #[test]
776    fn test_injection_map() {
777        let mut map = InjectionMap::new();
778        map.register(1, "Left");
779        map.register(2, "Right");
780        assert_eq!(map.get(1), Some("Left"));
781        assert!(map.get(3).is_none());
782        assert_eq!(map.len(), 2);
783    }
784    #[test]
785    fn test_sum4_is_fourth() {
786        let s: Sum4<i32, &str, bool, f64> = Sum4::In4(2.72);
787        assert!(s.is_fourth());
788        assert!(!s.is_first());
789    }
790    #[test]
791    fn test_sum_chain_flat_map_short_circuit() {
792        let c: Coproduct<String, i32> = Coproduct::inr(-5);
793        let chain = SumChain::new(c).flat_map(|n| -> Coproduct<String, i32> {
794            if n > 0 {
795                Coproduct::inr(n)
796            } else {
797                Coproduct::inl("negative".to_string())
798            }
799        });
800        assert!(chain.is_err());
801    }
802}
803/// Build axiom: Sum is categorical coproduct.
804pub(super) fn sm_ext_sum_categorical_coproduct(env: &mut Environment) -> Result<(), String> {
805    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
806    let type1 = Expr::Sort(Level::succ(Level::zero()));
807    let prop = Expr::Sort(Level::zero());
808    let ty = Expr::Pi(
809        Bi::Implicit,
810        Name::str("A"),
811        Box::new(type1.clone()),
812        Box::new(Expr::Pi(
813            Bi::Implicit,
814            Name::str("B"),
815            Box::new(type1.clone()),
816            Box::new(Expr::Pi(
817                Bi::Implicit,
818                Name::str("Z"),
819                Box::new(type1),
820                Box::new(prop),
821            )),
822        )),
823    );
824    match env.add(Declaration::Axiom {
825        name: Name::str("Sum.categoricalCoproduct"),
826        univ_params: vec![],
827        ty,
828    }) {
829        Ok(()) | Err(_) => Ok(()),
830    }
831}
832/// Build axiom: Sum universal property (unique factorization).
833pub(super) fn sm_ext_sum_universal_property(env: &mut Environment) -> Result<(), String> {
834    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
835    let type1 = Expr::Sort(Level::succ(Level::zero()));
836    let prop = Expr::Sort(Level::zero());
837    let ty = Expr::Pi(
838        Bi::Implicit,
839        Name::str("A"),
840        Box::new(type1.clone()),
841        Box::new(Expr::Pi(
842            Bi::Implicit,
843            Name::str("B"),
844            Box::new(type1.clone()),
845            Box::new(Expr::Pi(
846                Bi::Implicit,
847                Name::str("Z"),
848                Box::new(type1),
849                Box::new(Expr::Pi(
850                    Bi::Default,
851                    Name::str("f"),
852                    Box::new(Expr::Pi(
853                        Bi::Default,
854                        Name::str("_"),
855                        Box::new(Expr::BVar(2)),
856                        Box::new(Expr::BVar(1)),
857                    )),
858                    Box::new(Expr::Pi(
859                        Bi::Default,
860                        Name::str("g"),
861                        Box::new(Expr::Pi(
862                            Bi::Default,
863                            Name::str("_"),
864                            Box::new(Expr::BVar(2)),
865                            Box::new(Expr::BVar(2)),
866                        )),
867                        Box::new(prop),
868                    )),
869                )),
870            )),
871        )),
872    );
873    match env.add(Declaration::Axiom {
874        name: Name::str("Sum.universalProperty"),
875        univ_params: vec![],
876        ty,
877    }) {
878        Ok(()) | Err(_) => Ok(()),
879    }
880}
881/// Build axiom: inl injection law (inl is injective).
882pub(super) fn sm_ext_inl_injective(env: &mut Environment) -> Result<(), String> {
883    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
884    let type1 = Expr::Sort(Level::succ(Level::zero()));
885    let prop = Expr::Sort(Level::zero());
886    let ty = Expr::Pi(
887        Bi::Implicit,
888        Name::str("A"),
889        Box::new(type1.clone()),
890        Box::new(Expr::Pi(
891            Bi::Implicit,
892            Name::str("B"),
893            Box::new(type1),
894            Box::new(Expr::Pi(
895                Bi::Default,
896                Name::str("a1"),
897                Box::new(Expr::BVar(1)),
898                Box::new(Expr::Pi(
899                    Bi::Default,
900                    Name::str("a2"),
901                    Box::new(Expr::BVar(2)),
902                    Box::new(prop),
903                )),
904            )),
905        )),
906    );
907    match env.add(Declaration::Axiom {
908        name: Name::str("Sum.inlInjective"),
909        univ_params: vec![],
910        ty,
911    }) {
912        Ok(()) | Err(_) => Ok(()),
913    }
914}
915/// Build axiom: inr injection law (inr is injective).
916pub(super) fn sm_ext_inr_injective(env: &mut Environment) -> Result<(), String> {
917    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
918    let type1 = Expr::Sort(Level::succ(Level::zero()));
919    let prop = Expr::Sort(Level::zero());
920    let ty = Expr::Pi(
921        Bi::Implicit,
922        Name::str("A"),
923        Box::new(type1.clone()),
924        Box::new(Expr::Pi(
925            Bi::Implicit,
926            Name::str("B"),
927            Box::new(type1),
928            Box::new(Expr::Pi(
929                Bi::Default,
930                Name::str("b1"),
931                Box::new(Expr::BVar(0)),
932                Box::new(Expr::Pi(
933                    Bi::Default,
934                    Name::str("b2"),
935                    Box::new(Expr::BVar(1)),
936                    Box::new(prop),
937                )),
938            )),
939        )),
940    );
941    match env.add(Declaration::Axiom {
942        name: Name::str("Sum.inrInjective"),
943        univ_params: vec![],
944        ty,
945    }) {
946        Ok(()) | Err(_) => Ok(()),
947    }
948}
949/// Build axiom: inl and inr are disjoint (no overlap).
950pub(super) fn sm_ext_inl_inr_disjoint(env: &mut Environment) -> Result<(), String> {
951    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
952    let type1 = Expr::Sort(Level::succ(Level::zero()));
953    let prop = Expr::Sort(Level::zero());
954    let ty = Expr::Pi(
955        Bi::Implicit,
956        Name::str("A"),
957        Box::new(type1.clone()),
958        Box::new(Expr::Pi(
959            Bi::Implicit,
960            Name::str("B"),
961            Box::new(type1),
962            Box::new(Expr::Pi(
963                Bi::Default,
964                Name::str("a"),
965                Box::new(Expr::BVar(1)),
966                Box::new(Expr::Pi(
967                    Bi::Default,
968                    Name::str("b"),
969                    Box::new(Expr::BVar(1)),
970                    Box::new(prop),
971                )),
972            )),
973        )),
974    );
975    match env.add(Declaration::Axiom {
976        name: Name::str("Sum.inlInrDisjoint"),
977        univ_params: vec![],
978        ty,
979    }) {
980        Ok(()) | Err(_) => Ok(()),
981    }
982}
983/// Build axiom: Sum functor preserves identity.
984pub(super) fn sm_ext_sum_functor_id(env: &mut Environment) -> Result<(), String> {
985    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
986    let type1 = Expr::Sort(Level::succ(Level::zero()));
987    let prop = Expr::Sort(Level::zero());
988    let ty = Expr::Pi(
989        Bi::Implicit,
990        Name::str("A"),
991        Box::new(type1.clone()),
992        Box::new(Expr::Pi(
993            Bi::Implicit,
994            Name::str("B"),
995            Box::new(type1),
996            Box::new(prop),
997        )),
998    );
999    match env.add(Declaration::Axiom {
1000        name: Name::str("Sum.functorId"),
1001        univ_params: vec![],
1002        ty,
1003    }) {
1004        Ok(()) | Err(_) => Ok(()),
1005    }
1006}
1007/// Build axiom: Sum functor preserves composition.
1008pub(super) fn sm_ext_sum_functor_compose(env: &mut Environment) -> Result<(), String> {
1009    use oxilean_kernel::{Declaration, Expr, Name};
1010    let prop = Expr::Sort(oxilean_kernel::Level::zero());
1011    match env.add(Declaration::Axiom {
1012        name: Name::str("Sum.functorCompose"),
1013        univ_params: vec![],
1014        ty: prop,
1015    }) {
1016        Ok(()) | Err(_) => Ok(()),
1017    }
1018}
1019/// Build axiom: Sum bifunctor law (bimap identity).
1020pub(super) fn sm_ext_sum_bifunctor_id(env: &mut Environment) -> Result<(), String> {
1021    use oxilean_kernel::{Declaration, Expr, Name};
1022    let prop = Expr::Sort(oxilean_kernel::Level::zero());
1023    match env.add(Declaration::Axiom {
1024        name: Name::str("Sum.bifunctorId"),
1025        univ_params: vec![],
1026        ty: prop,
1027    }) {
1028        Ok(()) | Err(_) => Ok(()),
1029    }
1030}
1031/// Build axiom: Sum monad return (fixed Left = error type).
1032pub(super) fn sm_ext_sum_monad_return(env: &mut Environment) -> Result<(), String> {
1033    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
1034    let type1 = Expr::Sort(Level::succ(Level::zero()));
1035    let ty = Expr::Pi(
1036        Bi::Implicit,
1037        Name::str("E"),
1038        Box::new(type1.clone()),
1039        Box::new(Expr::Pi(
1040            Bi::Implicit,
1041            Name::str("A"),
1042            Box::new(type1),
1043            Box::new(Expr::Pi(
1044                Bi::Default,
1045                Name::str("a"),
1046                Box::new(Expr::BVar(0)),
1047                Box::new(Expr::App(
1048                    Box::new(Expr::App(
1049                        Box::new(Expr::Const(Name::str("Sum"), vec![])),
1050                        Box::new(Expr::BVar(2)),
1051                    )),
1052                    Box::new(Expr::BVar(1)),
1053                )),
1054            )),
1055        )),
1056    );
1057    match env.add(Declaration::Axiom {
1058        name: Name::str("Sum.monadReturn"),
1059        univ_params: vec![],
1060        ty,
1061    }) {
1062        Ok(()) | Err(_) => Ok(()),
1063    }
1064}
1065/// Build axiom: Sum monad bind (left short-circuits).
1066pub(super) fn sm_ext_sum_monad_bind(env: &mut Environment) -> Result<(), String> {
1067    use oxilean_kernel::{Declaration, Expr, Name};
1068    let prop = Expr::Sort(oxilean_kernel::Level::zero());
1069    match env.add(Declaration::Axiom {
1070        name: Name::str("Sum.monadBind"),
1071        univ_params: vec![],
1072        ty: prop,
1073    }) {
1074        Ok(()) | Err(_) => Ok(()),
1075    }
1076}
1077/// Build axiom: Sum monad left identity law.
1078pub(super) fn sm_ext_sum_monad_left_id(env: &mut Environment) -> Result<(), String> {
1079    use oxilean_kernel::{Declaration, Expr, Name};
1080    let prop = Expr::Sort(oxilean_kernel::Level::zero());
1081    match env.add(Declaration::Axiom {
1082        name: Name::str("Sum.monadLeftId"),
1083        univ_params: vec![],
1084        ty: prop,
1085    }) {
1086        Ok(()) | Err(_) => Ok(()),
1087    }
1088}
1089/// Build axiom: Sum monad right identity law.
1090pub(super) fn sm_ext_sum_monad_right_id(env: &mut Environment) -> Result<(), String> {
1091    use oxilean_kernel::{Declaration, Expr, Name};
1092    let prop = Expr::Sort(oxilean_kernel::Level::zero());
1093    match env.add(Declaration::Axiom {
1094        name: Name::str("Sum.monadRightId"),
1095        univ_params: vec![],
1096        ty: prop,
1097    }) {
1098        Ok(()) | Err(_) => Ok(()),
1099    }
1100}
1101/// Build axiom: Sum monad associativity law.
1102pub(super) fn sm_ext_sum_monad_assoc(env: &mut Environment) -> Result<(), String> {
1103    use oxilean_kernel::{Declaration, Expr, Name};
1104    let prop = Expr::Sort(oxilean_kernel::Level::zero());
1105    match env.add(Declaration::Axiom {
1106        name: Name::str("Sum.monadAssoc"),
1107        univ_params: vec![],
1108        ty: prop,
1109    }) {
1110        Ok(()) | Err(_) => Ok(()),
1111    }
1112}
1113/// Build axiom: Sum applicative pure.
1114pub(super) fn sm_ext_sum_applicative_pure(env: &mut Environment) -> Result<(), String> {
1115    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
1116    let type1 = Expr::Sort(Level::succ(Level::zero()));
1117    let ty = Expr::Pi(
1118        Bi::Implicit,
1119        Name::str("E"),
1120        Box::new(type1.clone()),
1121        Box::new(Expr::Pi(
1122            Bi::Implicit,
1123            Name::str("A"),
1124            Box::new(type1),
1125            Box::new(Expr::Pi(
1126                Bi::Default,
1127                Name::str("a"),
1128                Box::new(Expr::BVar(0)),
1129                Box::new(Expr::App(
1130                    Box::new(Expr::App(
1131                        Box::new(Expr::Const(Name::str("Sum"), vec![])),
1132                        Box::new(Expr::BVar(2)),
1133                    )),
1134                    Box::new(Expr::BVar(1)),
1135                )),
1136            )),
1137        )),
1138    );
1139    match env.add(Declaration::Axiom {
1140        name: Name::str("Sum.applicativePure"),
1141        univ_params: vec![],
1142        ty,
1143    }) {
1144        Ok(()) | Err(_) => Ok(()),
1145    }
1146}
1147/// Build axiom: Sum applicative ap (sequential application).
1148pub(super) fn sm_ext_sum_applicative_ap(env: &mut Environment) -> Result<(), String> {
1149    use oxilean_kernel::{Declaration, Expr, Name};
1150    let prop = Expr::Sort(oxilean_kernel::Level::zero());
1151    match env.add(Declaration::Axiom {
1152        name: Name::str("Sum.applicativeAp"),
1153        univ_params: vec![],
1154        ty: prop,
1155    }) {
1156        Ok(()) | Err(_) => Ok(()),
1157    }
1158}
1159/// Build axiom: Sum commutativity iso (swap is involution).
1160pub(super) fn sm_ext_sum_swap_involution(env: &mut Environment) -> Result<(), String> {
1161    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
1162    let type1 = Expr::Sort(Level::succ(Level::zero()));
1163    let prop = Expr::Sort(Level::zero());
1164    let sum_ab = Expr::App(
1165        Box::new(Expr::App(
1166            Box::new(Expr::Const(Name::str("Sum"), vec![])),
1167            Box::new(Expr::BVar(1)),
1168        )),
1169        Box::new(Expr::BVar(0)),
1170    );
1171    let ty = Expr::Pi(
1172        Bi::Implicit,
1173        Name::str("A"),
1174        Box::new(type1.clone()),
1175        Box::new(Expr::Pi(
1176            Bi::Implicit,
1177            Name::str("B"),
1178            Box::new(type1),
1179            Box::new(Expr::Pi(
1180                Bi::Default,
1181                Name::str("s"),
1182                Box::new(sum_ab),
1183                Box::new(prop),
1184            )),
1185        )),
1186    );
1187    match env.add(Declaration::Axiom {
1188        name: Name::str("Sum.swapInvolution"),
1189        univ_params: vec![],
1190        ty,
1191    }) {
1192        Ok(()) | Err(_) => Ok(()),
1193    }
1194}
1195/// Build axiom: Sum associativity iso.
1196pub(super) fn sm_ext_sum_assoc_iso(env: &mut Environment) -> Result<(), String> {
1197    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
1198    let type1 = Expr::Sort(Level::succ(Level::zero()));
1199    let ty = Expr::Pi(
1200        Bi::Implicit,
1201        Name::str("A"),
1202        Box::new(type1.clone()),
1203        Box::new(Expr::Pi(
1204            Bi::Implicit,
1205            Name::str("B"),
1206            Box::new(type1.clone()),
1207            Box::new(Expr::Pi(
1208                Bi::Implicit,
1209                Name::str("C"),
1210                Box::new(type1),
1211                Box::new(Expr::Const(Name::str("Prop"), vec![])),
1212            )),
1213        )),
1214    );
1215    match env.add(Declaration::Axiom {
1216        name: Name::str("Sum.assocIso"),
1217        univ_params: vec![],
1218        ty,
1219    }) {
1220        Ok(()) | Err(_) => Ok(()),
1221    }
1222}
1223/// Build axiom: Sum initial object (Void/Empty as neutral element).
1224pub(super) fn sm_ext_sum_void_initial(env: &mut Environment) -> Result<(), String> {
1225    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
1226    let type1 = Expr::Sort(Level::succ(Level::zero()));
1227    let ty = Expr::Pi(
1228        Bi::Implicit,
1229        Name::str("A"),
1230        Box::new(type1),
1231        Box::new(Expr::Const(Name::str("Prop"), vec![])),
1232    );
1233    match env.add(Declaration::Axiom {
1234        name: Name::str("Sum.voidInitial"),
1235        univ_params: vec![],
1236        ty,
1237    }) {
1238        Ok(()) | Err(_) => Ok(()),
1239    }
1240}
1241/// Build axiom: Sum distributivity over product.
1242pub(super) fn sm_ext_sum_distributes_over_prod(env: &mut Environment) -> Result<(), String> {
1243    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
1244    let type1 = Expr::Sort(Level::succ(Level::zero()));
1245    let ty = Expr::Pi(
1246        Bi::Implicit,
1247        Name::str("A"),
1248        Box::new(type1.clone()),
1249        Box::new(Expr::Pi(
1250            Bi::Implicit,
1251            Name::str("B"),
1252            Box::new(type1.clone()),
1253            Box::new(Expr::Pi(
1254                Bi::Implicit,
1255                Name::str("C"),
1256                Box::new(type1),
1257                Box::new(Expr::Const(Name::str("Prop"), vec![])),
1258            )),
1259        )),
1260    );
1261    match env.add(Declaration::Axiom {
1262        name: Name::str("Sum.distributesOverProd"),
1263        univ_params: vec![],
1264        ty,
1265    }) {
1266        Ok(()) | Err(_) => Ok(()),
1267    }
1268}
1269/// Build axiom: Sum as disjoint union (inl and inr cover all cases).
1270pub(super) fn sm_ext_sum_disjoint_union(env: &mut Environment) -> Result<(), String> {
1271    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
1272    let type1 = Expr::Sort(Level::succ(Level::zero()));
1273    let prop = Expr::Sort(Level::zero());
1274    let sum_ab = Expr::App(
1275        Box::new(Expr::App(
1276            Box::new(Expr::Const(Name::str("Sum"), vec![])),
1277            Box::new(Expr::BVar(1)),
1278        )),
1279        Box::new(Expr::BVar(0)),
1280    );
1281    let ty = Expr::Pi(
1282        Bi::Implicit,
1283        Name::str("A"),
1284        Box::new(type1.clone()),
1285        Box::new(Expr::Pi(
1286            Bi::Implicit,
1287            Name::str("B"),
1288            Box::new(type1),
1289            Box::new(Expr::Pi(
1290                Bi::Default,
1291                Name::str("s"),
1292                Box::new(sum_ab),
1293                Box::new(prop),
1294            )),
1295        )),
1296    );
1297    match env.add(Declaration::Axiom {
1298        name: Name::str("Sum.disjointUnion"),
1299        univ_params: vec![],
1300        ty,
1301    }) {
1302        Ok(()) | Err(_) => Ok(()),
1303    }
1304}
1305/// Build axiom: Sum case analysis (eliminator law).
1306pub(super) fn sm_ext_sum_case_analysis(env: &mut Environment) -> Result<(), String> {
1307    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
1308    let type1 = Expr::Sort(Level::succ(Level::zero()));
1309    let prop = Expr::Sort(Level::zero());
1310    let ty = Expr::Pi(
1311        Bi::Implicit,
1312        Name::str("A"),
1313        Box::new(type1.clone()),
1314        Box::new(Expr::Pi(
1315            Bi::Implicit,
1316            Name::str("B"),
1317            Box::new(type1.clone()),
1318            Box::new(Expr::Pi(
1319                Bi::Implicit,
1320                Name::str("P"),
1321                Box::new(Expr::Pi(
1322                    Bi::Default,
1323                    Name::str("_"),
1324                    Box::new(Expr::App(
1325                        Box::new(Expr::App(
1326                            Box::new(Expr::Const(Name::str("Sum"), vec![])),
1327                            Box::new(Expr::BVar(1)),
1328                        )),
1329                        Box::new(Expr::BVar(0)),
1330                    )),
1331                    Box::new(type1.clone()),
1332                )),
1333                Box::new(Expr::Pi(
1334                    Bi::Default,
1335                    Name::str("hl"),
1336                    Box::new(Expr::Pi(
1337                        Bi::Default,
1338                        Name::str("a"),
1339                        Box::new(Expr::BVar(2)),
1340                        Box::new(prop.clone()),
1341                    )),
1342                    Box::new(Expr::Pi(
1343                        Bi::Default,
1344                        Name::str("hr"),
1345                        Box::new(Expr::Pi(
1346                            Bi::Default,
1347                            Name::str("b"),
1348                            Box::new(Expr::BVar(2)),
1349                            Box::new(prop.clone()),
1350                        )),
1351                        Box::new(Expr::Pi(
1352                            Bi::Default,
1353                            Name::str("s"),
1354                            Box::new(Expr::App(
1355                                Box::new(Expr::App(
1356                                    Box::new(Expr::Const(Name::str("Sum"), vec![])),
1357                                    Box::new(Expr::BVar(4)),
1358                                )),
1359                                Box::new(Expr::BVar(3)),
1360                            )),
1361                            Box::new(prop),
1362                        )),
1363                    )),
1364                )),
1365            )),
1366        )),
1367    );
1368    match env.add(Declaration::Axiom {
1369        name: Name::str("Sum.caseAnalysis"),
1370        univ_params: vec![],
1371        ty,
1372    }) {
1373        Ok(()) | Err(_) => Ok(()),
1374    }
1375}
1376/// Build axiom: Tagged union semantics (tag determines variant).
1377pub(super) fn sm_ext_tagged_union_semantics(env: &mut Environment) -> Result<(), String> {
1378    use oxilean_kernel::{Declaration, Expr, Name};
1379    let prop = Expr::Sort(oxilean_kernel::Level::zero());
1380    match env.add(Declaration::Axiom {
1381        name: Name::str("Sum.taggedUnionSemantics"),
1382        univ_params: vec![],
1383        ty: prop,
1384    }) {
1385        Ok(()) | Err(_) => Ok(()),
1386    }
1387}
1388/// Build axiom: Sum.lefts partition (lefts ++ rights covers all).
1389pub(super) fn sm_ext_sum_partition_complete(env: &mut Environment) -> Result<(), String> {
1390    use oxilean_kernel::{Declaration, Expr, Name};
1391    let prop = Expr::Sort(oxilean_kernel::Level::zero());
1392    match env.add(Declaration::Axiom {
1393        name: Name::str("Sum.partitionComplete"),
1394        univ_params: vec![],
1395        ty: prop,
1396    }) {
1397        Ok(()) | Err(_) => Ok(()),
1398    }
1399}
1400/// Build axiom: Sum.lefts count (length of lefts).
1401pub(super) fn sm_ext_sum_lefts_count(env: &mut Environment) -> Result<(), String> {
1402    use oxilean_kernel::{Declaration, Expr, Name};
1403    let prop = Expr::Sort(oxilean_kernel::Level::zero());
1404    match env.add(Declaration::Axiom {
1405        name: Name::str("Sum.leftsCount"),
1406        univ_params: vec![],
1407        ty: prop,
1408    }) {
1409        Ok(()) | Err(_) => Ok(()),
1410    }
1411}
1412/// Build axiom: Sum traversal naturality.
1413pub(super) fn sm_ext_sum_traversal_naturality(env: &mut Environment) -> Result<(), String> {
1414    use oxilean_kernel::{Declaration, Expr, Name};
1415    let prop = Expr::Sort(oxilean_kernel::Level::zero());
1416    match env.add(Declaration::Axiom {
1417        name: Name::str("Sum.traversalNaturality"),
1418        univ_params: vec![],
1419        ty: prop,
1420    }) {
1421        Ok(()) | Err(_) => Ok(()),
1422    }
1423}
1424/// Build axiom: Sum traversal preserves identity.
1425pub(super) fn sm_ext_sum_traversal_id(env: &mut Environment) -> Result<(), String> {
1426    use oxilean_kernel::{Declaration, Expr, Name};
1427    let prop = Expr::Sort(oxilean_kernel::Level::zero());
1428    match env.add(Declaration::Axiom {
1429        name: Name::str("Sum.traversalId"),
1430        univ_params: vec![],
1431        ty: prop,
1432    }) {
1433        Ok(()) | Err(_) => Ok(()),
1434    }
1435}
1436/// Build axiom: HoTT path space for Sum (encode/decode).
1437pub(super) fn sm_ext_sum_path_space(env: &mut Environment) -> Result<(), String> {
1438    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
1439    let type1 = Expr::Sort(Level::succ(Level::zero()));
1440    let prop = Expr::Sort(Level::zero());
1441    let sum_ab = Expr::App(
1442        Box::new(Expr::App(
1443            Box::new(Expr::Const(Name::str("Sum"), vec![])),
1444            Box::new(Expr::BVar(1)),
1445        )),
1446        Box::new(Expr::BVar(0)),
1447    );
1448    let ty = Expr::Pi(
1449        Bi::Implicit,
1450        Name::str("A"),
1451        Box::new(type1.clone()),
1452        Box::new(Expr::Pi(
1453            Bi::Implicit,
1454            Name::str("B"),
1455            Box::new(type1),
1456            Box::new(Expr::Pi(
1457                Bi::Default,
1458                Name::str("s"),
1459                Box::new(sum_ab.clone()),
1460                Box::new(Expr::Pi(
1461                    Bi::Default,
1462                    Name::str("t"),
1463                    Box::new(sum_ab),
1464                    Box::new(prop),
1465                )),
1466            )),
1467        )),
1468    );
1469    match env.add(Declaration::Axiom {
1470        name: Name::str("Sum.pathSpace"),
1471        univ_params: vec![],
1472        ty,
1473    }) {
1474        Ok(()) | Err(_) => Ok(()),
1475    }
1476}
1477/// Build axiom: HoTT – inl path encodes to left.
1478pub(super) fn sm_ext_sum_path_inl(env: &mut Environment) -> Result<(), String> {
1479    use oxilean_kernel::{Declaration, Expr, Name};
1480    let prop = Expr::Sort(oxilean_kernel::Level::zero());
1481    match env.add(Declaration::Axiom {
1482        name: Name::str("Sum.pathInl"),
1483        univ_params: vec![],
1484        ty: prop,
1485    }) {
1486        Ok(()) | Err(_) => Ok(()),
1487    }
1488}
1489/// Build axiom: HoTT – inr path encodes to right.
1490pub(super) fn sm_ext_sum_path_inr(env: &mut Environment) -> Result<(), String> {
1491    use oxilean_kernel::{Declaration, Expr, Name};
1492    let prop = Expr::Sort(oxilean_kernel::Level::zero());
1493    match env.add(Declaration::Axiom {
1494        name: Name::str("Sum.pathInr"),
1495        univ_params: vec![],
1496        ty: prop,
1497    }) {
1498        Ok(()) | Err(_) => Ok(()),
1499    }
1500}
1501/// Build axiom: Dependent sum / Sigma type intro rule.
1502pub(super) fn sm_ext_sigma_intro(env: &mut Environment) -> Result<(), String> {
1503    use oxilean_kernel::{BinderInfo as Bi, Declaration, Expr, Level, Name};
1504    let type1 = Expr::Sort(Level::succ(Level::zero()));
1505    let ty = Expr::Pi(
1506        Bi::Default,
1507        Name::str("A"),
1508        Box::new(type1.clone()),
1509        Box::new(Expr::Pi(
1510            Bi::Default,
1511            Name::str("B"),
1512            Box::new(Expr::Pi(
1513                Bi::Default,
1514                Name::str("_"),
1515                Box::new(Expr::BVar(0)),
1516                Box::new(type1.clone()),
1517            )),
1518            Box::new(type1),
1519        )),
1520    );
1521    match env.add(Declaration::Axiom {
1522        name: Name::str("Sigma.intro"),
1523        univ_params: vec![],
1524        ty,
1525    }) {
1526        Ok(()) | Err(_) => Ok(()),
1527    }
1528}