1use crate::Node;
6use crate::{Expr, KernelError, Level, Name};
7use std::rc::Rc;
8
9use super::types::{
10 ConfigNode, FocusStack, InductiveEnv, InductiveError, InductiveFamily, InductiveType,
11 InductiveTypeBuilder, InductiveTypeInfo, IntroRule, LabelSet, NonEmptyVec, RecursorBuilder,
12 SimpleDag, SmallMap, StatSummary, TransformStat, VersionedRecord, WindowIterator,
13};
14
15pub(super) fn count_pi_args(ty: &Expr) -> u32 {
17 match ty {
18 Expr::Pi(_, _, _, body) => 1 + count_pi_args(body),
19 _ => 0,
20 }
21}
22#[allow(clippy::result_large_err)]
24pub fn check_inductive(ind: &InductiveType) -> Result<(), KernelError> {
25 for intro in &ind.intro_rules {
26 if !returns_type(&intro.ty, &ind.name) {
27 return Err(KernelError::InvalidInductive(format!(
28 "constructor {} does not return type {}",
29 intro.name, ind.name
30 )));
31 }
32 }
33 for intro in &ind.intro_rules {
34 check_positivity(&ind.name, &intro.ty)?;
35 }
36 Ok(())
37}
38fn returns_type(ty: &Expr, name: &Name) -> bool {
39 match ty {
40 Expr::Const(n, _) => n == name,
41 Expr::App(f, _) => returns_type(f, name),
42 Expr::Pi(_, _, _, cod) => returns_type(cod, name),
43 _ => false,
44 }
45}
46#[allow(clippy::result_large_err)]
47fn check_positivity(ind_name: &Name, ty: &Expr) -> Result<(), KernelError> {
48 check_positivity_rec(ind_name, ty, true)
49}
50#[allow(clippy::result_large_err)]
51fn check_positivity_rec(ind_name: &Name, ty: &Expr, positive: bool) -> Result<(), KernelError> {
52 match ty {
53 Expr::Const(n, _) => {
54 if n == ind_name && !positive {
55 return Err(KernelError::InvalidInductive(format!(
56 "negative occurrence of {} in constructor type",
57 ind_name
58 )));
59 }
60 Ok(())
61 }
62 Expr::App(f, a) => {
63 check_positivity_rec(ind_name, f, positive)?;
64 check_positivity_rec(ind_name, a, positive)
65 }
66 Expr::Pi(_, _, dom, cod) => {
67 check_positivity_rec_domain(ind_name, dom)?;
68 check_positivity_rec(ind_name, cod, positive)
69 }
70 Expr::Lam(_, _, ty_inner, body) => {
71 check_positivity_rec(ind_name, ty_inner, positive)?;
72 check_positivity_rec(ind_name, body, positive)
73 }
74 _ => Ok(()),
75 }
76}
77#[allow(clippy::result_large_err)]
81fn check_positivity_rec_domain(ind_name: &Name, dom: &Expr) -> Result<(), KernelError> {
82 match dom {
83 Expr::Const(_, _) => Ok(()),
84 Expr::App(f, a) => {
85 check_positivity_rec_domain(ind_name, f)?;
86 check_positivity_rec_domain(ind_name, a)
87 }
88 Expr::Pi(_, _, inner_dom, inner_cod) => {
89 check_positivity_rec(ind_name, inner_dom, false)?;
90 check_positivity_rec_domain(ind_name, inner_cod)
91 }
92 _ => Ok(()),
93 }
94}
95pub fn reduce_recursor(_rec_name: &Name, _args: &[Expr]) -> Option<Expr> {
99 None
100}
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::BinderInfo;
105 #[test]
106 fn test_inductive_creation() {
107 let bool_ty = Expr::Sort(Level::zero());
108 let true_intro = IntroRule {
109 name: Name::str("true"),
110 ty: Expr::Const(Name::str("Bool"), vec![]),
111 };
112 let false_intro = IntroRule {
113 name: Name::str("false"),
114 ty: Expr::Const(Name::str("Bool"), vec![]),
115 };
116 let bool_ind = InductiveType::new(
117 Name::str("Bool"),
118 vec![],
119 0,
120 0,
121 bool_ty,
122 vec![true_intro, false_intro],
123 );
124 assert_eq!(bool_ind.name, Name::str("Bool"));
125 assert_eq!(bool_ind.intro_rules.len(), 2);
126 assert!(!bool_ind.is_recursive());
127 }
128 #[test]
129 fn test_inductive_env() {
130 let mut env = InductiveEnv::new();
131 let bool_ty = Expr::Sort(Level::zero());
132 let bool_ind = InductiveType::new(
133 Name::str("Bool"),
134 vec![],
135 0,
136 0,
137 bool_ty,
138 vec![IntroRule {
139 name: Name::str("true"),
140 ty: Expr::Const(Name::str("Bool"), vec![]),
141 }],
142 );
143 env.add(bool_ind).expect("value should be present");
144 assert!(env.get(&Name::str("Bool")).is_some());
145 assert!(env.is_constructor(&Name::str("true")));
146 }
147 #[test]
148 fn test_positivity_check() {
149 let pos_ty = Expr::Pi(
150 BinderInfo::Default,
151 Name::str("x"),
152 Node::new(Expr::Const(Name::str("Bool"), vec![])),
153 Node::new(Expr::Const(Name::str("Nat"), vec![])),
154 );
155 assert!(check_positivity(&Name::str("Nat"), &pos_ty).is_ok());
156 let neg_ty = Expr::Pi(
157 BinderInfo::Default,
158 Name::str("f"),
159 Node::new(Expr::Pi(
160 BinderInfo::Default,
161 Name::str("n"),
162 Node::new(Expr::Const(Name::str("Nat"), vec![])),
163 Node::new(Expr::Const(Name::str("Bool"), vec![])),
164 )),
165 Node::new(Expr::Const(Name::str("Bool"), vec![])),
166 );
167 assert!(check_positivity(&Name::str("Nat"), &neg_ty).is_err());
168 }
169 #[test]
170 fn test_recursive_detection() {
171 let nat_ty = Expr::Sort(Level::zero());
172 let nat_ind = InductiveType::new(
173 Name::str("Nat"),
174 vec![],
175 0,
176 0,
177 nat_ty,
178 vec![
179 IntroRule {
180 name: Name::str("zero"),
181 ty: Expr::Const(Name::str("Nat"), vec![]),
182 },
183 IntroRule {
184 name: Name::str("succ"),
185 ty: Expr::Pi(
186 BinderInfo::Default,
187 Name::str("n"),
188 Node::new(Expr::Const(Name::str("Nat"), vec![])),
189 Node::new(Expr::Const(Name::str("Nat"), vec![])),
190 ),
191 },
192 ],
193 );
194 assert!(nat_ind.is_recursive());
195 }
196 #[test]
197 fn test_to_constant_infos() {
198 let nat_ind = InductiveType::new(
199 Name::str("Nat"),
200 vec![],
201 0,
202 0,
203 Expr::Sort(Level::succ(Level::zero())),
204 vec![
205 IntroRule {
206 name: Name::str("Nat.zero"),
207 ty: Expr::Const(Name::str("Nat"), vec![]),
208 },
209 IntroRule {
210 name: Name::str("Nat.succ"),
211 ty: Expr::Pi(
212 BinderInfo::Default,
213 Name::str("n"),
214 Node::new(Expr::Const(Name::str("Nat"), vec![])),
215 Node::new(Expr::Const(Name::str("Nat"), vec![])),
216 ),
217 },
218 ],
219 );
220 let (ind_ci, ctor_cis, rec_ci) = nat_ind
221 .to_constant_infos()
222 .expect("derivation should succeed");
223 assert!(ind_ci.is_inductive());
224 let iv = ind_ci.to_inductive_val().expect("iv should be present");
225 assert_eq!(iv.ctors.len(), 2);
226 assert!(iv.is_rec);
227 assert_eq!(ctor_cis.len(), 2);
228 assert!(ctor_cis[0].is_constructor());
229 let cv0 = ctor_cis[0]
230 .to_constructor_val()
231 .expect("cv0 should be present");
232 assert_eq!(cv0.num_fields, 0);
233 let cv1 = ctor_cis[1]
234 .to_constructor_val()
235 .expect("cv1 should be present");
236 assert_eq!(cv1.num_fields, 1);
237 assert!(rec_ci.is_recursor());
238 let rv = rec_ci.to_recursor_val().expect("rv should be present");
239 assert_eq!(rv.num_minors, 2);
240 assert_eq!(rv.get_major_idx(), 3);
241 }
242 #[test]
243 fn test_register_in_env() {
244 let mut env = crate::Environment::new();
245 let mut ind_env = InductiveEnv::new();
246 let bool_ind = InductiveType::new(
247 Name::str("Bool"),
248 vec![],
249 0,
250 0,
251 Expr::Sort(Level::succ(Level::zero())),
252 vec![
253 IntroRule {
254 name: Name::str("Bool.true"),
255 ty: Expr::Const(Name::str("Bool"), vec![]),
256 },
257 IntroRule {
258 name: Name::str("Bool.false"),
259 ty: Expr::Const(Name::str("Bool"), vec![]),
260 },
261 ],
262 );
263 ind_env
264 .register_in_env(&bool_ind, &mut env)
265 .expect("value should be present");
266 assert!(env.is_inductive(&Name::str("Bool")));
267 assert!(env.is_constructor(&Name::str("Bool.true")));
268 assert!(env.is_constructor(&Name::str("Bool.false")));
269 assert!(env.is_recursor(&Name::str("Bool").append_str("rec")));
270 }
271}
272#[allow(dead_code)]
274pub fn mk_bool_inductive() -> InductiveType {
275 InductiveType::new(
276 Name::str("Bool"),
277 vec![],
278 0,
279 0,
280 Expr::Sort(Level::succ(Level::zero())),
281 vec![
282 IntroRule {
283 name: Name::str("Bool.true"),
284 ty: Expr::Const(Name::str("Bool"), vec![]),
285 },
286 IntroRule {
287 name: Name::str("Bool.false"),
288 ty: Expr::Const(Name::str("Bool"), vec![]),
289 },
290 ],
291 )
292}
293#[allow(dead_code)]
295pub fn mk_nat_inductive() -> InductiveType {
296 InductiveType::new(
297 Name::str("Nat"),
298 vec![],
299 0,
300 0,
301 Expr::Sort(Level::succ(Level::zero())),
302 vec![
303 IntroRule {
304 name: Name::str("Nat.zero"),
305 ty: Expr::Const(Name::str("Nat"), vec![]),
306 },
307 IntroRule {
308 name: Name::str("Nat.succ"),
309 ty: Expr::Pi(
310 crate::BinderInfo::Default,
311 Name::str("n"),
312 Node::new(Expr::Const(Name::str("Nat"), vec![])),
313 Node::new(Expr::Const(Name::str("Nat"), vec![])),
314 ),
315 },
316 ],
317 )
318}
319#[allow(dead_code)]
321pub fn mk_unit_inductive() -> InductiveType {
322 InductiveType::new(
323 Name::str("Unit"),
324 vec![],
325 0,
326 0,
327 Expr::Sort(Level::succ(Level::zero())),
328 vec![IntroRule {
329 name: Name::str("Unit.unit"),
330 ty: Expr::Const(Name::str("Unit"), vec![]),
331 }],
332 )
333}
334#[allow(dead_code)]
336pub fn mk_empty_inductive() -> InductiveType {
337 InductiveTypeBuilder::new()
338 .name(Name::str("Empty"))
339 .ty(Expr::Sort(Level::succ(Level::zero())))
340 .build()
341 .unwrap_or_else(|_| {
342 InductiveType::new(
343 Name::str("Empty"),
344 vec![],
345 0,
346 0,
347 Expr::Sort(Level::succ(Level::zero())),
348 vec![],
349 )
350 })
351}
352#[allow(dead_code)]
354pub fn constructor_field_counts(ind: &InductiveType) -> Vec<(Name, u32)> {
355 ind.intro_rules
356 .iter()
357 .map(|r| {
358 let total = count_pi_args(&r.ty);
359 let fields = total.saturating_sub(ind.num_params);
360 (r.name.clone(), fields)
361 })
362 .collect()
363}
364#[allow(dead_code)]
366pub fn is_singleton_inductive(ind: &InductiveType) -> bool {
367 if ind.intro_rules.len() != 1 {
368 return false;
369 }
370 !ind.is_recursive()
371}
372#[allow(dead_code)]
374pub fn is_enum_inductive(ind: &InductiveType) -> bool {
375 ind.intro_rules.iter().all(|r| {
376 let fields = count_pi_args(&r.ty).saturating_sub(ind.num_params);
377 fields == 0
378 })
379}
380#[allow(dead_code)]
382pub fn recursor_name(ind_name: &Name) -> Name {
383 Name::mk_str(ind_name.clone(), "rec".to_string())
384}
385#[cfg(test)]
386mod extra_tests {
387 use super::*;
388 use crate::BinderInfo;
389 #[test]
390 fn test_builder_basic() {
391 let ind = InductiveTypeBuilder::new()
392 .name(Name::str("MyType"))
393 .ty(Expr::Sort(Level::succ(Level::zero())))
394 .intro_rule(
395 Name::str("MyType.mk"),
396 Expr::Const(Name::str("MyType"), vec![]),
397 )
398 .build()
399 .expect("value should be present");
400 assert_eq!(ind.name, Name::str("MyType"));
401 assert_eq!(ind.intro_rules.len(), 1);
402 }
403 #[test]
404 fn test_builder_missing_name_fails() {
405 let result = InductiveTypeBuilder::new()
406 .ty(Expr::Sort(Level::zero()))
407 .build();
408 assert!(result.is_err());
409 }
410 #[test]
411 fn test_builder_missing_ty_fails() {
412 let result = InductiveTypeBuilder::new().name(Name::str("X")).build();
413 assert!(result.is_err());
414 }
415 #[test]
416 fn test_mk_bool_inductive() {
417 let b = mk_bool_inductive();
418 assert_eq!(b.name, Name::str("Bool"));
419 assert_eq!(b.intro_rules.len(), 2);
420 assert!(is_enum_inductive(&b));
421 }
422 #[test]
423 fn test_mk_nat_inductive() {
424 let n = mk_nat_inductive();
425 assert_eq!(n.name, Name::str("Nat"));
426 assert!(n.is_recursive());
427 assert!(!is_enum_inductive(&n));
428 }
429 #[test]
430 fn test_mk_unit_inductive() {
431 let u = mk_unit_inductive();
432 assert!(is_singleton_inductive(&u));
433 }
434 #[test]
435 fn test_mk_empty_inductive() {
436 let e = mk_empty_inductive();
437 assert_eq!(e.name, Name::str("Empty"));
438 assert_eq!(e.intro_rules.len(), 0);
439 assert!(!is_singleton_inductive(&e));
440 }
441 #[test]
442 fn test_constructor_field_counts_bool() {
443 let b = mk_bool_inductive();
444 let counts = constructor_field_counts(&b);
445 assert_eq!(counts.len(), 2);
446 assert_eq!(counts[0].1, 0);
447 assert_eq!(counts[1].1, 0);
448 }
449 #[test]
450 fn test_constructor_field_counts_nat() {
451 let n = mk_nat_inductive();
452 let counts = constructor_field_counts(&n);
453 assert_eq!(counts[0].1, 0);
454 assert_eq!(counts[1].1, 1);
455 }
456 #[test]
457 fn test_recursor_name() {
458 let n = Name::str("Nat");
459 let r = recursor_name(&n);
460 assert_eq!(r, Name::mk_str(Name::str("Nat"), "rec".to_string()));
461 }
462 #[test]
463 fn test_is_prop_flag() {
464 let mut ind = mk_bool_inductive();
465 assert!(!ind.is_prop);
466 ind.is_prop = true;
467 assert!(ind.is_prop);
468 }
469 #[test]
470 fn test_arity() {
471 let ind = InductiveTypeBuilder::new()
472 .name(Name::str("Vec"))
473 .ty(Expr::Sort(Level::succ(Level::zero())))
474 .num_params(1)
475 .num_indices(1)
476 .intro_rule(Name::str("Vec.nil"), Expr::Const(Name::str("Vec"), vec![]))
477 .build()
478 .expect("value should be present");
479 assert_eq!(ind.arity(), 2);
480 }
481 #[test]
482 fn test_builder_univ_params() {
483 let ind = InductiveTypeBuilder::new()
484 .name(Name::str("List"))
485 .ty(Expr::Sort(Level::succ(Level::zero())))
486 .univ_params(vec![Name::str("u")])
487 .intro_rule(
488 Name::str("List.nil"),
489 Expr::Const(Name::str("List"), vec![Level::param(Name::str("u"))]),
490 )
491 .build()
492 .expect("value should be present");
493 assert_eq!(ind.univ_params.len(), 1);
494 }
495 #[test]
496 fn test_is_nested_flag() {
497 let ind = InductiveTypeBuilder::new()
498 .name(Name::str("NTree"))
499 .ty(Expr::Sort(Level::succ(Level::zero())))
500 .is_nested(true)
501 .intro_rule(
502 Name::str("NTree.leaf"),
503 Expr::Const(Name::str("NTree"), vec![]),
504 )
505 .build()
506 .expect("value should be present");
507 assert!(ind.is_nested);
508 }
509 #[test]
510 fn test_positivity_passes_for_nat_succ() {
511 let succ_ty = Expr::Pi(
512 BinderInfo::Default,
513 Name::str("n"),
514 Node::new(Expr::Const(Name::str("Nat"), vec![])),
515 Node::new(Expr::Const(Name::str("Nat"), vec![])),
516 );
517 assert!(check_positivity(&Name::str("Nat"), &succ_ty).is_ok());
518 }
519}
520#[cfg(test)]
521mod inductive_extra_tests {
522 use super::*;
523 fn mk_nat_type() -> InductiveType {
524 InductiveType::new(
525 Name::str("Nat"),
526 vec![],
527 0,
528 0,
529 Expr::Sort(Level::succ(Level::zero())),
530 vec![
531 IntroRule {
532 name: Name::str("Nat.zero"),
533 ty: Expr::Const(Name::str("Nat"), vec![]),
534 },
535 IntroRule {
536 name: Name::str("Nat.succ"),
537 ty: Expr::Pi(
538 crate::BinderInfo::Default,
539 Name::str("n"),
540 Node::new(Expr::Const(Name::str("Nat"), vec![])),
541 Node::new(Expr::Const(Name::str("Nat"), vec![])),
542 ),
543 },
544 ],
545 )
546 }
547 #[test]
548 fn test_recursor_builder_validate() {
549 let builder = RecursorBuilder::new(Name::str("Nat.rec"));
550 assert!(builder.validate().is_ok());
551 }
552 #[test]
553 fn test_recursor_builder_build_name() {
554 let builder = RecursorBuilder::new(Name::str("List.rec"));
555 assert_eq!(builder.build_name(), Name::str("List.rec"));
556 }
557 #[test]
558 fn test_recursor_builder_chain() {
559 let builder = RecursorBuilder::new(Name::str("Nat.rec"))
560 .num_params(0)
561 .num_indices(0)
562 .is_prop(false);
563 assert!(!builder.is_prop);
564 assert_eq!(builder.num_params, 0);
565 }
566 #[test]
567 fn test_inductive_family_singleton() {
568 let nat = mk_nat_type();
569 let fam = InductiveFamily::singleton(nat.clone());
570 assert!(fam.is_singleton());
571 assert_eq!(fam.len(), 1);
572 assert_eq!(fam.total_constructors(), 2);
573 }
574 #[test]
575 fn test_inductive_family_type_names() {
576 let nat = mk_nat_type();
577 let fam = InductiveFamily::singleton(nat);
578 let names = fam.type_names();
579 assert_eq!(names.len(), 1);
580 assert_eq!(*names[0], Name::str("Nat"));
581 }
582 #[test]
583 fn test_inductive_family_all_constructor_names() {
584 let nat = mk_nat_type();
585 let fam = InductiveFamily::singleton(nat);
586 let ctors = fam.all_constructor_names();
587 assert_eq!(ctors.len(), 2);
588 }
589 #[test]
590 fn test_inductive_family_find_type() {
591 let nat = mk_nat_type();
592 let fam = InductiveFamily::singleton(nat);
593 assert!(fam.find_type(&Name::str("Nat")).is_some());
594 assert!(fam.find_type(&Name::str("List")).is_none());
595 }
596 #[test]
597 fn test_inductive_type_info_from_type() {
598 let nat = mk_nat_type();
599 let info = InductiveTypeInfo::from_type(&nat, false);
600 assert_eq!(info.name, Name::str("Nat"));
601 assert_eq!(info.num_constructors, 2);
602 assert!(!info.is_prop);
603 assert!(!info.is_mutual);
604 }
605 #[test]
606 fn test_inductive_type_info_summary() {
607 let nat = mk_nat_type();
608 let info = InductiveTypeInfo::from_type(&nat, false);
609 let s = info.summary();
610 assert!(s.contains("Nat"));
611 assert!(s.contains("ctors"));
612 }
613 #[test]
614 fn test_inductive_error_display() {
615 let e = InductiveError::AlreadyDefined(Name::str("Nat"));
616 assert!(e.to_string().contains("already defined"));
617 let e2 = InductiveError::NonStrictlyPositive(Name::str("T"));
618 assert!(e2.to_string().contains("non-strictly-positive"));
619 }
620 #[test]
621 fn test_inductive_error_other() {
622 let e = InductiveError::Other("some problem".to_string());
623 assert!(e.to_string().contains("some problem"));
624 }
625 #[test]
626 fn test_inductive_family_empty() {
627 let fam = InductiveFamily::new(vec![], vec![]);
628 assert!(fam.is_empty());
629 assert_eq!(fam.total_constructors(), 0);
630 }
631 #[test]
632 fn test_inductive_type_is_recursive() {
633 let nat = mk_nat_type();
634 assert!(nat.is_recursive());
635 }
636 #[test]
637 fn test_inductive_type_num_constructors() {
638 let nat = mk_nat_type();
639 assert_eq!(nat.num_constructors(), 2);
640 }
641 fn mk_bool_type() -> InductiveType {
642 InductiveType::new(
643 Name::str("Bool"),
644 vec![],
645 0,
646 0,
647 Expr::Sort(Level::succ(Level::zero())),
648 vec![
649 IntroRule {
650 name: Name::str("Bool.false"),
651 ty: Expr::Const(Name::str("Bool"), vec![]),
652 },
653 IntroRule {
654 name: Name::str("Bool.true"),
655 ty: Expr::Const(Name::str("Bool"), vec![]),
656 },
657 ],
658 )
659 }
660 fn strip_lams(mut e: &Expr) -> (usize, &Expr) {
662 let mut n = 0;
663 while let Expr::Lam(_, _, _, body) = e {
664 e = body;
665 n += 1;
666 }
667 (n, e)
668 }
669 #[test]
670 fn test_recursor_rhs_bool_false() {
671 let bool_ind = mk_bool_type();
675 let (_, _, rec_ci) = bool_ind
676 .to_constant_infos()
677 .expect("derivation should succeed");
678 let rec_val = rec_ci.to_recursor_val().expect("rec_val should be present");
679 let rule_false = rec_val
680 .get_rule(&Name::str("Bool.false"))
681 .expect("rule_false should be present");
682 assert_eq!(rule_false.nfields, 0);
683 let (n, body) = strip_lams(&rule_false.rhs);
684 assert_eq!(n, 3, "motive + two minors");
685 assert_eq!(*body, Expr::BVar(1));
686 }
687 #[test]
688 fn test_recursor_rhs_bool_true() {
689 let bool_ind = mk_bool_type();
690 let (_, _, rec_ci) = bool_ind
691 .to_constant_infos()
692 .expect("derivation should succeed");
693 let rec_val = rec_ci.to_recursor_val().expect("rec_val should be present");
694 let rule_true = rec_val
695 .get_rule(&Name::str("Bool.true"))
696 .expect("rule_true should be present");
697 assert_eq!(rule_true.nfields, 0);
698 let (n, body) = strip_lams(&rule_true.rhs);
699 assert_eq!(n, 3);
700 assert_eq!(*body, Expr::BVar(0));
701 }
702 #[test]
703 fn test_recursor_rhs_nat_zero() {
704 let nat_ind = mk_nat_type();
705 let (_, _, rec_ci) = nat_ind
706 .to_constant_infos()
707 .expect("derivation should succeed");
708 let rec_val = rec_ci.to_recursor_val().expect("rec_val should be present");
709 let rule_zero = rec_val
710 .get_rule(&Name::str("Nat.zero"))
711 .expect("rule_zero should be present");
712 assert_eq!(rule_zero.nfields, 0);
713 let (n, body) = strip_lams(&rule_zero.rhs);
714 assert_eq!(n, 3, "motive + zero minor + succ minor");
715 assert_eq!(*body, Expr::BVar(1));
716 }
717 #[test]
718 fn test_recursor_rhs_nat_succ() {
719 let nat_ind = mk_nat_type();
721 let (_, _, rec_ci) = nat_ind
722 .to_constant_infos()
723 .expect("derivation should succeed");
724 let rec_val = rec_ci.to_recursor_val().expect("rec_val should be present");
725 let rule_succ = rec_val
726 .get_rule(&Name::str("Nat.succ"))
727 .expect("rule_succ should be present");
728 assert_eq!(rule_succ.nfields, 1);
729 let (n, body) = strip_lams(&rule_succ.rhs);
730 assert_eq!(n, 4, "motive + two minors + one field");
731 let nat_rec = Expr::Const(
732 Name::str("Nat").append_str("rec"),
733 vec![crate::Level::param(Name::str("u"))],
734 );
735 let ih = Expr::App(
736 Node::new(Expr::App(
737 Node::new(Expr::App(
738 Node::new(Expr::App(Node::new(nat_rec), Node::new(Expr::BVar(3)))),
739 Node::new(Expr::BVar(2)),
740 )),
741 Node::new(Expr::BVar(1)),
742 )),
743 Node::new(Expr::BVar(0)),
744 );
745 let expected = Expr::App(
746 Node::new(Expr::App(
747 Node::new(Expr::BVar(1)),
748 Node::new(Expr::BVar(0)),
749 )),
750 Node::new(ih),
751 );
752 assert_eq!(*body, expected);
753 }
754 #[test]
755 fn test_recursor_iota_bool() {
756 use crate::reduce::Reducer;
757 use crate::{BinderInfo, Environment};
758 let mut env = Environment::new();
759 let mut ind_env = crate::InductiveEnv::new();
760 let bool_ind = mk_bool_type();
761 ind_env
762 .register_in_env(&bool_ind, &mut env)
763 .expect("value should be present");
764 let bool_const = Expr::Const(Name::str("Bool"), vec![]);
765 let false_const = Expr::Const(Name::str("Bool.false"), vec![]);
766 let true_const = Expr::Const(Name::str("Bool.true"), vec![]);
767 let motive = Expr::Lam(
768 BinderInfo::Default,
769 Name::str("_"),
770 Node::new(bool_const.clone()),
771 Node::new(bool_const.clone()),
772 );
773 let rec_app = Expr::App(
774 Node::new(Expr::App(
775 Node::new(Expr::App(
776 Node::new(Expr::App(
777 Node::new(Expr::Const(Name::str("Bool").append_str("rec"), vec![])),
778 Node::new(motive),
779 )),
780 Node::new(false_const.clone()),
781 )),
782 Node::new(true_const.clone()),
783 )),
784 Node::new(false_const.clone()),
785 );
786 let mut reducer = Reducer::new();
787 let result = reducer.whnf_env(&rec_app, &env);
788 assert_eq!(
789 result, false_const,
790 "Bool.rec motive false true Bool.false should reduce to Bool.false"
791 );
792 }
793 #[test]
794 fn test_recursor_iota_nat_zero() {
795 use crate::reduce::Reducer;
796 use crate::{BinderInfo, Environment};
797 let mut env = Environment::new();
798 let mut ind_env = crate::InductiveEnv::new();
799 let nat_ind = mk_nat_type();
800 ind_env
801 .register_in_env(&nat_ind, &mut env)
802 .expect("value should be present");
803 let nat_const = Expr::Const(Name::str("Nat"), vec![]);
804 let zero_const = Expr::Const(Name::str("Nat.zero"), vec![]);
805 let motive = Expr::Lam(
806 BinderInfo::Default,
807 Name::str("_"),
808 Node::new(nat_const.clone()),
809 Node::new(nat_const.clone()),
810 );
811 let zero_val = zero_const.clone();
812 let succ_fn = Expr::Lam(
813 BinderInfo::Default,
814 Name::str("n"),
815 Node::new(nat_const.clone()),
816 Node::new(Expr::Lam(
817 BinderInfo::Default,
818 Name::str("ih"),
819 Node::new(nat_const.clone()),
820 Node::new(Expr::App(
821 Node::new(Expr::Const(Name::str("Nat.succ"), vec![])),
822 Node::new(Expr::BVar(1)),
823 )),
824 )),
825 );
826 let rec_app = Expr::App(
827 Node::new(Expr::App(
828 Node::new(Expr::App(
829 Node::new(Expr::App(
830 Node::new(Expr::Const(Name::str("Nat").append_str("rec"), vec![])),
831 Node::new(motive),
832 )),
833 Node::new(zero_val.clone()),
834 )),
835 Node::new(succ_fn),
836 )),
837 Node::new(zero_const.clone()),
838 );
839 let mut reducer = Reducer::new();
840 let result = reducer.whnf_env(&rec_app, &env);
841 assert_eq!(
842 result, zero_val,
843 "Nat.rec motive zero_val succ_fn Nat.zero should reduce to zero_val"
844 );
845 }
846}
847#[cfg(test)]
848mod tests_padding_infra {
849 use super::*;
850 #[test]
851 fn test_stat_summary() {
852 let mut ss = StatSummary::new();
853 ss.record(10.0);
854 ss.record(20.0);
855 ss.record(30.0);
856 assert_eq!(ss.count(), 3);
857 assert!((ss.mean().expect("mean should succeed") - 20.0).abs() < 1e-9);
858 assert_eq!(ss.min().expect("min should succeed") as i64, 10);
859 assert_eq!(ss.max().expect("max should succeed") as i64, 30);
860 }
861 #[test]
862 fn test_transform_stat() {
863 let mut ts = TransformStat::new();
864 ts.record_before(100.0);
865 ts.record_after(80.0);
866 let ratio = ts.mean_ratio().expect("ratio should be present");
867 assert!((ratio - 0.8).abs() < 1e-9);
868 }
869 #[test]
870 fn test_small_map() {
871 let mut m: SmallMap<u32, &str> = SmallMap::new();
872 m.insert(3, "three");
873 m.insert(1, "one");
874 m.insert(2, "two");
875 assert_eq!(m.get(&2), Some(&"two"));
876 assert_eq!(m.len(), 3);
877 let keys = m.keys();
878 assert_eq!(*keys[0], 1);
879 assert_eq!(*keys[2], 3);
880 }
881 #[test]
882 fn test_label_set() {
883 let mut ls = LabelSet::new();
884 ls.add("foo");
885 ls.add("bar");
886 ls.add("foo");
887 assert_eq!(ls.count(), 2);
888 assert!(ls.has("bar"));
889 assert!(!ls.has("baz"));
890 }
891 #[test]
892 fn test_config_node() {
893 let mut root = ConfigNode::section("root");
894 let child = ConfigNode::leaf("key", "value");
895 root.add_child(child);
896 assert_eq!(root.num_children(), 1);
897 }
898 #[test]
899 fn test_versioned_record() {
900 let mut vr = VersionedRecord::new(0u32);
901 vr.update(1);
902 vr.update(2);
903 assert_eq!(*vr.current(), 2);
904 assert_eq!(vr.version(), 2);
905 assert!(vr.has_history());
906 assert_eq!(*vr.at_version(0).expect("value should be present"), 0);
907 }
908 #[test]
909 fn test_simple_dag() {
910 let mut dag = SimpleDag::new(4);
911 dag.add_edge(0, 1);
912 dag.add_edge(1, 2);
913 dag.add_edge(2, 3);
914 assert!(dag.can_reach(0, 3));
915 assert!(!dag.can_reach(3, 0));
916 let order = dag.topological_sort().expect("order should be present");
917 assert_eq!(order, vec![0, 1, 2, 3]);
918 }
919 #[test]
920 fn test_focus_stack() {
921 let mut fs: FocusStack<&str> = FocusStack::new();
922 fs.focus("a");
923 fs.focus("b");
924 assert_eq!(fs.current(), Some(&"b"));
925 assert_eq!(fs.depth(), 2);
926 fs.blur();
927 assert_eq!(fs.current(), Some(&"a"));
928 }
929}
930#[cfg(test)]
931mod tests_extra_iterators {
932 use super::*;
933 #[test]
934 fn test_window_iterator() {
935 let data = vec![1u32, 2, 3, 4, 5];
936 let windows: Vec<_> = WindowIterator::new(&data, 3).collect();
937 assert_eq!(windows.len(), 3);
938 assert_eq!(windows[0], &[1, 2, 3]);
939 assert_eq!(windows[2], &[3, 4, 5]);
940 }
941 #[test]
942 fn test_non_empty_vec() {
943 let mut nev = NonEmptyVec::singleton(10u32);
944 nev.push(20);
945 nev.push(30);
946 assert_eq!(nev.len(), 3);
947 assert_eq!(*nev.first(), 10);
948 assert_eq!(*nev.last(), 30);
949 }
950}
951#[cfg(test)]
952mod builtin_inductive_tests;