1use crate::node_memo::NodeMemo;
6use crate::Node;
7use crate::{Expr, FVarId};
8use std::collections::HashMap;
9use std::rc::Rc;
10
11use super::types::{
12 ConfigNode, DecisionNode, Either2, FlatSubstitution, FocusStack, LabelSet, NonEmptyVec,
13 PathBuf, RewriteRule, RewriteRuleSet, SimpleDag, SlidingSum, SmallMap, SparseVec, StackCalc,
14 StatSummary, Stopwatch, StringPool, SubstStats, Substitution, TokenBucket, TransformStat,
15 TransitiveClosure, VersionedRecord, WindowIterator, WriteOnce,
16};
17
18pub fn instantiate(body: &Expr, arg: &Expr) -> Expr {
53 if body.range() == 0 {
54 return body.clone();
56 }
57 let mut walk = Instantiate {
58 args: Args::Terms(std::slice::from_ref(&arg)),
59 rebuilt: NodeMemo::new(),
60 lifted: Vec::new(),
61 };
62 walk.expr(body, 0)
63}
64
65pub(crate) fn instantiate_rev_nodes(body: &Expr, args: &[&Node]) -> Expr {
75 if args.is_empty() || body.range() == 0 {
76 return body.clone();
77 }
78 let mut walk = Instantiate {
79 args: Args::Nodes(args),
80 rebuilt: NodeMemo::new(),
81 lifted: Vec::new(),
82 };
83 walk.expr(body, 0)
84}
85
86enum Args<'arg> {
88 Terms(&'arg [&'arg Expr]),
90 Nodes(&'arg [&'arg Node]),
92}
93
94impl Args<'_> {
95 fn len(&self) -> usize {
96 match self {
97 Args::Terms(terms) => terms.len(),
98 Args::Nodes(nodes) => nodes.len(),
99 }
100 }
101
102 fn lifted(&self, index: usize, depth: u32) -> Node {
105 match self {
106 Args::Terms(terms) => {
107 Node::new(crate::expr_util::lift_loose_bvars(terms[index], depth, 0))
108 }
109 Args::Nodes(nodes) => {
110 let node = nodes[index];
111 if depth == 0 || node.range() == 0 {
112 node.clone()
113 } else {
114 Node::new(crate::expr_util::lift_loose_bvars(node, depth, 0))
115 }
116 }
117 }
118 }
119
120 fn lifted_term(&self, index: usize, depth: u32) -> Expr {
122 let term: &Expr = match self {
123 Args::Terms(terms) => terms[index],
124 Args::Nodes(nodes) => nodes[index],
125 };
126 crate::expr_util::lift_loose_bvars(term, depth, 0)
127 }
128}
129
130struct Instantiate<'term, 'arg> {
132 args: Args<'arg>,
135 rebuilt: NodeMemo<'term, Node>,
138 lifted: Vec<(u32, usize, Node)>,
142}
143
144impl<'term> Instantiate<'term, '_> {
145 fn arg_index(&self, index: u32, depth: u32) -> Option<usize> {
148 let count = self.args.len();
149 let offset = (index - depth) as usize;
151 (offset < count).then(|| count - 1 - offset)
152 }
153
154 fn shift(&self) -> u32 {
156 u32::try_from(self.args.len()).unwrap_or(u32::MAX)
159 }
160
161 fn node(&mut self, node: &'term Node, depth: u32) -> Node {
163 crate::fuel::charge(1);
164 if node.range() <= depth {
165 return node.clone();
167 }
168 if let Expr::BVar(index) = &**node {
169 return match self.arg_index(*index, depth) {
172 Some(which) => self.arg_at(which, depth),
173 None => Node::new(Expr::BVar(index.saturating_sub(self.shift()))),
174 };
175 }
176 let shared = node.is_shared();
177 if shared {
178 if let Some(done) = self.rebuilt.get(node, depth) {
179 return done.clone();
180 }
181 }
182 let result = Node::new(self.rebuild(node, depth));
183 if shared {
184 self.rebuilt.insert(node, depth, result.clone());
185 }
186 result
187 }
188
189 fn arg_at(&mut self, which: usize, depth: u32) -> Node {
192 if let Some((_, _, lifted)) = self
193 .lifted
194 .iter()
195 .find(|(d, w, _)| *d == depth && *w == which)
196 {
197 return lifted.clone();
198 }
199 let lifted = self.args.lifted(which, depth);
200 self.lifted.push((depth, which, lifted.clone()));
201 lifted
202 }
203
204 fn expr(&mut self, expr: &'term Expr, depth: u32) -> Expr {
206 crate::fuel::charge(1);
207 self.rebuild(expr, depth)
208 }
209
210 fn rebuild(&mut self, expr: &'term Expr, depth: u32) -> Expr {
212 match expr {
213 Expr::BVar(n) => {
214 if *n < depth {
215 return expr.clone();
216 }
217 match self.arg_index(*n, depth) {
218 Some(which) => self.args.lifted_term(which, depth),
219 None => Expr::BVar(n.saturating_sub(self.shift())),
220 }
221 }
222 Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
223 Expr::App(f, a) => Expr::App(self.node(f, depth), self.node(a, depth)),
224 Expr::Lam(bi, name, ty, body) => Expr::Lam(
225 *bi,
226 name.clone(),
227 self.node(ty, depth),
228 self.node(body, depth + 1),
229 ),
230 Expr::Pi(bi, name, ty, body) => Expr::Pi(
231 *bi,
232 name.clone(),
233 self.node(ty, depth),
234 self.node(body, depth + 1),
235 ),
236 Expr::Let(name, ty, val, body) => Expr::Let(
237 name.clone(),
238 self.node(ty, depth),
239 self.node(val, depth),
240 self.node(body, depth + 1),
241 ),
242 Expr::Proj(name, idx, e) => Expr::Proj(name.clone(), *idx, self.node(e, depth)),
243 }
244 }
245}
246
247pub fn abstract_expr(expr: &Expr, fvar: FVarId) -> Expr {
268 let mut walk = Abstract {
269 fvar,
270 done: NodeMemo::new(),
271 };
272 crate::fuel::charge(1);
273 match walk.rebuild(expr, 0) {
274 Some(changed) => changed,
275 None => expr.clone(),
276 }
277}
278
279struct Abstract<'term> {
282 fvar: FVarId,
283 done: NodeMemo<'term, Option<Node>>,
286}
287
288impl<'term> Abstract<'term> {
289 fn node(&mut self, node: &'term Node, depth: u32) -> Option<Node> {
292 crate::fuel::charge(1);
293 if !node.has_fvar() && node.range() <= depth {
294 return None;
296 }
297 if matches!(&**node, Expr::FVar(_) | Expr::BVar(_)) {
298 return self.rebuild(node, depth).map(Node::new);
300 }
301 let shared = node.is_shared();
302 if shared {
303 if let Some(done) = self.done.get(node, depth) {
304 return done.clone();
305 }
306 }
307 let result = self.rebuild(node, depth).map(Node::new);
308 if shared {
309 self.done.insert(node, depth, result.clone());
310 }
311 result
312 }
313
314 fn rebuild(&mut self, expr: &'term Expr, depth: u32) -> Option<Expr> {
317 match expr {
318 Expr::FVar(id) if *id == self.fvar => Some(Expr::BVar(depth)),
319 Expr::BVar(n) if *n >= depth => Some(Expr::BVar(n.saturating_add(1))),
323 Expr::BVar(_) | Expr::FVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => {
324 None
325 }
326 Expr::App(f, a) => {
327 let f_new = self.node(f, depth);
328 let a_new = self.node(a, depth);
329 if f_new.is_none() && a_new.is_none() {
330 return None;
331 }
332 Some(Expr::App(
333 f_new.unwrap_or_else(|| f.clone()),
334 a_new.unwrap_or_else(|| a.clone()),
335 ))
336 }
337 Expr::Lam(bi, name, ty, body) => {
338 let ty_new = self.node(ty, depth);
339 let body_new = self.node(body, depth + 1);
340 if ty_new.is_none() && body_new.is_none() {
341 return None;
342 }
343 Some(Expr::Lam(
344 *bi,
345 name.clone(),
346 ty_new.unwrap_or_else(|| ty.clone()),
347 body_new.unwrap_or_else(|| body.clone()),
348 ))
349 }
350 Expr::Pi(bi, name, ty, body) => {
351 let ty_new = self.node(ty, depth);
352 let body_new = self.node(body, depth + 1);
353 if ty_new.is_none() && body_new.is_none() {
354 return None;
355 }
356 Some(Expr::Pi(
357 *bi,
358 name.clone(),
359 ty_new.unwrap_or_else(|| ty.clone()),
360 body_new.unwrap_or_else(|| body.clone()),
361 ))
362 }
363 Expr::Let(name, ty, val, body) => {
364 let ty_new = self.node(ty, depth);
365 let val_new = self.node(val, depth);
366 let body_new = self.node(body, depth + 1);
367 if ty_new.is_none() && val_new.is_none() && body_new.is_none() {
368 return None;
369 }
370 Some(Expr::Let(
371 name.clone(),
372 ty_new.unwrap_or_else(|| ty.clone()),
373 val_new.unwrap_or_else(|| val.clone()),
374 body_new.unwrap_or_else(|| body.clone()),
375 ))
376 }
377 Expr::Proj(name, idx, e) => {
378 let e_new = self.node(e, depth)?;
379 Some(Expr::Proj(name.clone(), *idx, e_new))
380 }
381 }
382 }
383}
384pub type SubstMap = HashMap<FVarId, Expr>;
386pub fn instantiate_many(expr: &Expr, args: &[Expr]) -> Expr {
388 let k = args.len() as u32;
389 if k == 0 {
390 return expr.clone();
391 }
392 instantiate_many_at(expr, args, k, 0)
393}
394fn instantiate_many_at(expr: &Expr, args: &[Expr], k: u32, offset: u32) -> Expr {
395 crate::fuel::charge(1);
397 match expr {
398 Expr::BVar(n) => {
399 let idx = *n;
400 if idx >= offset && idx < offset + k {
401 args[(idx - offset) as usize].clone()
402 } else if idx >= offset + k {
403 Expr::BVar(idx - k)
404 } else {
405 expr.clone()
406 }
407 }
408 Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
409 Expr::App(f, a) => Expr::App(
410 Node::new(instantiate_many_at(f, args, k, offset)),
411 Node::new(instantiate_many_at(a, args, k, offset)),
412 ),
413 Expr::Lam(bi, name, ty, body) => Expr::Lam(
414 *bi,
415 name.clone(),
416 Node::new(instantiate_many_at(ty, args, k, offset)),
417 Node::new(instantiate_many_at(body, args, k, offset + 1)),
418 ),
419 Expr::Pi(bi, name, ty, body) => Expr::Pi(
420 *bi,
421 name.clone(),
422 Node::new(instantiate_many_at(ty, args, k, offset)),
423 Node::new(instantiate_many_at(body, args, k, offset + 1)),
424 ),
425 Expr::Let(name, ty, val, body) => Expr::Let(
426 name.clone(),
427 Node::new(instantiate_many_at(ty, args, k, offset)),
428 Node::new(instantiate_many_at(val, args, k, offset)),
429 Node::new(instantiate_many_at(body, args, k, offset + 1)),
430 ),
431 Expr::Proj(name, idx, e) => Expr::Proj(
432 name.clone(),
433 *idx,
434 Node::new(instantiate_many_at(e, args, k, offset)),
435 ),
436 }
437}
438pub fn parallel_subst(expr: &Expr, map: &SubstMap) -> Expr {
441 if map.is_empty() {
442 return expr.clone();
443 }
444 parallel_subst_impl(expr, map)
445}
446fn parallel_subst_impl(expr: &Expr, map: &SubstMap) -> Expr {
447 crate::fuel::charge(1);
449 match expr {
450 Expr::FVar(id) => {
451 if let Some(replacement) = map.get(id) {
452 replacement.clone()
453 } else {
454 expr.clone()
455 }
456 }
457 Expr::BVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
458 Expr::App(f, a) => Expr::App(
459 Node::new(parallel_subst_impl(f, map)),
460 Node::new(parallel_subst_impl(a, map)),
461 ),
462 Expr::Lam(bi, name, ty, body) => Expr::Lam(
463 *bi,
464 name.clone(),
465 Node::new(parallel_subst_impl(ty, map)),
466 Node::new(parallel_subst_impl(body, map)),
467 ),
468 Expr::Pi(bi, name, ty, body) => Expr::Pi(
469 *bi,
470 name.clone(),
471 Node::new(parallel_subst_impl(ty, map)),
472 Node::new(parallel_subst_impl(body, map)),
473 ),
474 Expr::Let(name, ty, val, body) => Expr::Let(
475 name.clone(),
476 Node::new(parallel_subst_impl(ty, map)),
477 Node::new(parallel_subst_impl(val, map)),
478 Node::new(parallel_subst_impl(body, map)),
479 ),
480 Expr::Proj(name, idx, e) => {
481 Expr::Proj(name.clone(), *idx, Node::new(parallel_subst_impl(e, map)))
482 }
483 }
484}
485pub fn shift_bvars(expr: &Expr, amount: u32, cutoff: u32) -> Expr {
487 crate::fuel::charge(1);
489 match expr {
490 Expr::BVar(i) => {
491 if *i >= cutoff {
492 Expr::BVar(*i + amount)
493 } else {
494 expr.clone()
495 }
496 }
497 Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
498 Expr::App(f, a) => Expr::App(
499 Node::new(shift_bvars(f, amount, cutoff)),
500 Node::new(shift_bvars(a, amount, cutoff)),
501 ),
502 Expr::Lam(bi, name, ty, body) => Expr::Lam(
503 *bi,
504 name.clone(),
505 Node::new(shift_bvars(ty, amount, cutoff)),
506 Node::new(shift_bvars(body, amount, cutoff + 1)),
507 ),
508 Expr::Pi(bi, name, ty, body) => Expr::Pi(
509 *bi,
510 name.clone(),
511 Node::new(shift_bvars(ty, amount, cutoff)),
512 Node::new(shift_bvars(body, amount, cutoff + 1)),
513 ),
514 Expr::Let(name, ty, val, body) => Expr::Let(
515 name.clone(),
516 Node::new(shift_bvars(ty, amount, cutoff)),
517 Node::new(shift_bvars(val, amount, cutoff)),
518 Node::new(shift_bvars(body, amount, cutoff + 1)),
519 ),
520 Expr::Proj(name, idx, e) => Expr::Proj(
521 name.clone(),
522 *idx,
523 Node::new(shift_bvars(e, amount, cutoff)),
524 ),
525 }
526}
527pub fn free_vars(expr: &Expr) -> std::collections::HashSet<FVarId> {
529 let mut set = std::collections::HashSet::new();
530 free_vars_impl(expr, &mut set);
531 set
532}
533fn free_vars_impl(expr: &Expr, set: &mut std::collections::HashSet<FVarId>) {
534 match expr {
535 Expr::FVar(id) => {
536 set.insert(*id);
537 }
538 Expr::App(f, a) => {
539 free_vars_impl(f, set);
540 free_vars_impl(a, set);
541 }
542 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
543 free_vars_impl(ty, set);
544 free_vars_impl(body, set);
545 }
546 Expr::Let(_, ty, val, body) => {
547 free_vars_impl(ty, set);
548 free_vars_impl(val, set);
549 free_vars_impl(body, set);
550 }
551 Expr::Proj(_, _, e) => free_vars_impl(e, set),
552 _ => {}
553 }
554}
555pub fn occurs_free(expr: &Expr, fvar: FVarId) -> bool {
557 match expr {
558 Expr::FVar(id) => *id == fvar,
559 Expr::App(f, a) => occurs_free(f, fvar) || occurs_free(a, fvar),
560 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
561 occurs_free(ty, fvar) || occurs_free(body, fvar)
562 }
563 Expr::Let(_, ty, val, body) => {
564 occurs_free(ty, fvar) || occurs_free(val, fvar) || occurs_free(body, fvar)
565 }
566 Expr::Proj(_, _, e) => occurs_free(e, fvar),
567 _ => false,
568 }
569}
570pub fn count_free_occurrences(expr: &Expr, fvar: FVarId) -> usize {
572 match expr {
573 Expr::FVar(id) => usize::from(*id == fvar),
574 Expr::App(f, a) => count_free_occurrences(f, fvar) + count_free_occurrences(a, fvar),
575 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
576 count_free_occurrences(ty, fvar) + count_free_occurrences(body, fvar)
577 }
578 Expr::Let(_, ty, val, body) => {
579 count_free_occurrences(ty, fvar)
580 + count_free_occurrences(val, fvar)
581 + count_free_occurrences(body, fvar)
582 }
583 Expr::Proj(_, _, e) => count_free_occurrences(e, fvar),
584 _ => 0,
585 }
586}
587pub fn instantiate_tracked(body: &Expr, arg: &Expr, stats: &mut SubstStats) -> Expr {
589 instantiate_tracked_at(body, arg, 0, stats)
590}
591fn instantiate_tracked_at(expr: &Expr, arg: &Expr, depth: u32, stats: &mut SubstStats) -> Expr {
592 crate::fuel::charge(1);
594 stats.nodes_visited += 1;
595 match expr {
596 Expr::BVar(n) => {
597 if *n == depth {
598 stats.bvar_hits += 1;
599 crate::expr_util::lift_loose_bvars(arg, depth, 0)
603 } else if *n > depth {
604 stats.bvar_misses += 1;
605 Expr::BVar(*n - 1)
606 } else {
607 expr.clone()
608 }
609 }
610 Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
611 Expr::App(f, a) => Expr::App(
612 Node::new(instantiate_tracked_at(f, arg, depth, stats)),
613 Node::new(instantiate_tracked_at(a, arg, depth, stats)),
614 ),
615 Expr::Lam(bi, name, ty, body) => Expr::Lam(
616 *bi,
617 name.clone(),
618 Node::new(instantiate_tracked_at(ty, arg, depth, stats)),
619 Node::new(instantiate_tracked_at(body, arg, depth + 1, stats)),
620 ),
621 Expr::Pi(bi, name, ty, body) => Expr::Pi(
622 *bi,
623 name.clone(),
624 Node::new(instantiate_tracked_at(ty, arg, depth, stats)),
625 Node::new(instantiate_tracked_at(body, arg, depth + 1, stats)),
626 ),
627 Expr::Let(name, ty, val, body) => Expr::Let(
628 name.clone(),
629 Node::new(instantiate_tracked_at(ty, arg, depth, stats)),
630 Node::new(instantiate_tracked_at(val, arg, depth, stats)),
631 Node::new(instantiate_tracked_at(body, arg, depth + 1, stats)),
632 ),
633 Expr::Proj(name, idx, e) => Expr::Proj(
634 name.clone(),
635 *idx,
636 Node::new(instantiate_tracked_at(e, arg, depth, stats)),
637 ),
638 }
639}
640pub fn build_subst_map(fvars: &[FVarId], replacements: &[Expr]) -> SubstMap {
642 assert_eq!(fvars.len(), replacements.len());
643 fvars
644 .iter()
645 .zip(replacements.iter())
646 .map(|(id, expr)| (*id, expr.clone()))
647 .collect()
648}
649pub fn try_beta_reduce(expr: &Expr) -> Option<Expr> {
651 match expr {
652 Expr::App(f, a) => {
653 if let Expr::Lam(_, _, _, body) = f.as_ref() {
654 Some(instantiate(body, a))
655 } else {
656 None
657 }
658 }
659 _ => None,
660 }
661}
662pub fn beta_reduce_head(expr: Expr) -> Expr {
664 let mut curr = expr;
665 while let Some(reduced) = try_beta_reduce(&curr) {
666 curr = reduced;
667 }
668 curr
669}
670pub fn subst_fvar(expr: &Expr, fvar: FVarId, replacement: &Expr) -> Expr {
672 let mut map = SubstMap::new();
673 map.insert(fvar, replacement.clone());
674 parallel_subst(expr, &map)
675}
676pub fn is_whnf_beta(expr: &Expr) -> bool {
678 match expr {
679 Expr::App(f, _) => match f.as_ref() {
680 Expr::Lam(_, _, _, _) => false,
681 other => is_whnf_beta(other),
682 },
683 _ => true,
684 }
685}
686pub fn collect_loose_bvar_indices(expr: &Expr) -> Vec<u32> {
688 let mut indices = Vec::new();
689 collect_loose_bvar_impl(expr, 0, &mut indices);
690 indices.sort();
691 indices.dedup();
692 indices
693}
694fn collect_loose_bvar_impl(expr: &Expr, depth: u32, result: &mut Vec<u32>) {
695 match expr {
696 Expr::BVar(n) if *n >= depth => {
697 result.push(*n - depth);
698 }
699 Expr::BVar(_) => {}
700 Expr::App(f, a) => {
701 collect_loose_bvar_impl(f, depth, result);
702 collect_loose_bvar_impl(a, depth, result);
703 }
704 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
705 collect_loose_bvar_impl(ty, depth, result);
706 collect_loose_bvar_impl(body, depth + 1, result);
707 }
708 Expr::Let(_, ty, val, body) => {
709 collect_loose_bvar_impl(ty, depth, result);
710 collect_loose_bvar_impl(val, depth, result);
711 collect_loose_bvar_impl(body, depth + 1, result);
712 }
713 Expr::Proj(_, _, e) => collect_loose_bvar_impl(e, depth, result),
714 _ => {}
715 }
716}
717pub fn parallel_subst_tracked(expr: &Expr, map: &SubstMap, stats: &mut SubstStats) -> Expr {
719 if map.is_empty() {
720 return expr.clone();
721 }
722 parallel_subst_tracked_impl(expr, map, stats)
723}
724fn parallel_subst_tracked_impl(expr: &Expr, map: &SubstMap, stats: &mut SubstStats) -> Expr {
725 stats.nodes_visited += 1;
726 match expr {
727 Expr::FVar(id) => {
728 if let Some(r) = map.get(id) {
729 stats.fvar_hits += 1;
730 r.clone()
731 } else {
732 expr.clone()
733 }
734 }
735 Expr::BVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
736 Expr::App(f, a) => Expr::App(
737 Node::new(parallel_subst_tracked_impl(f, map, stats)),
738 Node::new(parallel_subst_tracked_impl(a, map, stats)),
739 ),
740 Expr::Lam(bi, name, ty, body) => Expr::Lam(
741 *bi,
742 name.clone(),
743 Node::new(parallel_subst_tracked_impl(ty, map, stats)),
744 Node::new(parallel_subst_tracked_impl(body, map, stats)),
745 ),
746 Expr::Pi(bi, name, ty, body) => Expr::Pi(
747 *bi,
748 name.clone(),
749 Node::new(parallel_subst_tracked_impl(ty, map, stats)),
750 Node::new(parallel_subst_tracked_impl(body, map, stats)),
751 ),
752 Expr::Let(name, ty, val, body) => Expr::Let(
753 name.clone(),
754 Node::new(parallel_subst_tracked_impl(ty, map, stats)),
755 Node::new(parallel_subst_tracked_impl(val, map, stats)),
756 Node::new(parallel_subst_tracked_impl(body, map, stats)),
757 ),
758 Expr::Proj(name, idx, e) => Expr::Proj(
759 name.clone(),
760 *idx,
761 Node::new(parallel_subst_tracked_impl(e, map, stats)),
762 ),
763 }
764}
765#[cfg(test)]
766mod extended_tests {
767 use super::*;
768 use crate::{BinderInfo, Level, Literal, Name};
769 fn fvar(id: u64) -> Expr {
770 Expr::FVar(FVarId(id))
771 }
772 fn lit(n: u64) -> Expr {
773 Expr::Lit(Literal::nat(n))
774 }
775 fn sort0() -> Expr {
776 Expr::Sort(Level::zero())
777 }
778 #[test]
779 fn test_instantiate_many_two() {
780 let app = Expr::App(Node::new(Expr::BVar(0)), Node::new(Expr::BVar(1)));
781 let result = instantiate_many(&app, &[lit(10), lit(20)]);
782 match result {
783 Expr::App(f, a) => {
784 assert_eq!(*f, lit(10));
785 assert_eq!(*a, lit(20));
786 }
787 _ => panic!("Expected App"),
788 }
789 }
790 #[test]
791 fn test_parallel_subst_single() {
792 let mut map = SubstMap::new();
793 map.insert(FVarId(1), lit(99));
794 let expr = fvar(1);
795 assert_eq!(parallel_subst(&expr, &map), lit(99));
796 }
797 #[test]
798 fn test_parallel_subst_empty() {
799 let expr = fvar(5);
800 let map = SubstMap::new();
801 assert_eq!(parallel_subst(&expr, &map), expr);
802 }
803 #[test]
804 fn test_shift_bvars_above_cutoff() {
805 let e = Expr::BVar(2);
806 let shifted = shift_bvars(&e, 3, 1);
807 assert_eq!(shifted, Expr::BVar(5));
808 }
809 #[test]
810 fn test_shift_bvars_below_cutoff() {
811 let e = Expr::BVar(0);
812 let shifted = shift_bvars(&e, 2, 1);
813 assert_eq!(shifted, Expr::BVar(0));
814 }
815 #[test]
816 fn test_free_vars_single() {
817 let set = free_vars(&fvar(7));
818 assert_eq!(set.len(), 1);
819 assert!(set.contains(&FVarId(7)));
820 }
821 #[test]
822 fn test_occurs_free_true() {
823 let expr = Expr::App(Node::new(fvar(1)), Node::new(fvar(2)));
824 assert!(occurs_free(&expr, FVarId(1)));
825 assert!(!occurs_free(&expr, FVarId(3)));
826 }
827 #[test]
828 fn test_count_free_occurrences() {
829 let expr = Expr::App(Node::new(fvar(1)), Node::new(fvar(1)));
830 assert_eq!(count_free_occurrences(&expr, FVarId(1)), 2);
831 }
832 #[test]
833 fn test_subst_stats_merge() {
834 let mut a = SubstStats {
835 bvar_hits: 3,
836 bvar_misses: 1,
837 fvar_hits: 2,
838 nodes_visited: 10,
839 };
840 let b = SubstStats {
841 bvar_hits: 1,
842 bvar_misses: 0,
843 fvar_hits: 1,
844 nodes_visited: 5,
845 };
846 a.merge(&b);
847 assert_eq!(a.bvar_hits, 4);
848 assert_eq!(a.total_substs(), 7);
849 }
850 #[test]
851 fn test_instantiate_tracked() {
852 let body = Expr::BVar(0);
853 let arg = lit(42);
854 let mut stats = SubstStats::default();
855 let result = instantiate_tracked(&body, &arg, &mut stats);
856 assert_eq!(result, lit(42));
857 assert_eq!(stats.bvar_hits, 1);
858 }
859 #[test]
860 fn test_build_subst_map() {
861 let fvars = vec![FVarId(1), FVarId(2)];
862 let replacements = vec![lit(10), lit(20)];
863 let map = build_subst_map(&fvars, &replacements);
864 assert_eq!(map.get(&FVarId(1)), Some(&lit(10)));
865 }
866 #[test]
867 fn test_try_beta_reduce_success() {
868 let lam = Expr::Lam(
869 BinderInfo::Default,
870 Name::str("x"),
871 Node::new(sort0()),
872 Node::new(Expr::BVar(0)),
873 );
874 let app = Expr::App(Node::new(lam), Node::new(lit(42)));
875 let result = try_beta_reduce(&app).expect("result should be present");
876 assert_eq!(result, lit(42));
877 }
878 #[test]
879 fn test_try_beta_reduce_fail() {
880 let app = Expr::App(Node::new(fvar(1)), Node::new(lit(42)));
881 assert!(try_beta_reduce(&app).is_none());
882 }
883 #[test]
884 fn test_beta_reduce_head() {
885 let lam = Expr::Lam(
886 BinderInfo::Default,
887 Name::str("x"),
888 Node::new(sort0()),
889 Node::new(Expr::BVar(0)),
890 );
891 let app = Expr::App(Node::new(lam), Node::new(lit(7)));
892 let result = beta_reduce_head(app);
893 assert_eq!(result, lit(7));
894 }
895 #[test]
896 fn test_subst_fvar() {
897 let expr = fvar(1);
898 let result = subst_fvar(&expr, FVarId(1), &lit(99));
899 assert_eq!(result, lit(99));
900 }
901 #[test]
902 fn test_is_whnf_beta_lam() {
903 let lam = Expr::Lam(
904 BinderInfo::Default,
905 Name::str("x"),
906 Node::new(sort0()),
907 Node::new(Expr::BVar(0)),
908 );
909 assert!(is_whnf_beta(&lam));
910 }
911 #[test]
912 fn test_is_whnf_beta_app_not_whnf() {
913 let lam = Expr::Lam(
914 BinderInfo::Default,
915 Name::str("x"),
916 Node::new(sort0()),
917 Node::new(Expr::BVar(0)),
918 );
919 let app = Expr::App(Node::new(lam), Node::new(lit(1)));
920 assert!(!is_whnf_beta(&app));
921 }
922 #[test]
923 fn test_collect_loose_bvar_indices() {
924 let e = Expr::App(Node::new(Expr::BVar(0)), Node::new(Expr::BVar(2)));
925 let indices = collect_loose_bvar_indices(&e);
926 assert!(indices.contains(&0));
927 assert!(indices.contains(&2));
928 }
929 #[test]
930 fn test_parallel_subst_tracked() {
931 let expr = Expr::App(Node::new(fvar(1)), Node::new(fvar(2)));
932 let mut map = SubstMap::new();
933 map.insert(FVarId(1), lit(10));
934 map.insert(FVarId(2), lit(20));
935 let mut stats = SubstStats::default();
936 let result = parallel_subst_tracked(&expr, &map, &mut stats);
937 assert_eq!(stats.fvar_hits, 2);
938 match result {
939 Expr::App(f, a) => {
940 assert_eq!(*f, lit(10));
941 assert_eq!(*a, lit(20));
942 }
943 _ => panic!("Expected App"),
944 }
945 }
946}
947pub fn apply_args(f: &Expr, args: &[Expr]) -> Expr {
952 let mut result = f.clone();
953 for arg in args {
954 if let Expr::Lam(_, _, _, body) = result {
955 result = instantiate(&body, arg);
956 } else {
957 result = Expr::App(Node::new(result), Node::new(arg.clone()));
958 }
959 }
960 result
961}
962#[inline]
964pub fn is_lambda(e: &Expr) -> bool {
965 matches!(e, Expr::Lam(_, _, _, _))
966}
967#[allow(clippy::type_complexity)]
972pub fn peel_lambdas(e: &Expr, n: usize) -> (Vec<(crate::Name, crate::BinderInfo, Node)>, &Expr) {
973 let mut binders = Vec::new();
974 let mut cur = e;
975 for _ in 0..n {
976 if let Expr::Lam(bi, _, ty, body) = cur {
977 binders.push((crate::Name::anonymous(), *bi, ty.clone()));
978 cur = body;
979 } else {
980 break;
981 }
982 }
983 (binders, cur)
984}
985pub fn count_lambdas(e: &Expr) -> usize {
987 let mut n = 0;
988 let mut cur = e;
989 while let Expr::Lam(_, _, _, body) = cur {
990 n += 1;
991 cur = body;
992 }
993 n
994}
995pub fn count_pis(e: &Expr) -> usize {
997 let mut n = 0;
998 let mut cur = e;
999 while let Expr::Pi(_, _, _, body) = cur {
1000 n += 1;
1001 cur = body;
1002 }
1003 n
1004}
1005#[cfg(test)]
1006mod extended2_tests {
1007 use super::*;
1008 use crate::{BinderInfo, Expr, FVarId, Literal, Name};
1009 fn lit(n: u64) -> Expr {
1010 Expr::Lit(Literal::nat(n))
1011 }
1012 fn fvar(id: u64) -> Expr {
1013 Expr::FVar(FVarId(id))
1014 }
1015 #[test]
1016 fn test_apply_args_lam() {
1017 let body = Expr::BVar(0);
1018 let lam = Expr::Lam(
1019 BinderInfo::Default,
1020 Name::str("_"),
1021 Node::new(lit(0)),
1022 Node::new(body),
1023 );
1024 let result = apply_args(&lam, &[lit(42)]);
1025 assert_eq!(result, lit(42));
1026 }
1027 #[test]
1028 fn test_apply_args_non_lam() {
1029 let f = fvar(1);
1030 let result = apply_args(&f, &[lit(1), lit(2)]);
1031 match &result {
1032 Expr::App(outer, arg2) => {
1033 assert_eq!(**arg2, lit(2));
1034 match outer.as_ref() {
1035 Expr::App(inner_f, arg1) => {
1036 assert_eq!(**inner_f, fvar(1));
1037 assert_eq!(**arg1, lit(1));
1038 }
1039 _ => panic!("Expected App"),
1040 }
1041 }
1042 _ => panic!("Expected App"),
1043 }
1044 }
1045 #[test]
1046 fn test_is_lambda() {
1047 let lam = Expr::Lam(
1048 BinderInfo::Default,
1049 Name::str("_"),
1050 Node::new(lit(0)),
1051 Node::new(lit(1)),
1052 );
1053 assert!(is_lambda(&lam));
1054 assert!(!is_lambda(&lit(0)));
1055 }
1056 #[test]
1057 fn test_count_lambdas() {
1058 let inner = lit(0);
1059 let lam1 = Expr::Lam(
1060 BinderInfo::Default,
1061 Name::str("_"),
1062 Node::new(lit(0)),
1063 Node::new(inner),
1064 );
1065 let lam2 = Expr::Lam(
1066 BinderInfo::Default,
1067 Name::str("_"),
1068 Node::new(lit(0)),
1069 Node::new(lam1),
1070 );
1071 assert_eq!(count_lambdas(&lam2), 2);
1072 assert_eq!(count_lambdas(&lit(0)), 0);
1073 }
1074 #[test]
1075 fn test_count_pis() {
1076 let inner = lit(0);
1077 let pi1 = Expr::Pi(
1078 BinderInfo::Default,
1079 Name::str("_"),
1080 Node::new(lit(0)),
1081 Node::new(inner),
1082 );
1083 let pi2 = Expr::Pi(
1084 BinderInfo::Default,
1085 Name::str("_"),
1086 Node::new(lit(0)),
1087 Node::new(pi1),
1088 );
1089 assert_eq!(count_pis(&pi2), 2);
1090 assert_eq!(count_pis(&lit(0)), 0);
1091 }
1092 #[test]
1093 fn test_peel_lambdas_two() {
1094 let body = Expr::BVar(0);
1095 let lam1 = Expr::Lam(
1096 BinderInfo::Default,
1097 Name::str("_"),
1098 Node::new(lit(0)),
1099 Node::new(body),
1100 );
1101 let lam2 = Expr::Lam(
1102 BinderInfo::Implicit,
1103 Name::str("_"),
1104 Node::new(lit(0)),
1105 Node::new(lam1),
1106 );
1107 let (binders, inner) = peel_lambdas(&lam2, 2);
1108 assert_eq!(binders.len(), 2);
1109 assert_eq!(binders[0].1, BinderInfo::Implicit);
1110 assert_eq!(binders[1].1, BinderInfo::Default);
1111 assert_eq!(*inner, Expr::BVar(0));
1112 }
1113 #[test]
1114 fn test_apply_args_empty() {
1115 let e = lit(5);
1116 let result = apply_args(&e, &[]);
1117 assert_eq!(result, e);
1118 }
1119}
1120#[allow(dead_code)]
1122pub fn substitute_fvars(expr: &Expr, fvars: &[FVarId], replacements: &[Expr]) -> Expr {
1123 assert_eq!(fvars.len(), replacements.len());
1124 let mut subst = Substitution::new();
1125 for (fvar, rep) in fvars.iter().zip(replacements.iter()) {
1126 subst.insert(*fvar, rep.clone());
1127 }
1128 subst.apply(expr)
1129}
1130#[allow(dead_code)]
1132pub fn expr_contains_fvar(expr: &Expr, fvars: &[FVarId]) -> bool {
1133 match expr {
1134 Expr::FVar(id) => fvars.contains(id),
1135 Expr::App(f, a) => expr_contains_fvar(f, fvars) || expr_contains_fvar(a, fvars),
1136 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
1137 expr_contains_fvar(ty, fvars) || expr_contains_fvar(body, fvars)
1138 }
1139 Expr::Let(_, ty, val, body) => {
1140 expr_contains_fvar(ty, fvars)
1141 || expr_contains_fvar(val, fvars)
1142 || expr_contains_fvar(body, fvars)
1143 }
1144 Expr::Proj(_, _, inner) => expr_contains_fvar(inner, fvars),
1145 _ => false,
1146 }
1147}
1148#[allow(dead_code)]
1152pub fn open_binder(body: &Expr, id: FVarId) -> Expr {
1153 open_binder_at(body, &Expr::FVar(id), 0)
1154}
1155fn open_binder_at(expr: &Expr, fvar_expr: &Expr, depth: u32) -> Expr {
1156 match expr {
1157 Expr::BVar(n) => {
1158 if *n == depth {
1159 fvar_expr.clone()
1160 } else {
1161 expr.clone()
1162 }
1163 }
1164 Expr::App(f, a) => {
1165 let f2 = open_binder_at(f, fvar_expr, depth);
1166 let a2 = open_binder_at(a, fvar_expr, depth);
1167 Expr::App(Node::new(f2), Node::new(a2))
1168 }
1169 Expr::Lam(bi, name, ty, body) => {
1170 let ty2 = open_binder_at(ty, fvar_expr, depth);
1171 let body2 = open_binder_at(body, fvar_expr, depth + 1);
1172 Expr::Lam(*bi, name.clone(), Node::new(ty2), Node::new(body2))
1173 }
1174 Expr::Pi(bi, name, ty, body) => {
1175 let ty2 = open_binder_at(ty, fvar_expr, depth);
1176 let body2 = open_binder_at(body, fvar_expr, depth + 1);
1177 Expr::Pi(*bi, name.clone(), Node::new(ty2), Node::new(body2))
1178 }
1179 Expr::Let(name, ty, val, body) => {
1180 let ty2 = open_binder_at(ty, fvar_expr, depth);
1181 let val2 = open_binder_at(val, fvar_expr, depth);
1182 let body2 = open_binder_at(body, fvar_expr, depth + 1);
1183 Expr::Let(
1184 name.clone(),
1185 Node::new(ty2),
1186 Node::new(val2),
1187 Node::new(body2),
1188 )
1189 }
1190 Expr::Proj(name, idx, inner) => {
1191 let inner2 = open_binder_at(inner, fvar_expr, depth);
1192 Expr::Proj(name.clone(), *idx, Node::new(inner2))
1193 }
1194 _ => expr.clone(),
1195 }
1196}
1197#[cfg(test)]
1198mod extended3_subst_tests {
1199 use super::*;
1200 use crate::{BinderInfo, Expr, FVarId, Literal, Name};
1201 fn lit(n: u64) -> Expr {
1202 Expr::Lit(Literal::nat(n))
1203 }
1204 fn fvar(id: u64) -> Expr {
1205 Expr::FVar(FVarId(id))
1206 }
1207 #[test]
1208 fn test_substitution_insert_and_get() {
1209 let mut s = Substitution::new();
1210 s.insert(FVarId(0), lit(42));
1211 assert_eq!(s.get(FVarId(0)), Some(&lit(42)));
1212 assert_eq!(s.get(FVarId(1)), None);
1213 }
1214 #[test]
1215 fn test_substitution_len() {
1216 let mut s = Substitution::new();
1217 assert_eq!(s.len(), 0);
1218 s.insert(FVarId(0), lit(1));
1219 assert_eq!(s.len(), 1);
1220 }
1221 #[test]
1222 fn test_substitution_apply_fvar() {
1223 let mut s = Substitution::new();
1224 s.insert(FVarId(1), lit(99));
1225 let expr = fvar(1);
1226 assert_eq!(s.apply(&expr), lit(99));
1227 }
1228 #[test]
1229 fn test_substitution_apply_no_match() {
1230 let s = Substitution::new();
1231 let expr = fvar(5);
1232 assert_eq!(s.apply(&expr), fvar(5));
1233 }
1234 #[test]
1235 fn test_substitution_remove() {
1236 let mut s = Substitution::new();
1237 s.insert(FVarId(0), lit(1));
1238 s.remove(FVarId(0));
1239 assert_eq!(s.len(), 0);
1240 }
1241 #[test]
1242 fn test_substitution_restrict() {
1243 let mut s = Substitution::new();
1244 s.insert(FVarId(0), lit(1));
1245 s.insert(FVarId(1), lit(2));
1246 let r = s.restrict(&[FVarId(0)]);
1247 assert_eq!(r.len(), 1);
1248 assert!(r.get(FVarId(0)).is_some());
1249 assert!(r.get(FVarId(1)).is_none());
1250 }
1251 #[test]
1252 fn test_substitute_fvars_parallel() {
1253 let expr = Expr::App(Node::new(fvar(0)), Node::new(fvar(1)));
1254 let result = substitute_fvars(&expr, &[FVarId(0), FVarId(1)], &[lit(10), lit(20)]);
1255 match result {
1256 Expr::App(f, a) => {
1257 assert_eq!(*f, lit(10));
1258 assert_eq!(*a, lit(20));
1259 }
1260 _ => panic!("Expected App"),
1261 }
1262 }
1263 #[test]
1264 fn test_expr_contains_fvar_true() {
1265 let expr = Expr::App(Node::new(fvar(3)), Node::new(lit(0)));
1266 assert!(expr_contains_fvar(&expr, &[FVarId(3)]));
1267 }
1268 #[test]
1269 fn test_expr_contains_fvar_false() {
1270 let expr = lit(5);
1271 assert!(!expr_contains_fvar(&expr, &[FVarId(0)]));
1272 }
1273 #[test]
1274 fn test_open_binder_simple() {
1275 let body = Expr::BVar(0);
1276 let result = open_binder(&body, FVarId(7));
1277 assert_eq!(result, fvar(7));
1278 }
1279 #[test]
1280 fn test_open_binder_nested() {
1281 let inner_body = Expr::BVar(0);
1282 let inner_lam = Expr::Lam(
1283 BinderInfo::Default,
1284 Name::str("_"),
1285 Node::new(Expr::Const(Name::str("Nat"), vec![])),
1286 Node::new(inner_body),
1287 );
1288 let outer_body = Expr::App(Node::new(inner_lam), Node::new(Expr::BVar(0)));
1289 let opened = open_binder(&outer_body, FVarId(99));
1290 match opened {
1291 Expr::App(f, arg) => {
1292 assert!(matches!(*f, Expr::Lam(_, _, _, _)));
1293 assert_eq!(*arg, fvar(99));
1294 }
1295 _ => panic!("Expected App"),
1296 }
1297 }
1298 #[test]
1299 fn test_substitution_compose() {
1300 let mut s1 = Substitution::new();
1301 s1.insert(FVarId(0), lit(10));
1302 let mut s2 = Substitution::new();
1303 s2.insert(FVarId(1), lit(20));
1304 let composed = s1.compose(&s2);
1305 assert_eq!(composed.get(FVarId(0)), Some(&lit(10)));
1306 assert_eq!(composed.get(FVarId(1)), Some(&lit(20)));
1307 }
1308 #[test]
1309 fn test_substitution_insert_replace() {
1310 let mut s = Substitution::new();
1311 s.insert(FVarId(0), lit(1));
1312 s.insert(FVarId(0), lit(2));
1313 assert_eq!(s.len(), 1);
1314 assert_eq!(s.get(FVarId(0)), Some(&lit(2)));
1315 }
1316}
1317#[cfg(test)]
1318mod tests_padding_infra {
1319 use super::*;
1320 #[test]
1321 fn test_stat_summary() {
1322 let mut ss = StatSummary::new();
1323 ss.record(10.0);
1324 ss.record(20.0);
1325 ss.record(30.0);
1326 assert_eq!(ss.count(), 3);
1327 assert!((ss.mean().expect("mean should succeed") - 20.0).abs() < 1e-9);
1328 assert_eq!(ss.min().expect("min should succeed") as i64, 10);
1329 assert_eq!(ss.max().expect("max should succeed") as i64, 30);
1330 }
1331 #[test]
1332 fn test_transform_stat() {
1333 let mut ts = TransformStat::new();
1334 ts.record_before(100.0);
1335 ts.record_after(80.0);
1336 let ratio = ts.mean_ratio().expect("ratio should be present");
1337 assert!((ratio - 0.8).abs() < 1e-9);
1338 }
1339 #[test]
1340 fn test_small_map() {
1341 let mut m: SmallMap<u32, &str> = SmallMap::new();
1342 m.insert(3, "three");
1343 m.insert(1, "one");
1344 m.insert(2, "two");
1345 assert_eq!(m.get(&2), Some(&"two"));
1346 assert_eq!(m.len(), 3);
1347 let keys = m.keys();
1348 assert_eq!(*keys[0], 1);
1349 assert_eq!(*keys[2], 3);
1350 }
1351 #[test]
1352 fn test_label_set() {
1353 let mut ls = LabelSet::new();
1354 ls.add("foo");
1355 ls.add("bar");
1356 ls.add("foo");
1357 assert_eq!(ls.count(), 2);
1358 assert!(ls.has("bar"));
1359 assert!(!ls.has("baz"));
1360 }
1361 #[test]
1362 fn test_config_node() {
1363 let mut root = ConfigNode::section("root");
1364 let child = ConfigNode::leaf("key", "value");
1365 root.add_child(child);
1366 assert_eq!(root.num_children(), 1);
1367 }
1368 #[test]
1369 fn test_versioned_record() {
1370 let mut vr = VersionedRecord::new(0u32);
1371 vr.update(1);
1372 vr.update(2);
1373 assert_eq!(*vr.current(), 2);
1374 assert_eq!(vr.version(), 2);
1375 assert!(vr.has_history());
1376 assert_eq!(*vr.at_version(0).expect("value should be present"), 0);
1377 }
1378 #[test]
1379 fn test_simple_dag() {
1380 let mut dag = SimpleDag::new(4);
1381 dag.add_edge(0, 1);
1382 dag.add_edge(1, 2);
1383 dag.add_edge(2, 3);
1384 assert!(dag.can_reach(0, 3));
1385 assert!(!dag.can_reach(3, 0));
1386 let order = dag.topological_sort().expect("order should be present");
1387 assert_eq!(order, vec![0, 1, 2, 3]);
1388 }
1389 #[test]
1390 fn test_focus_stack() {
1391 let mut fs: FocusStack<&str> = FocusStack::new();
1392 fs.focus("a");
1393 fs.focus("b");
1394 assert_eq!(fs.current(), Some(&"b"));
1395 assert_eq!(fs.depth(), 2);
1396 fs.blur();
1397 assert_eq!(fs.current(), Some(&"a"));
1398 }
1399}
1400#[cfg(test)]
1401mod tests_extra_iterators {
1402 use super::*;
1403 #[test]
1404 fn test_window_iterator() {
1405 let data = vec![1u32, 2, 3, 4, 5];
1406 let windows: Vec<_> = WindowIterator::new(&data, 3).collect();
1407 assert_eq!(windows.len(), 3);
1408 assert_eq!(windows[0], &[1, 2, 3]);
1409 assert_eq!(windows[2], &[3, 4, 5]);
1410 }
1411 #[test]
1412 fn test_non_empty_vec() {
1413 let mut nev = NonEmptyVec::singleton(10u32);
1414 nev.push(20);
1415 nev.push(30);
1416 assert_eq!(nev.len(), 3);
1417 assert_eq!(*nev.first(), 10);
1418 assert_eq!(*nev.last(), 30);
1419 }
1420}
1421#[cfg(test)]
1422mod tests_padding2 {
1423 use super::*;
1424 #[test]
1425 fn test_sliding_sum() {
1426 let mut ss = SlidingSum::new(3);
1427 ss.push(1.0);
1428 ss.push(2.0);
1429 ss.push(3.0);
1430 assert!((ss.sum() - 6.0).abs() < 1e-9);
1431 ss.push(4.0);
1432 assert!((ss.sum() - 9.0).abs() < 1e-9);
1433 assert_eq!(ss.count(), 3);
1434 }
1435 #[test]
1436 fn test_path_buf() {
1437 let mut pb = PathBuf::new();
1438 pb.push("src");
1439 pb.push("main");
1440 assert_eq!(pb.as_str(), "src/main");
1441 assert_eq!(pb.depth(), 2);
1442 pb.pop();
1443 assert_eq!(pb.as_str(), "src");
1444 }
1445 #[test]
1446 fn test_string_pool() {
1447 let mut pool = StringPool::new();
1448 let s = pool.take();
1449 assert!(s.is_empty());
1450 pool.give("hello".to_string());
1451 let s2 = pool.take();
1452 assert!(s2.is_empty());
1453 assert_eq!(pool.free_count(), 0);
1454 }
1455 #[test]
1456 fn test_transitive_closure() {
1457 let mut tc = TransitiveClosure::new(4);
1458 tc.add_edge(0, 1);
1459 tc.add_edge(1, 2);
1460 tc.add_edge(2, 3);
1461 assert!(tc.can_reach(0, 3));
1462 assert!(!tc.can_reach(3, 0));
1463 let r = tc.reachable_from(0);
1464 assert_eq!(r.len(), 4);
1465 }
1466 #[test]
1467 fn test_token_bucket() {
1468 let mut tb = TokenBucket::new(100, 0);
1469 assert_eq!(tb.available(), 100);
1470 assert!(tb.try_consume(50));
1471 assert_eq!(tb.available(), 50);
1472 assert!(!tb.try_consume(60));
1473 assert_eq!(tb.capacity(), 100);
1474 }
1475 #[test]
1476 fn test_rewrite_rule_set() {
1477 let mut rrs = RewriteRuleSet::new();
1478 rrs.add(RewriteRule::unconditional(
1479 "beta",
1480 "App(Lam(x, b), v)",
1481 "b[x:=v]",
1482 ));
1483 rrs.add(RewriteRule::conditional("comm", "a + b", "b + a"));
1484 assert_eq!(rrs.len(), 2);
1485 assert_eq!(rrs.unconditional_rules().len(), 1);
1486 assert_eq!(rrs.conditional_rules().len(), 1);
1487 assert!(rrs.get("beta").is_some());
1488 let disp = rrs
1489 .get("beta")
1490 .expect("element at \'beta\' should exist")
1491 .display();
1492 assert!(disp.contains("→"));
1493 }
1494}
1495#[cfg(test)]
1496mod tests_padding3 {
1497 use super::*;
1498 #[test]
1499 fn test_decision_node() {
1500 let tree = DecisionNode::Branch {
1501 key: "x".into(),
1502 val: "1".into(),
1503 yes_branch: Box::new(DecisionNode::Leaf("yes".into())),
1504 no_branch: Box::new(DecisionNode::Leaf("no".into())),
1505 };
1506 let mut ctx = std::collections::HashMap::new();
1507 ctx.insert("x".into(), "1".into());
1508 assert_eq!(tree.evaluate(&ctx), "yes");
1509 ctx.insert("x".into(), "2".into());
1510 assert_eq!(tree.evaluate(&ctx), "no");
1511 assert_eq!(tree.depth(), 1);
1512 }
1513 #[test]
1514 fn test_flat_substitution() {
1515 let mut sub = FlatSubstitution::new();
1516 sub.add("foo", "bar");
1517 sub.add("baz", "qux");
1518 assert_eq!(sub.apply("foo and baz"), "bar and qux");
1519 assert_eq!(sub.len(), 2);
1520 }
1521 #[test]
1522 fn test_stopwatch() {
1523 let mut sw = Stopwatch::start();
1524 sw.split();
1525 sw.split();
1526 assert_eq!(sw.num_splits(), 2);
1527 assert!(sw.elapsed_ms() >= 0.0);
1528 for &s in sw.splits() {
1529 assert!(s >= 0.0);
1530 }
1531 }
1532 #[test]
1533 fn test_either2() {
1534 let e: Either2<i32, &str> = Either2::First(42);
1535 assert!(e.is_first());
1536 let mapped = e.map_first(|x| x * 2);
1537 assert_eq!(mapped.first(), Some(84));
1538 let e2: Either2<i32, &str> = Either2::Second("hello");
1539 assert!(e2.is_second());
1540 assert_eq!(e2.second(), Some("hello"));
1541 }
1542 #[test]
1543 fn test_write_once() {
1544 let wo: WriteOnce<u32> = WriteOnce::new();
1545 assert!(!wo.is_written());
1546 assert!(wo.write(42));
1547 assert!(!wo.write(99));
1548 assert_eq!(wo.read(), Some(42));
1549 }
1550 #[test]
1551 fn test_sparse_vec() {
1552 let mut sv: SparseVec<i32> = SparseVec::new(100);
1553 sv.set(5, 10);
1554 sv.set(50, 20);
1555 assert_eq!(*sv.get(5), 10);
1556 assert_eq!(*sv.get(50), 20);
1557 assert_eq!(*sv.get(0), 0);
1558 assert_eq!(sv.nnz(), 2);
1559 sv.set(5, 0);
1560 assert_eq!(sv.nnz(), 1);
1561 }
1562 #[test]
1563 fn test_stack_calc() {
1564 let mut calc = StackCalc::new();
1565 calc.push(3);
1566 calc.push(4);
1567 assert_eq!(calc.add(), Some(7));
1568 assert_eq!(calc.peek(), Some(7));
1569 calc.push(2);
1570 assert_eq!(calc.mul(), Some(14));
1571 assert_eq!(calc.peek(), Some(14));
1572 }
1573}