1use crate::{Expr, FVarId};
6use std::collections::HashMap;
7
8use super::types::{
9 ConfigNode, DecisionNode, Either2, FlatSubstitution, FocusStack, LabelSet, NonEmptyVec,
10 PathBuf, RewriteRule, RewriteRuleSet, SimpleDag, SlidingSum, SmallMap, SparseVec, StackCalc,
11 StatSummary, Stopwatch, StringPool, SubstStats, Substitution, TokenBucket, TransformStat,
12 TransitiveClosure, VersionedRecord, WindowIterator, WriteOnce,
13};
14
15pub fn instantiate(body: &Expr, arg: &Expr) -> Expr {
19 instantiate_at(body, arg, 0)
20}
21fn instantiate_at(expr: &Expr, arg: &Expr, depth: u32) -> Expr {
22 match expr {
23 Expr::BVar(n) => {
24 if *n == depth {
25 arg.clone()
26 } else if *n > depth {
27 Expr::BVar(*n - 1)
28 } else {
29 expr.clone()
30 }
31 }
32 Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
33 Expr::App(f, a) => {
34 let f_new = instantiate_at(f, arg, depth);
35 let a_new = instantiate_at(a, arg, depth);
36 Expr::App(Box::new(f_new), Box::new(a_new))
37 }
38 Expr::Lam(bi, name, ty, body) => {
39 let ty_new = instantiate_at(ty, arg, depth);
40 let body_new = instantiate_at(body, arg, depth + 1);
41 Expr::Lam(*bi, name.clone(), Box::new(ty_new), Box::new(body_new))
42 }
43 Expr::Pi(bi, name, ty, body) => {
44 let ty_new = instantiate_at(ty, arg, depth);
45 let body_new = instantiate_at(body, arg, depth + 1);
46 Expr::Pi(*bi, name.clone(), Box::new(ty_new), Box::new(body_new))
47 }
48 Expr::Let(name, ty, val, body) => {
49 let ty_new = instantiate_at(ty, arg, depth);
50 let val_new = instantiate_at(val, arg, depth);
51 let body_new = instantiate_at(body, arg, depth + 1);
52 Expr::Let(
53 name.clone(),
54 Box::new(ty_new),
55 Box::new(val_new),
56 Box::new(body_new),
57 )
58 }
59 Expr::Proj(name, idx, e) => {
60 let e_new = instantiate_at(e, arg, depth);
61 Expr::Proj(name.clone(), *idx, Box::new(e_new))
62 }
63 }
64}
65pub fn abstract_expr(expr: &Expr, fvar: FVarId) -> Expr {
69 abstract_at(expr, fvar, 0)
70}
71fn abstract_at(expr: &Expr, fvar: FVarId, depth: u32) -> Expr {
72 match expr {
73 Expr::FVar(id) if *id == fvar => Expr::BVar(depth),
74 Expr::BVar(n) => Expr::BVar(*n + 1),
75 Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
76 Expr::App(f, a) => {
77 let f_new = abstract_at(f, fvar, depth);
78 let a_new = abstract_at(a, fvar, depth);
79 Expr::App(Box::new(f_new), Box::new(a_new))
80 }
81 Expr::Lam(bi, name, ty, body) => {
82 let ty_new = abstract_at(ty, fvar, depth);
83 let body_new = abstract_at(body, fvar, depth + 1);
84 Expr::Lam(*bi, name.clone(), Box::new(ty_new), Box::new(body_new))
85 }
86 Expr::Pi(bi, name, ty, body) => {
87 let ty_new = abstract_at(ty, fvar, depth);
88 let body_new = abstract_at(body, fvar, depth + 1);
89 Expr::Pi(*bi, name.clone(), Box::new(ty_new), Box::new(body_new))
90 }
91 Expr::Let(name, ty, val, body) => {
92 let ty_new = abstract_at(ty, fvar, depth);
93 let val_new = abstract_at(val, fvar, depth);
94 let body_new = abstract_at(body, fvar, depth + 1);
95 Expr::Let(
96 name.clone(),
97 Box::new(ty_new),
98 Box::new(val_new),
99 Box::new(body_new),
100 )
101 }
102 Expr::Proj(name, idx, e) => {
103 let e_new = abstract_at(e, fvar, depth);
104 Expr::Proj(name.clone(), *idx, Box::new(e_new))
105 }
106 }
107}
108#[cfg(test)]
109mod tests {
110 use super::*;
111 #[test]
112 fn test_instantiate_simple() {
113 let bvar0 = Expr::BVar(0);
114 let fvar = Expr::FVar(FVarId(1));
115 let result = instantiate(&bvar0, &fvar);
116 assert_eq!(result, fvar);
117 }
118 #[test]
119 fn test_instantiate_shift_down() {
120 let bvar1 = Expr::BVar(1);
121 let arg = Expr::FVar(FVarId(1));
122 let result = instantiate(&bvar1, &arg);
123 assert_eq!(result, Expr::BVar(0));
124 }
125 #[test]
126 fn test_abstract_roundtrip() {
127 let fvar_id = FVarId(42);
128 let fvar = Expr::FVar(fvar_id);
129 let abstracted = abstract_expr(&fvar, fvar_id);
130 let back = instantiate(&abstracted, &fvar);
131 assert_eq!(back, fvar);
132 }
133 #[test]
134 fn test_instantiate_app() {
135 let app = Expr::App(Box::new(Expr::BVar(0)), Box::new(Expr::BVar(1)));
136 let arg = Expr::FVar(FVarId(99));
137 let result = instantiate(&app, &arg);
138 match result {
139 Expr::App(f, a) => {
140 assert_eq!(*f, arg);
141 assert_eq!(*a, Expr::BVar(0));
142 }
143 _ => panic!("Expected App"),
144 }
145 }
146}
147pub type SubstMap = HashMap<FVarId, Expr>;
149pub fn instantiate_many(expr: &Expr, args: &[Expr]) -> Expr {
151 let k = args.len() as u32;
152 if k == 0 {
153 return expr.clone();
154 }
155 instantiate_many_at(expr, args, k, 0)
156}
157fn instantiate_many_at(expr: &Expr, args: &[Expr], k: u32, offset: u32) -> Expr {
158 match expr {
159 Expr::BVar(n) => {
160 let idx = *n;
161 if idx >= offset && idx < offset + k {
162 args[(idx - offset) as usize].clone()
163 } else if idx >= offset + k {
164 Expr::BVar(idx - k)
165 } else {
166 expr.clone()
167 }
168 }
169 Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
170 Expr::App(f, a) => Expr::App(
171 Box::new(instantiate_many_at(f, args, k, offset)),
172 Box::new(instantiate_many_at(a, args, k, offset)),
173 ),
174 Expr::Lam(bi, name, ty, body) => Expr::Lam(
175 *bi,
176 name.clone(),
177 Box::new(instantiate_many_at(ty, args, k, offset)),
178 Box::new(instantiate_many_at(body, args, k, offset + 1)),
179 ),
180 Expr::Pi(bi, name, ty, body) => Expr::Pi(
181 *bi,
182 name.clone(),
183 Box::new(instantiate_many_at(ty, args, k, offset)),
184 Box::new(instantiate_many_at(body, args, k, offset + 1)),
185 ),
186 Expr::Let(name, ty, val, body) => Expr::Let(
187 name.clone(),
188 Box::new(instantiate_many_at(ty, args, k, offset)),
189 Box::new(instantiate_many_at(val, args, k, offset)),
190 Box::new(instantiate_many_at(body, args, k, offset + 1)),
191 ),
192 Expr::Proj(name, idx, e) => Expr::Proj(
193 name.clone(),
194 *idx,
195 Box::new(instantiate_many_at(e, args, k, offset)),
196 ),
197 }
198}
199pub fn parallel_subst(expr: &Expr, map: &SubstMap) -> Expr {
202 if map.is_empty() {
203 return expr.clone();
204 }
205 parallel_subst_impl(expr, map)
206}
207fn parallel_subst_impl(expr: &Expr, map: &SubstMap) -> Expr {
208 match expr {
209 Expr::FVar(id) => {
210 if let Some(replacement) = map.get(id) {
211 replacement.clone()
212 } else {
213 expr.clone()
214 }
215 }
216 Expr::BVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
217 Expr::App(f, a) => Expr::App(
218 Box::new(parallel_subst_impl(f, map)),
219 Box::new(parallel_subst_impl(a, map)),
220 ),
221 Expr::Lam(bi, name, ty, body) => Expr::Lam(
222 *bi,
223 name.clone(),
224 Box::new(parallel_subst_impl(ty, map)),
225 Box::new(parallel_subst_impl(body, map)),
226 ),
227 Expr::Pi(bi, name, ty, body) => Expr::Pi(
228 *bi,
229 name.clone(),
230 Box::new(parallel_subst_impl(ty, map)),
231 Box::new(parallel_subst_impl(body, map)),
232 ),
233 Expr::Let(name, ty, val, body) => Expr::Let(
234 name.clone(),
235 Box::new(parallel_subst_impl(ty, map)),
236 Box::new(parallel_subst_impl(val, map)),
237 Box::new(parallel_subst_impl(body, map)),
238 ),
239 Expr::Proj(name, idx, e) => {
240 Expr::Proj(name.clone(), *idx, Box::new(parallel_subst_impl(e, map)))
241 }
242 }
243}
244pub fn shift_bvars(expr: &Expr, amount: u32, cutoff: u32) -> Expr {
246 match expr {
247 Expr::BVar(i) => {
248 if *i >= cutoff {
249 Expr::BVar(*i + amount)
250 } else {
251 expr.clone()
252 }
253 }
254 Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
255 Expr::App(f, a) => Expr::App(
256 Box::new(shift_bvars(f, amount, cutoff)),
257 Box::new(shift_bvars(a, amount, cutoff)),
258 ),
259 Expr::Lam(bi, name, ty, body) => Expr::Lam(
260 *bi,
261 name.clone(),
262 Box::new(shift_bvars(ty, amount, cutoff)),
263 Box::new(shift_bvars(body, amount, cutoff + 1)),
264 ),
265 Expr::Pi(bi, name, ty, body) => Expr::Pi(
266 *bi,
267 name.clone(),
268 Box::new(shift_bvars(ty, amount, cutoff)),
269 Box::new(shift_bvars(body, amount, cutoff + 1)),
270 ),
271 Expr::Let(name, ty, val, body) => Expr::Let(
272 name.clone(),
273 Box::new(shift_bvars(ty, amount, cutoff)),
274 Box::new(shift_bvars(val, amount, cutoff)),
275 Box::new(shift_bvars(body, amount, cutoff + 1)),
276 ),
277 Expr::Proj(name, idx, e) => {
278 Expr::Proj(name.clone(), *idx, Box::new(shift_bvars(e, amount, cutoff)))
279 }
280 }
281}
282pub fn free_vars(expr: &Expr) -> std::collections::HashSet<FVarId> {
284 let mut set = std::collections::HashSet::new();
285 free_vars_impl(expr, &mut set);
286 set
287}
288fn free_vars_impl(expr: &Expr, set: &mut std::collections::HashSet<FVarId>) {
289 match expr {
290 Expr::FVar(id) => {
291 set.insert(*id);
292 }
293 Expr::App(f, a) => {
294 free_vars_impl(f, set);
295 free_vars_impl(a, set);
296 }
297 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
298 free_vars_impl(ty, set);
299 free_vars_impl(body, set);
300 }
301 Expr::Let(_, ty, val, body) => {
302 free_vars_impl(ty, set);
303 free_vars_impl(val, set);
304 free_vars_impl(body, set);
305 }
306 Expr::Proj(_, _, e) => free_vars_impl(e, set),
307 _ => {}
308 }
309}
310pub fn occurs_free(expr: &Expr, fvar: FVarId) -> bool {
312 match expr {
313 Expr::FVar(id) => *id == fvar,
314 Expr::App(f, a) => occurs_free(f, fvar) || occurs_free(a, fvar),
315 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
316 occurs_free(ty, fvar) || occurs_free(body, fvar)
317 }
318 Expr::Let(_, ty, val, body) => {
319 occurs_free(ty, fvar) || occurs_free(val, fvar) || occurs_free(body, fvar)
320 }
321 Expr::Proj(_, _, e) => occurs_free(e, fvar),
322 _ => false,
323 }
324}
325pub fn count_free_occurrences(expr: &Expr, fvar: FVarId) -> usize {
327 match expr {
328 Expr::FVar(id) => usize::from(*id == fvar),
329 Expr::App(f, a) => count_free_occurrences(f, fvar) + count_free_occurrences(a, fvar),
330 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
331 count_free_occurrences(ty, fvar) + count_free_occurrences(body, fvar)
332 }
333 Expr::Let(_, ty, val, body) => {
334 count_free_occurrences(ty, fvar)
335 + count_free_occurrences(val, fvar)
336 + count_free_occurrences(body, fvar)
337 }
338 Expr::Proj(_, _, e) => count_free_occurrences(e, fvar),
339 _ => 0,
340 }
341}
342pub fn instantiate_tracked(body: &Expr, arg: &Expr, stats: &mut SubstStats) -> Expr {
344 instantiate_tracked_at(body, arg, 0, stats)
345}
346fn instantiate_tracked_at(expr: &Expr, arg: &Expr, depth: u32, stats: &mut SubstStats) -> Expr {
347 stats.nodes_visited += 1;
348 match expr {
349 Expr::BVar(n) => {
350 if *n == depth {
351 stats.bvar_hits += 1;
352 arg.clone()
353 } else if *n > depth {
354 stats.bvar_misses += 1;
355 Expr::BVar(*n - 1)
356 } else {
357 expr.clone()
358 }
359 }
360 Expr::Sort(_) | Expr::FVar(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
361 Expr::App(f, a) => Expr::App(
362 Box::new(instantiate_tracked_at(f, arg, depth, stats)),
363 Box::new(instantiate_tracked_at(a, arg, depth, stats)),
364 ),
365 Expr::Lam(bi, name, ty, body) => Expr::Lam(
366 *bi,
367 name.clone(),
368 Box::new(instantiate_tracked_at(ty, arg, depth, stats)),
369 Box::new(instantiate_tracked_at(body, arg, depth + 1, stats)),
370 ),
371 Expr::Pi(bi, name, ty, body) => Expr::Pi(
372 *bi,
373 name.clone(),
374 Box::new(instantiate_tracked_at(ty, arg, depth, stats)),
375 Box::new(instantiate_tracked_at(body, arg, depth + 1, stats)),
376 ),
377 Expr::Let(name, ty, val, body) => Expr::Let(
378 name.clone(),
379 Box::new(instantiate_tracked_at(ty, arg, depth, stats)),
380 Box::new(instantiate_tracked_at(val, arg, depth, stats)),
381 Box::new(instantiate_tracked_at(body, arg, depth + 1, stats)),
382 ),
383 Expr::Proj(name, idx, e) => Expr::Proj(
384 name.clone(),
385 *idx,
386 Box::new(instantiate_tracked_at(e, arg, depth, stats)),
387 ),
388 }
389}
390pub fn build_subst_map(fvars: &[FVarId], replacements: &[Expr]) -> SubstMap {
392 assert_eq!(fvars.len(), replacements.len());
393 fvars
394 .iter()
395 .zip(replacements.iter())
396 .map(|(id, expr)| (*id, expr.clone()))
397 .collect()
398}
399pub fn try_beta_reduce(expr: &Expr) -> Option<Expr> {
401 match expr {
402 Expr::App(f, a) => {
403 if let Expr::Lam(_, _, _, body) = f.as_ref() {
404 Some(instantiate(body, a))
405 } else {
406 None
407 }
408 }
409 _ => None,
410 }
411}
412pub fn beta_reduce_head(expr: Expr) -> Expr {
414 let mut curr = expr;
415 while let Some(reduced) = try_beta_reduce(&curr) {
416 curr = reduced;
417 }
418 curr
419}
420pub fn subst_fvar(expr: &Expr, fvar: FVarId, replacement: &Expr) -> Expr {
422 let mut map = SubstMap::new();
423 map.insert(fvar, replacement.clone());
424 parallel_subst(expr, &map)
425}
426pub fn is_whnf_beta(expr: &Expr) -> bool {
428 match expr {
429 Expr::App(f, _) => match f.as_ref() {
430 Expr::Lam(_, _, _, _) => false,
431 other => is_whnf_beta(other),
432 },
433 _ => true,
434 }
435}
436pub fn collect_loose_bvar_indices(expr: &Expr) -> Vec<u32> {
438 let mut indices = Vec::new();
439 collect_loose_bvar_impl(expr, 0, &mut indices);
440 indices.sort();
441 indices.dedup();
442 indices
443}
444fn collect_loose_bvar_impl(expr: &Expr, depth: u32, result: &mut Vec<u32>) {
445 match expr {
446 Expr::BVar(n) => {
447 if *n >= depth {
448 result.push(*n - depth);
449 }
450 }
451 Expr::App(f, a) => {
452 collect_loose_bvar_impl(f, depth, result);
453 collect_loose_bvar_impl(a, depth, result);
454 }
455 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
456 collect_loose_bvar_impl(ty, depth, result);
457 collect_loose_bvar_impl(body, depth + 1, result);
458 }
459 Expr::Let(_, ty, val, body) => {
460 collect_loose_bvar_impl(ty, depth, result);
461 collect_loose_bvar_impl(val, depth, result);
462 collect_loose_bvar_impl(body, depth + 1, result);
463 }
464 Expr::Proj(_, _, e) => collect_loose_bvar_impl(e, depth, result),
465 _ => {}
466 }
467}
468pub fn parallel_subst_tracked(expr: &Expr, map: &SubstMap, stats: &mut SubstStats) -> Expr {
470 if map.is_empty() {
471 return expr.clone();
472 }
473 parallel_subst_tracked_impl(expr, map, stats)
474}
475fn parallel_subst_tracked_impl(expr: &Expr, map: &SubstMap, stats: &mut SubstStats) -> Expr {
476 stats.nodes_visited += 1;
477 match expr {
478 Expr::FVar(id) => {
479 if let Some(r) = map.get(id) {
480 stats.fvar_hits += 1;
481 r.clone()
482 } else {
483 expr.clone()
484 }
485 }
486 Expr::BVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
487 Expr::App(f, a) => Expr::App(
488 Box::new(parallel_subst_tracked_impl(f, map, stats)),
489 Box::new(parallel_subst_tracked_impl(a, map, stats)),
490 ),
491 Expr::Lam(bi, name, ty, body) => Expr::Lam(
492 *bi,
493 name.clone(),
494 Box::new(parallel_subst_tracked_impl(ty, map, stats)),
495 Box::new(parallel_subst_tracked_impl(body, map, stats)),
496 ),
497 Expr::Pi(bi, name, ty, body) => Expr::Pi(
498 *bi,
499 name.clone(),
500 Box::new(parallel_subst_tracked_impl(ty, map, stats)),
501 Box::new(parallel_subst_tracked_impl(body, map, stats)),
502 ),
503 Expr::Let(name, ty, val, body) => Expr::Let(
504 name.clone(),
505 Box::new(parallel_subst_tracked_impl(ty, map, stats)),
506 Box::new(parallel_subst_tracked_impl(val, map, stats)),
507 Box::new(parallel_subst_tracked_impl(body, map, stats)),
508 ),
509 Expr::Proj(name, idx, e) => Expr::Proj(
510 name.clone(),
511 *idx,
512 Box::new(parallel_subst_tracked_impl(e, map, stats)),
513 ),
514 }
515}
516#[cfg(test)]
517mod extended_tests {
518 use super::*;
519 use crate::{BinderInfo, Level, Literal, Name};
520 fn fvar(id: u64) -> Expr {
521 Expr::FVar(FVarId(id))
522 }
523 fn lit(n: u64) -> Expr {
524 Expr::Lit(Literal::Nat(n))
525 }
526 fn sort0() -> Expr {
527 Expr::Sort(Level::zero())
528 }
529 #[test]
530 fn test_instantiate_many_two() {
531 let app = Expr::App(Box::new(Expr::BVar(0)), Box::new(Expr::BVar(1)));
532 let result = instantiate_many(&app, &[lit(10), lit(20)]);
533 match result {
534 Expr::App(f, a) => {
535 assert_eq!(*f, lit(10));
536 assert_eq!(*a, lit(20));
537 }
538 _ => panic!("Expected App"),
539 }
540 }
541 #[test]
542 fn test_parallel_subst_single() {
543 let mut map = SubstMap::new();
544 map.insert(FVarId(1), lit(99));
545 let expr = fvar(1);
546 assert_eq!(parallel_subst(&expr, &map), lit(99));
547 }
548 #[test]
549 fn test_parallel_subst_empty() {
550 let expr = fvar(5);
551 let map = SubstMap::new();
552 assert_eq!(parallel_subst(&expr, &map), expr);
553 }
554 #[test]
555 fn test_shift_bvars_above_cutoff() {
556 let e = Expr::BVar(2);
557 let shifted = shift_bvars(&e, 3, 1);
558 assert_eq!(shifted, Expr::BVar(5));
559 }
560 #[test]
561 fn test_shift_bvars_below_cutoff() {
562 let e = Expr::BVar(0);
563 let shifted = shift_bvars(&e, 2, 1);
564 assert_eq!(shifted, Expr::BVar(0));
565 }
566 #[test]
567 fn test_free_vars_single() {
568 let set = free_vars(&fvar(7));
569 assert_eq!(set.len(), 1);
570 assert!(set.contains(&FVarId(7)));
571 }
572 #[test]
573 fn test_occurs_free_true() {
574 let expr = Expr::App(Box::new(fvar(1)), Box::new(fvar(2)));
575 assert!(occurs_free(&expr, FVarId(1)));
576 assert!(!occurs_free(&expr, FVarId(3)));
577 }
578 #[test]
579 fn test_count_free_occurrences() {
580 let expr = Expr::App(Box::new(fvar(1)), Box::new(fvar(1)));
581 assert_eq!(count_free_occurrences(&expr, FVarId(1)), 2);
582 }
583 #[test]
584 fn test_subst_stats_merge() {
585 let mut a = SubstStats {
586 bvar_hits: 3,
587 bvar_misses: 1,
588 fvar_hits: 2,
589 nodes_visited: 10,
590 };
591 let b = SubstStats {
592 bvar_hits: 1,
593 bvar_misses: 0,
594 fvar_hits: 1,
595 nodes_visited: 5,
596 };
597 a.merge(&b);
598 assert_eq!(a.bvar_hits, 4);
599 assert_eq!(a.total_substs(), 7);
600 }
601 #[test]
602 fn test_instantiate_tracked() {
603 let body = Expr::BVar(0);
604 let arg = lit(42);
605 let mut stats = SubstStats::default();
606 let result = instantiate_tracked(&body, &arg, &mut stats);
607 assert_eq!(result, lit(42));
608 assert_eq!(stats.bvar_hits, 1);
609 }
610 #[test]
611 fn test_build_subst_map() {
612 let fvars = vec![FVarId(1), FVarId(2)];
613 let replacements = vec![lit(10), lit(20)];
614 let map = build_subst_map(&fvars, &replacements);
615 assert_eq!(map.get(&FVarId(1)), Some(&lit(10)));
616 }
617 #[test]
618 fn test_try_beta_reduce_success() {
619 let lam = Expr::Lam(
620 BinderInfo::Default,
621 Name::str("x"),
622 Box::new(sort0()),
623 Box::new(Expr::BVar(0)),
624 );
625 let app = Expr::App(Box::new(lam), Box::new(lit(42)));
626 let result = try_beta_reduce(&app).expect("result should be present");
627 assert_eq!(result, lit(42));
628 }
629 #[test]
630 fn test_try_beta_reduce_fail() {
631 let app = Expr::App(Box::new(fvar(1)), Box::new(lit(42)));
632 assert!(try_beta_reduce(&app).is_none());
633 }
634 #[test]
635 fn test_beta_reduce_head() {
636 let lam = Expr::Lam(
637 BinderInfo::Default,
638 Name::str("x"),
639 Box::new(sort0()),
640 Box::new(Expr::BVar(0)),
641 );
642 let app = Expr::App(Box::new(lam), Box::new(lit(7)));
643 let result = beta_reduce_head(app);
644 assert_eq!(result, lit(7));
645 }
646 #[test]
647 fn test_subst_fvar() {
648 let expr = fvar(1);
649 let result = subst_fvar(&expr, FVarId(1), &lit(99));
650 assert_eq!(result, lit(99));
651 }
652 #[test]
653 fn test_is_whnf_beta_lam() {
654 let lam = Expr::Lam(
655 BinderInfo::Default,
656 Name::str("x"),
657 Box::new(sort0()),
658 Box::new(Expr::BVar(0)),
659 );
660 assert!(is_whnf_beta(&lam));
661 }
662 #[test]
663 fn test_is_whnf_beta_app_not_whnf() {
664 let lam = Expr::Lam(
665 BinderInfo::Default,
666 Name::str("x"),
667 Box::new(sort0()),
668 Box::new(Expr::BVar(0)),
669 );
670 let app = Expr::App(Box::new(lam), Box::new(lit(1)));
671 assert!(!is_whnf_beta(&app));
672 }
673 #[test]
674 fn test_collect_loose_bvar_indices() {
675 let e = Expr::App(Box::new(Expr::BVar(0)), Box::new(Expr::BVar(2)));
676 let indices = collect_loose_bvar_indices(&e);
677 assert!(indices.contains(&0));
678 assert!(indices.contains(&2));
679 }
680 #[test]
681 fn test_parallel_subst_tracked() {
682 let expr = Expr::App(Box::new(fvar(1)), Box::new(fvar(2)));
683 let mut map = SubstMap::new();
684 map.insert(FVarId(1), lit(10));
685 map.insert(FVarId(2), lit(20));
686 let mut stats = SubstStats::default();
687 let result = parallel_subst_tracked(&expr, &map, &mut stats);
688 assert_eq!(stats.fvar_hits, 2);
689 match result {
690 Expr::App(f, a) => {
691 assert_eq!(*f, lit(10));
692 assert_eq!(*a, lit(20));
693 }
694 _ => panic!("Expected App"),
695 }
696 }
697}
698pub fn apply_args(f: &Expr, args: &[Expr]) -> Expr {
703 let mut result = f.clone();
704 for arg in args {
705 if let Expr::Lam(_, _, _, body) = result {
706 result = instantiate(&body, arg);
707 } else {
708 result = Expr::App(Box::new(result), Box::new(arg.clone()));
709 }
710 }
711 result
712}
713#[inline]
715pub fn is_lambda(e: &Expr) -> bool {
716 matches!(e, Expr::Lam(_, _, _, _))
717}
718#[allow(clippy::type_complexity)]
723pub fn peel_lambdas(
724 e: &Expr,
725 n: usize,
726) -> (Vec<(crate::Name, crate::BinderInfo, Box<Expr>)>, &Expr) {
727 let mut binders = Vec::new();
728 let mut cur = e;
729 for _ in 0..n {
730 if let Expr::Lam(bi, _, ty, body) = cur {
731 binders.push((crate::Name::Anonymous, *bi, ty.clone()));
732 cur = body;
733 } else {
734 break;
735 }
736 }
737 (binders, cur)
738}
739pub fn count_lambdas(e: &Expr) -> usize {
741 let mut n = 0;
742 let mut cur = e;
743 while let Expr::Lam(_, _, _, body) = cur {
744 n += 1;
745 cur = body;
746 }
747 n
748}
749pub fn count_pis(e: &Expr) -> usize {
751 let mut n = 0;
752 let mut cur = e;
753 while let Expr::Pi(_, _, _, body) = cur {
754 n += 1;
755 cur = body;
756 }
757 n
758}
759#[cfg(test)]
760mod extended2_tests {
761 use super::*;
762 use crate::{BinderInfo, Expr, FVarId, Literal, Name};
763 fn lit(n: u64) -> Expr {
764 Expr::Lit(Literal::Nat(n))
765 }
766 fn fvar(id: u64) -> Expr {
767 Expr::FVar(FVarId(id))
768 }
769 #[test]
770 fn test_apply_args_lam() {
771 let body = Expr::BVar(0);
772 let lam = Expr::Lam(
773 BinderInfo::Default,
774 Name::str("_"),
775 Box::new(lit(0)),
776 Box::new(body),
777 );
778 let result = apply_args(&lam, &[lit(42)]);
779 assert_eq!(result, lit(42));
780 }
781 #[test]
782 fn test_apply_args_non_lam() {
783 let f = fvar(1);
784 let result = apply_args(&f, &[lit(1), lit(2)]);
785 match &result {
786 Expr::App(outer, arg2) => {
787 assert_eq!(**arg2, lit(2));
788 match outer.as_ref() {
789 Expr::App(inner_f, arg1) => {
790 assert_eq!(**inner_f, fvar(1));
791 assert_eq!(**arg1, lit(1));
792 }
793 _ => panic!("Expected App"),
794 }
795 }
796 _ => panic!("Expected App"),
797 }
798 }
799 #[test]
800 fn test_is_lambda() {
801 let lam = Expr::Lam(
802 BinderInfo::Default,
803 Name::str("_"),
804 Box::new(lit(0)),
805 Box::new(lit(1)),
806 );
807 assert!(is_lambda(&lam));
808 assert!(!is_lambda(&lit(0)));
809 }
810 #[test]
811 fn test_count_lambdas() {
812 let inner = lit(0);
813 let lam1 = Expr::Lam(
814 BinderInfo::Default,
815 Name::str("_"),
816 Box::new(lit(0)),
817 Box::new(inner),
818 );
819 let lam2 = Expr::Lam(
820 BinderInfo::Default,
821 Name::str("_"),
822 Box::new(lit(0)),
823 Box::new(lam1),
824 );
825 assert_eq!(count_lambdas(&lam2), 2);
826 assert_eq!(count_lambdas(&lit(0)), 0);
827 }
828 #[test]
829 fn test_count_pis() {
830 let inner = lit(0);
831 let pi1 = Expr::Pi(
832 BinderInfo::Default,
833 Name::str("_"),
834 Box::new(lit(0)),
835 Box::new(inner),
836 );
837 let pi2 = Expr::Pi(
838 BinderInfo::Default,
839 Name::str("_"),
840 Box::new(lit(0)),
841 Box::new(pi1),
842 );
843 assert_eq!(count_pis(&pi2), 2);
844 assert_eq!(count_pis(&lit(0)), 0);
845 }
846 #[test]
847 fn test_peel_lambdas_two() {
848 let body = Expr::BVar(0);
849 let lam1 = Expr::Lam(
850 BinderInfo::Default,
851 Name::str("_"),
852 Box::new(lit(0)),
853 Box::new(body),
854 );
855 let lam2 = Expr::Lam(
856 BinderInfo::Implicit,
857 Name::str("_"),
858 Box::new(lit(0)),
859 Box::new(lam1),
860 );
861 let (binders, inner) = peel_lambdas(&lam2, 2);
862 assert_eq!(binders.len(), 2);
863 assert_eq!(binders[0].1, BinderInfo::Implicit);
864 assert_eq!(binders[1].1, BinderInfo::Default);
865 assert_eq!(*inner, Expr::BVar(0));
866 }
867 #[test]
868 fn test_apply_args_empty() {
869 let e = lit(5);
870 let result = apply_args(&e, &[]);
871 assert_eq!(result, e);
872 }
873}
874#[allow(dead_code)]
876pub fn substitute_fvars(expr: &Expr, fvars: &[FVarId], replacements: &[Expr]) -> Expr {
877 assert_eq!(fvars.len(), replacements.len());
878 let mut subst = Substitution::new();
879 for (fvar, rep) in fvars.iter().zip(replacements.iter()) {
880 subst.insert(*fvar, rep.clone());
881 }
882 subst.apply(expr)
883}
884#[allow(dead_code)]
886pub fn expr_contains_fvar(expr: &Expr, fvars: &[FVarId]) -> bool {
887 match expr {
888 Expr::FVar(id) => fvars.contains(id),
889 Expr::App(f, a) => expr_contains_fvar(f, fvars) || expr_contains_fvar(a, fvars),
890 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
891 expr_contains_fvar(ty, fvars) || expr_contains_fvar(body, fvars)
892 }
893 Expr::Let(_, ty, val, body) => {
894 expr_contains_fvar(ty, fvars)
895 || expr_contains_fvar(val, fvars)
896 || expr_contains_fvar(body, fvars)
897 }
898 Expr::Proj(_, _, inner) => expr_contains_fvar(inner, fvars),
899 _ => false,
900 }
901}
902#[allow(dead_code)]
906pub fn open_binder(body: &Expr, id: FVarId) -> Expr {
907 open_binder_at(body, &Expr::FVar(id), 0)
908}
909fn open_binder_at(expr: &Expr, fvar_expr: &Expr, depth: u32) -> Expr {
910 match expr {
911 Expr::BVar(n) => {
912 if *n == depth {
913 fvar_expr.clone()
914 } else {
915 expr.clone()
916 }
917 }
918 Expr::App(f, a) => {
919 let f2 = open_binder_at(f, fvar_expr, depth);
920 let a2 = open_binder_at(a, fvar_expr, depth);
921 Expr::App(Box::new(f2), Box::new(a2))
922 }
923 Expr::Lam(bi, name, ty, body) => {
924 let ty2 = open_binder_at(ty, fvar_expr, depth);
925 let body2 = open_binder_at(body, fvar_expr, depth + 1);
926 Expr::Lam(*bi, name.clone(), Box::new(ty2), Box::new(body2))
927 }
928 Expr::Pi(bi, name, ty, body) => {
929 let ty2 = open_binder_at(ty, fvar_expr, depth);
930 let body2 = open_binder_at(body, fvar_expr, depth + 1);
931 Expr::Pi(*bi, name.clone(), Box::new(ty2), Box::new(body2))
932 }
933 Expr::Let(name, ty, val, body) => {
934 let ty2 = open_binder_at(ty, fvar_expr, depth);
935 let val2 = open_binder_at(val, fvar_expr, depth);
936 let body2 = open_binder_at(body, fvar_expr, depth + 1);
937 Expr::Let(name.clone(), Box::new(ty2), Box::new(val2), Box::new(body2))
938 }
939 Expr::Proj(name, idx, inner) => {
940 let inner2 = open_binder_at(inner, fvar_expr, depth);
941 Expr::Proj(name.clone(), *idx, Box::new(inner2))
942 }
943 _ => expr.clone(),
944 }
945}
946#[cfg(test)]
947mod extended3_subst_tests {
948 use super::*;
949 use crate::{BinderInfo, Expr, FVarId, Literal, Name};
950 fn lit(n: u64) -> Expr {
951 Expr::Lit(Literal::Nat(n))
952 }
953 fn fvar(id: u64) -> Expr {
954 Expr::FVar(FVarId(id))
955 }
956 #[test]
957 fn test_substitution_insert_and_get() {
958 let mut s = Substitution::new();
959 s.insert(FVarId(0), lit(42));
960 assert_eq!(s.get(FVarId(0)), Some(&lit(42)));
961 assert_eq!(s.get(FVarId(1)), None);
962 }
963 #[test]
964 fn test_substitution_len() {
965 let mut s = Substitution::new();
966 assert_eq!(s.len(), 0);
967 s.insert(FVarId(0), lit(1));
968 assert_eq!(s.len(), 1);
969 }
970 #[test]
971 fn test_substitution_apply_fvar() {
972 let mut s = Substitution::new();
973 s.insert(FVarId(1), lit(99));
974 let expr = fvar(1);
975 assert_eq!(s.apply(&expr), lit(99));
976 }
977 #[test]
978 fn test_substitution_apply_no_match() {
979 let s = Substitution::new();
980 let expr = fvar(5);
981 assert_eq!(s.apply(&expr), fvar(5));
982 }
983 #[test]
984 fn test_substitution_remove() {
985 let mut s = Substitution::new();
986 s.insert(FVarId(0), lit(1));
987 s.remove(FVarId(0));
988 assert_eq!(s.len(), 0);
989 }
990 #[test]
991 fn test_substitution_restrict() {
992 let mut s = Substitution::new();
993 s.insert(FVarId(0), lit(1));
994 s.insert(FVarId(1), lit(2));
995 let r = s.restrict(&[FVarId(0)]);
996 assert_eq!(r.len(), 1);
997 assert!(r.get(FVarId(0)).is_some());
998 assert!(r.get(FVarId(1)).is_none());
999 }
1000 #[test]
1001 fn test_substitute_fvars_parallel() {
1002 let expr = Expr::App(Box::new(fvar(0)), Box::new(fvar(1)));
1003 let result = substitute_fvars(&expr, &[FVarId(0), FVarId(1)], &[lit(10), lit(20)]);
1004 match result {
1005 Expr::App(f, a) => {
1006 assert_eq!(*f, lit(10));
1007 assert_eq!(*a, lit(20));
1008 }
1009 _ => panic!("Expected App"),
1010 }
1011 }
1012 #[test]
1013 fn test_expr_contains_fvar_true() {
1014 let expr = Expr::App(Box::new(fvar(3)), Box::new(lit(0)));
1015 assert!(expr_contains_fvar(&expr, &[FVarId(3)]));
1016 }
1017 #[test]
1018 fn test_expr_contains_fvar_false() {
1019 let expr = lit(5);
1020 assert!(!expr_contains_fvar(&expr, &[FVarId(0)]));
1021 }
1022 #[test]
1023 fn test_open_binder_simple() {
1024 let body = Expr::BVar(0);
1025 let result = open_binder(&body, FVarId(7));
1026 assert_eq!(result, fvar(7));
1027 }
1028 #[test]
1029 fn test_open_binder_nested() {
1030 let inner_body = Expr::BVar(0);
1031 let inner_lam = Expr::Lam(
1032 BinderInfo::Default,
1033 Name::str("_"),
1034 Box::new(Expr::Const(Name::str("Nat"), vec![])),
1035 Box::new(inner_body),
1036 );
1037 let outer_body = Expr::App(Box::new(inner_lam), Box::new(Expr::BVar(0)));
1038 let opened = open_binder(&outer_body, FVarId(99));
1039 match opened {
1040 Expr::App(f, arg) => {
1041 assert!(matches!(*f, Expr::Lam(_, _, _, _)));
1042 assert_eq!(*arg, fvar(99));
1043 }
1044 _ => panic!("Expected App"),
1045 }
1046 }
1047 #[test]
1048 fn test_substitution_compose() {
1049 let mut s1 = Substitution::new();
1050 s1.insert(FVarId(0), lit(10));
1051 let mut s2 = Substitution::new();
1052 s2.insert(FVarId(1), lit(20));
1053 let composed = s1.compose(&s2);
1054 assert_eq!(composed.get(FVarId(0)), Some(&lit(10)));
1055 assert_eq!(composed.get(FVarId(1)), Some(&lit(20)));
1056 }
1057 #[test]
1058 fn test_substitution_insert_replace() {
1059 let mut s = Substitution::new();
1060 s.insert(FVarId(0), lit(1));
1061 s.insert(FVarId(0), lit(2));
1062 assert_eq!(s.len(), 1);
1063 assert_eq!(s.get(FVarId(0)), Some(&lit(2)));
1064 }
1065}
1066#[cfg(test)]
1067mod tests_padding_infra {
1068 use super::*;
1069 #[test]
1070 fn test_stat_summary() {
1071 let mut ss = StatSummary::new();
1072 ss.record(10.0);
1073 ss.record(20.0);
1074 ss.record(30.0);
1075 assert_eq!(ss.count(), 3);
1076 assert!((ss.mean().expect("mean should succeed") - 20.0).abs() < 1e-9);
1077 assert_eq!(ss.min().expect("min should succeed") as i64, 10);
1078 assert_eq!(ss.max().expect("max should succeed") as i64, 30);
1079 }
1080 #[test]
1081 fn test_transform_stat() {
1082 let mut ts = TransformStat::new();
1083 ts.record_before(100.0);
1084 ts.record_after(80.0);
1085 let ratio = ts.mean_ratio().expect("ratio should be present");
1086 assert!((ratio - 0.8).abs() < 1e-9);
1087 }
1088 #[test]
1089 fn test_small_map() {
1090 let mut m: SmallMap<u32, &str> = SmallMap::new();
1091 m.insert(3, "three");
1092 m.insert(1, "one");
1093 m.insert(2, "two");
1094 assert_eq!(m.get(&2), Some(&"two"));
1095 assert_eq!(m.len(), 3);
1096 let keys = m.keys();
1097 assert_eq!(*keys[0], 1);
1098 assert_eq!(*keys[2], 3);
1099 }
1100 #[test]
1101 fn test_label_set() {
1102 let mut ls = LabelSet::new();
1103 ls.add("foo");
1104 ls.add("bar");
1105 ls.add("foo");
1106 assert_eq!(ls.count(), 2);
1107 assert!(ls.has("bar"));
1108 assert!(!ls.has("baz"));
1109 }
1110 #[test]
1111 fn test_config_node() {
1112 let mut root = ConfigNode::section("root");
1113 let child = ConfigNode::leaf("key", "value");
1114 root.add_child(child);
1115 assert_eq!(root.num_children(), 1);
1116 }
1117 #[test]
1118 fn test_versioned_record() {
1119 let mut vr = VersionedRecord::new(0u32);
1120 vr.update(1);
1121 vr.update(2);
1122 assert_eq!(*vr.current(), 2);
1123 assert_eq!(vr.version(), 2);
1124 assert!(vr.has_history());
1125 assert_eq!(*vr.at_version(0).expect("value should be present"), 0);
1126 }
1127 #[test]
1128 fn test_simple_dag() {
1129 let mut dag = SimpleDag::new(4);
1130 dag.add_edge(0, 1);
1131 dag.add_edge(1, 2);
1132 dag.add_edge(2, 3);
1133 assert!(dag.can_reach(0, 3));
1134 assert!(!dag.can_reach(3, 0));
1135 let order = dag.topological_sort().expect("order should be present");
1136 assert_eq!(order, vec![0, 1, 2, 3]);
1137 }
1138 #[test]
1139 fn test_focus_stack() {
1140 let mut fs: FocusStack<&str> = FocusStack::new();
1141 fs.focus("a");
1142 fs.focus("b");
1143 assert_eq!(fs.current(), Some(&"b"));
1144 assert_eq!(fs.depth(), 2);
1145 fs.blur();
1146 assert_eq!(fs.current(), Some(&"a"));
1147 }
1148}
1149#[cfg(test)]
1150mod tests_extra_iterators {
1151 use super::*;
1152 #[test]
1153 fn test_window_iterator() {
1154 let data = vec![1u32, 2, 3, 4, 5];
1155 let windows: Vec<_> = WindowIterator::new(&data, 3).collect();
1156 assert_eq!(windows.len(), 3);
1157 assert_eq!(windows[0], &[1, 2, 3]);
1158 assert_eq!(windows[2], &[3, 4, 5]);
1159 }
1160 #[test]
1161 fn test_non_empty_vec() {
1162 let mut nev = NonEmptyVec::singleton(10u32);
1163 nev.push(20);
1164 nev.push(30);
1165 assert_eq!(nev.len(), 3);
1166 assert_eq!(*nev.first(), 10);
1167 assert_eq!(*nev.last(), 30);
1168 }
1169}
1170#[cfg(test)]
1171mod tests_padding2 {
1172 use super::*;
1173 #[test]
1174 fn test_sliding_sum() {
1175 let mut ss = SlidingSum::new(3);
1176 ss.push(1.0);
1177 ss.push(2.0);
1178 ss.push(3.0);
1179 assert!((ss.sum() - 6.0).abs() < 1e-9);
1180 ss.push(4.0);
1181 assert!((ss.sum() - 9.0).abs() < 1e-9);
1182 assert_eq!(ss.count(), 3);
1183 }
1184 #[test]
1185 fn test_path_buf() {
1186 let mut pb = PathBuf::new();
1187 pb.push("src");
1188 pb.push("main");
1189 assert_eq!(pb.as_str(), "src/main");
1190 assert_eq!(pb.depth(), 2);
1191 pb.pop();
1192 assert_eq!(pb.as_str(), "src");
1193 }
1194 #[test]
1195 fn test_string_pool() {
1196 let mut pool = StringPool::new();
1197 let s = pool.take();
1198 assert!(s.is_empty());
1199 pool.give("hello".to_string());
1200 let s2 = pool.take();
1201 assert!(s2.is_empty());
1202 assert_eq!(pool.free_count(), 0);
1203 }
1204 #[test]
1205 fn test_transitive_closure() {
1206 let mut tc = TransitiveClosure::new(4);
1207 tc.add_edge(0, 1);
1208 tc.add_edge(1, 2);
1209 tc.add_edge(2, 3);
1210 assert!(tc.can_reach(0, 3));
1211 assert!(!tc.can_reach(3, 0));
1212 let r = tc.reachable_from(0);
1213 assert_eq!(r.len(), 4);
1214 }
1215 #[test]
1216 fn test_token_bucket() {
1217 let mut tb = TokenBucket::new(100, 10);
1218 assert_eq!(tb.available(), 100);
1219 assert!(tb.try_consume(50));
1220 assert_eq!(tb.available(), 50);
1221 assert!(!tb.try_consume(60));
1222 assert_eq!(tb.capacity(), 100);
1223 }
1224 #[test]
1225 fn test_rewrite_rule_set() {
1226 let mut rrs = RewriteRuleSet::new();
1227 rrs.add(RewriteRule::unconditional(
1228 "beta",
1229 "App(Lam(x, b), v)",
1230 "b[x:=v]",
1231 ));
1232 rrs.add(RewriteRule::conditional("comm", "a + b", "b + a"));
1233 assert_eq!(rrs.len(), 2);
1234 assert_eq!(rrs.unconditional_rules().len(), 1);
1235 assert_eq!(rrs.conditional_rules().len(), 1);
1236 assert!(rrs.get("beta").is_some());
1237 let disp = rrs
1238 .get("beta")
1239 .expect("element at \'beta\' should exist")
1240 .display();
1241 assert!(disp.contains("→"));
1242 }
1243}
1244#[cfg(test)]
1245mod tests_padding3 {
1246 use super::*;
1247 #[test]
1248 fn test_decision_node() {
1249 let tree = DecisionNode::Branch {
1250 key: "x".into(),
1251 val: "1".into(),
1252 yes_branch: Box::new(DecisionNode::Leaf("yes".into())),
1253 no_branch: Box::new(DecisionNode::Leaf("no".into())),
1254 };
1255 let mut ctx = std::collections::HashMap::new();
1256 ctx.insert("x".into(), "1".into());
1257 assert_eq!(tree.evaluate(&ctx), "yes");
1258 ctx.insert("x".into(), "2".into());
1259 assert_eq!(tree.evaluate(&ctx), "no");
1260 assert_eq!(tree.depth(), 1);
1261 }
1262 #[test]
1263 fn test_flat_substitution() {
1264 let mut sub = FlatSubstitution::new();
1265 sub.add("foo", "bar");
1266 sub.add("baz", "qux");
1267 assert_eq!(sub.apply("foo and baz"), "bar and qux");
1268 assert_eq!(sub.len(), 2);
1269 }
1270 #[test]
1271 fn test_stopwatch() {
1272 let mut sw = Stopwatch::start();
1273 sw.split();
1274 sw.split();
1275 assert_eq!(sw.num_splits(), 2);
1276 assert!(sw.elapsed_ms() >= 0.0);
1277 for &s in sw.splits() {
1278 assert!(s >= 0.0);
1279 }
1280 }
1281 #[test]
1282 fn test_either2() {
1283 let e: Either2<i32, &str> = Either2::First(42);
1284 assert!(e.is_first());
1285 let mapped = e.map_first(|x| x * 2);
1286 assert_eq!(mapped.first(), Some(84));
1287 let e2: Either2<i32, &str> = Either2::Second("hello");
1288 assert!(e2.is_second());
1289 assert_eq!(e2.second(), Some("hello"));
1290 }
1291 #[test]
1292 fn test_write_once() {
1293 let wo: WriteOnce<u32> = WriteOnce::new();
1294 assert!(!wo.is_written());
1295 assert!(wo.write(42));
1296 assert!(!wo.write(99));
1297 assert_eq!(wo.read(), Some(42));
1298 }
1299 #[test]
1300 fn test_sparse_vec() {
1301 let mut sv: SparseVec<i32> = SparseVec::new(100);
1302 sv.set(5, 10);
1303 sv.set(50, 20);
1304 assert_eq!(*sv.get(5), 10);
1305 assert_eq!(*sv.get(50), 20);
1306 assert_eq!(*sv.get(0), 0);
1307 assert_eq!(sv.nnz(), 2);
1308 sv.set(5, 0);
1309 assert_eq!(sv.nnz(), 1);
1310 }
1311 #[test]
1312 fn test_stack_calc() {
1313 let mut calc = StackCalc::new();
1314 calc.push(3);
1315 calc.push(4);
1316 calc.add();
1317 assert_eq!(calc.peek(), Some(7));
1318 calc.push(2);
1319 calc.mul();
1320 assert_eq!(calc.peek(), Some(14));
1321 }
1322}