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) if *n >= depth => {
447 result.push(*n - depth);
448 }
449 Expr::BVar(_) => {}
450 Expr::App(f, a) => {
451 collect_loose_bvar_impl(f, depth, result);
452 collect_loose_bvar_impl(a, depth, result);
453 }
454 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
455 collect_loose_bvar_impl(ty, depth, result);
456 collect_loose_bvar_impl(body, depth + 1, result);
457 }
458 Expr::Let(_, ty, val, body) => {
459 collect_loose_bvar_impl(ty, depth, result);
460 collect_loose_bvar_impl(val, depth, result);
461 collect_loose_bvar_impl(body, depth + 1, result);
462 }
463 Expr::Proj(_, _, e) => collect_loose_bvar_impl(e, depth, result),
464 _ => {}
465 }
466}
467pub fn parallel_subst_tracked(expr: &Expr, map: &SubstMap, stats: &mut SubstStats) -> Expr {
469 if map.is_empty() {
470 return expr.clone();
471 }
472 parallel_subst_tracked_impl(expr, map, stats)
473}
474fn parallel_subst_tracked_impl(expr: &Expr, map: &SubstMap, stats: &mut SubstStats) -> Expr {
475 stats.nodes_visited += 1;
476 match expr {
477 Expr::FVar(id) => {
478 if let Some(r) = map.get(id) {
479 stats.fvar_hits += 1;
480 r.clone()
481 } else {
482 expr.clone()
483 }
484 }
485 Expr::BVar(_) | Expr::Sort(_) | Expr::Const(_, _) | Expr::Lit(_) => expr.clone(),
486 Expr::App(f, a) => Expr::App(
487 Box::new(parallel_subst_tracked_impl(f, map, stats)),
488 Box::new(parallel_subst_tracked_impl(a, map, stats)),
489 ),
490 Expr::Lam(bi, name, ty, body) => Expr::Lam(
491 *bi,
492 name.clone(),
493 Box::new(parallel_subst_tracked_impl(ty, map, stats)),
494 Box::new(parallel_subst_tracked_impl(body, map, stats)),
495 ),
496 Expr::Pi(bi, name, ty, body) => Expr::Pi(
497 *bi,
498 name.clone(),
499 Box::new(parallel_subst_tracked_impl(ty, map, stats)),
500 Box::new(parallel_subst_tracked_impl(body, map, stats)),
501 ),
502 Expr::Let(name, ty, val, body) => Expr::Let(
503 name.clone(),
504 Box::new(parallel_subst_tracked_impl(ty, map, stats)),
505 Box::new(parallel_subst_tracked_impl(val, map, stats)),
506 Box::new(parallel_subst_tracked_impl(body, map, stats)),
507 ),
508 Expr::Proj(name, idx, e) => Expr::Proj(
509 name.clone(),
510 *idx,
511 Box::new(parallel_subst_tracked_impl(e, map, stats)),
512 ),
513 }
514}
515#[cfg(test)]
516mod extended_tests {
517 use super::*;
518 use crate::{BinderInfo, Level, Literal, Name};
519 fn fvar(id: u64) -> Expr {
520 Expr::FVar(FVarId(id))
521 }
522 fn lit(n: u64) -> Expr {
523 Expr::Lit(Literal::Nat(n))
524 }
525 fn sort0() -> Expr {
526 Expr::Sort(Level::zero())
527 }
528 #[test]
529 fn test_instantiate_many_two() {
530 let app = Expr::App(Box::new(Expr::BVar(0)), Box::new(Expr::BVar(1)));
531 let result = instantiate_many(&app, &[lit(10), lit(20)]);
532 match result {
533 Expr::App(f, a) => {
534 assert_eq!(*f, lit(10));
535 assert_eq!(*a, lit(20));
536 }
537 _ => panic!("Expected App"),
538 }
539 }
540 #[test]
541 fn test_parallel_subst_single() {
542 let mut map = SubstMap::new();
543 map.insert(FVarId(1), lit(99));
544 let expr = fvar(1);
545 assert_eq!(parallel_subst(&expr, &map), lit(99));
546 }
547 #[test]
548 fn test_parallel_subst_empty() {
549 let expr = fvar(5);
550 let map = SubstMap::new();
551 assert_eq!(parallel_subst(&expr, &map), expr);
552 }
553 #[test]
554 fn test_shift_bvars_above_cutoff() {
555 let e = Expr::BVar(2);
556 let shifted = shift_bvars(&e, 3, 1);
557 assert_eq!(shifted, Expr::BVar(5));
558 }
559 #[test]
560 fn test_shift_bvars_below_cutoff() {
561 let e = Expr::BVar(0);
562 let shifted = shift_bvars(&e, 2, 1);
563 assert_eq!(shifted, Expr::BVar(0));
564 }
565 #[test]
566 fn test_free_vars_single() {
567 let set = free_vars(&fvar(7));
568 assert_eq!(set.len(), 1);
569 assert!(set.contains(&FVarId(7)));
570 }
571 #[test]
572 fn test_occurs_free_true() {
573 let expr = Expr::App(Box::new(fvar(1)), Box::new(fvar(2)));
574 assert!(occurs_free(&expr, FVarId(1)));
575 assert!(!occurs_free(&expr, FVarId(3)));
576 }
577 #[test]
578 fn test_count_free_occurrences() {
579 let expr = Expr::App(Box::new(fvar(1)), Box::new(fvar(1)));
580 assert_eq!(count_free_occurrences(&expr, FVarId(1)), 2);
581 }
582 #[test]
583 fn test_subst_stats_merge() {
584 let mut a = SubstStats {
585 bvar_hits: 3,
586 bvar_misses: 1,
587 fvar_hits: 2,
588 nodes_visited: 10,
589 };
590 let b = SubstStats {
591 bvar_hits: 1,
592 bvar_misses: 0,
593 fvar_hits: 1,
594 nodes_visited: 5,
595 };
596 a.merge(&b);
597 assert_eq!(a.bvar_hits, 4);
598 assert_eq!(a.total_substs(), 7);
599 }
600 #[test]
601 fn test_instantiate_tracked() {
602 let body = Expr::BVar(0);
603 let arg = lit(42);
604 let mut stats = SubstStats::default();
605 let result = instantiate_tracked(&body, &arg, &mut stats);
606 assert_eq!(result, lit(42));
607 assert_eq!(stats.bvar_hits, 1);
608 }
609 #[test]
610 fn test_build_subst_map() {
611 let fvars = vec![FVarId(1), FVarId(2)];
612 let replacements = vec![lit(10), lit(20)];
613 let map = build_subst_map(&fvars, &replacements);
614 assert_eq!(map.get(&FVarId(1)), Some(&lit(10)));
615 }
616 #[test]
617 fn test_try_beta_reduce_success() {
618 let lam = Expr::Lam(
619 BinderInfo::Default,
620 Name::str("x"),
621 Box::new(sort0()),
622 Box::new(Expr::BVar(0)),
623 );
624 let app = Expr::App(Box::new(lam), Box::new(lit(42)));
625 let result = try_beta_reduce(&app).expect("result should be present");
626 assert_eq!(result, lit(42));
627 }
628 #[test]
629 fn test_try_beta_reduce_fail() {
630 let app = Expr::App(Box::new(fvar(1)), Box::new(lit(42)));
631 assert!(try_beta_reduce(&app).is_none());
632 }
633 #[test]
634 fn test_beta_reduce_head() {
635 let lam = Expr::Lam(
636 BinderInfo::Default,
637 Name::str("x"),
638 Box::new(sort0()),
639 Box::new(Expr::BVar(0)),
640 );
641 let app = Expr::App(Box::new(lam), Box::new(lit(7)));
642 let result = beta_reduce_head(app);
643 assert_eq!(result, lit(7));
644 }
645 #[test]
646 fn test_subst_fvar() {
647 let expr = fvar(1);
648 let result = subst_fvar(&expr, FVarId(1), &lit(99));
649 assert_eq!(result, lit(99));
650 }
651 #[test]
652 fn test_is_whnf_beta_lam() {
653 let lam = Expr::Lam(
654 BinderInfo::Default,
655 Name::str("x"),
656 Box::new(sort0()),
657 Box::new(Expr::BVar(0)),
658 );
659 assert!(is_whnf_beta(&lam));
660 }
661 #[test]
662 fn test_is_whnf_beta_app_not_whnf() {
663 let lam = Expr::Lam(
664 BinderInfo::Default,
665 Name::str("x"),
666 Box::new(sort0()),
667 Box::new(Expr::BVar(0)),
668 );
669 let app = Expr::App(Box::new(lam), Box::new(lit(1)));
670 assert!(!is_whnf_beta(&app));
671 }
672 #[test]
673 fn test_collect_loose_bvar_indices() {
674 let e = Expr::App(Box::new(Expr::BVar(0)), Box::new(Expr::BVar(2)));
675 let indices = collect_loose_bvar_indices(&e);
676 assert!(indices.contains(&0));
677 assert!(indices.contains(&2));
678 }
679 #[test]
680 fn test_parallel_subst_tracked() {
681 let expr = Expr::App(Box::new(fvar(1)), Box::new(fvar(2)));
682 let mut map = SubstMap::new();
683 map.insert(FVarId(1), lit(10));
684 map.insert(FVarId(2), lit(20));
685 let mut stats = SubstStats::default();
686 let result = parallel_subst_tracked(&expr, &map, &mut stats);
687 assert_eq!(stats.fvar_hits, 2);
688 match result {
689 Expr::App(f, a) => {
690 assert_eq!(*f, lit(10));
691 assert_eq!(*a, lit(20));
692 }
693 _ => panic!("Expected App"),
694 }
695 }
696}
697pub fn apply_args(f: &Expr, args: &[Expr]) -> Expr {
702 let mut result = f.clone();
703 for arg in args {
704 if let Expr::Lam(_, _, _, body) = result {
705 result = instantiate(&body, arg);
706 } else {
707 result = Expr::App(Box::new(result), Box::new(arg.clone()));
708 }
709 }
710 result
711}
712#[inline]
714pub fn is_lambda(e: &Expr) -> bool {
715 matches!(e, Expr::Lam(_, _, _, _))
716}
717#[allow(clippy::type_complexity)]
722pub fn peel_lambdas(
723 e: &Expr,
724 n: usize,
725) -> (Vec<(crate::Name, crate::BinderInfo, Box<Expr>)>, &Expr) {
726 let mut binders = Vec::new();
727 let mut cur = e;
728 for _ in 0..n {
729 if let Expr::Lam(bi, _, ty, body) = cur {
730 binders.push((crate::Name::Anonymous, *bi, ty.clone()));
731 cur = body;
732 } else {
733 break;
734 }
735 }
736 (binders, cur)
737}
738pub fn count_lambdas(e: &Expr) -> usize {
740 let mut n = 0;
741 let mut cur = e;
742 while let Expr::Lam(_, _, _, body) = cur {
743 n += 1;
744 cur = body;
745 }
746 n
747}
748pub fn count_pis(e: &Expr) -> usize {
750 let mut n = 0;
751 let mut cur = e;
752 while let Expr::Pi(_, _, _, body) = cur {
753 n += 1;
754 cur = body;
755 }
756 n
757}
758#[cfg(test)]
759mod extended2_tests {
760 use super::*;
761 use crate::{BinderInfo, Expr, FVarId, Literal, Name};
762 fn lit(n: u64) -> Expr {
763 Expr::Lit(Literal::Nat(n))
764 }
765 fn fvar(id: u64) -> Expr {
766 Expr::FVar(FVarId(id))
767 }
768 #[test]
769 fn test_apply_args_lam() {
770 let body = Expr::BVar(0);
771 let lam = Expr::Lam(
772 BinderInfo::Default,
773 Name::str("_"),
774 Box::new(lit(0)),
775 Box::new(body),
776 );
777 let result = apply_args(&lam, &[lit(42)]);
778 assert_eq!(result, lit(42));
779 }
780 #[test]
781 fn test_apply_args_non_lam() {
782 let f = fvar(1);
783 let result = apply_args(&f, &[lit(1), lit(2)]);
784 match &result {
785 Expr::App(outer, arg2) => {
786 assert_eq!(**arg2, lit(2));
787 match outer.as_ref() {
788 Expr::App(inner_f, arg1) => {
789 assert_eq!(**inner_f, fvar(1));
790 assert_eq!(**arg1, lit(1));
791 }
792 _ => panic!("Expected App"),
793 }
794 }
795 _ => panic!("Expected App"),
796 }
797 }
798 #[test]
799 fn test_is_lambda() {
800 let lam = Expr::Lam(
801 BinderInfo::Default,
802 Name::str("_"),
803 Box::new(lit(0)),
804 Box::new(lit(1)),
805 );
806 assert!(is_lambda(&lam));
807 assert!(!is_lambda(&lit(0)));
808 }
809 #[test]
810 fn test_count_lambdas() {
811 let inner = lit(0);
812 let lam1 = Expr::Lam(
813 BinderInfo::Default,
814 Name::str("_"),
815 Box::new(lit(0)),
816 Box::new(inner),
817 );
818 let lam2 = Expr::Lam(
819 BinderInfo::Default,
820 Name::str("_"),
821 Box::new(lit(0)),
822 Box::new(lam1),
823 );
824 assert_eq!(count_lambdas(&lam2), 2);
825 assert_eq!(count_lambdas(&lit(0)), 0);
826 }
827 #[test]
828 fn test_count_pis() {
829 let inner = lit(0);
830 let pi1 = Expr::Pi(
831 BinderInfo::Default,
832 Name::str("_"),
833 Box::new(lit(0)),
834 Box::new(inner),
835 );
836 let pi2 = Expr::Pi(
837 BinderInfo::Default,
838 Name::str("_"),
839 Box::new(lit(0)),
840 Box::new(pi1),
841 );
842 assert_eq!(count_pis(&pi2), 2);
843 assert_eq!(count_pis(&lit(0)), 0);
844 }
845 #[test]
846 fn test_peel_lambdas_two() {
847 let body = Expr::BVar(0);
848 let lam1 = Expr::Lam(
849 BinderInfo::Default,
850 Name::str("_"),
851 Box::new(lit(0)),
852 Box::new(body),
853 );
854 let lam2 = Expr::Lam(
855 BinderInfo::Implicit,
856 Name::str("_"),
857 Box::new(lit(0)),
858 Box::new(lam1),
859 );
860 let (binders, inner) = peel_lambdas(&lam2, 2);
861 assert_eq!(binders.len(), 2);
862 assert_eq!(binders[0].1, BinderInfo::Implicit);
863 assert_eq!(binders[1].1, BinderInfo::Default);
864 assert_eq!(*inner, Expr::BVar(0));
865 }
866 #[test]
867 fn test_apply_args_empty() {
868 let e = lit(5);
869 let result = apply_args(&e, &[]);
870 assert_eq!(result, e);
871 }
872}
873#[allow(dead_code)]
875pub fn substitute_fvars(expr: &Expr, fvars: &[FVarId], replacements: &[Expr]) -> Expr {
876 assert_eq!(fvars.len(), replacements.len());
877 let mut subst = Substitution::new();
878 for (fvar, rep) in fvars.iter().zip(replacements.iter()) {
879 subst.insert(*fvar, rep.clone());
880 }
881 subst.apply(expr)
882}
883#[allow(dead_code)]
885pub fn expr_contains_fvar(expr: &Expr, fvars: &[FVarId]) -> bool {
886 match expr {
887 Expr::FVar(id) => fvars.contains(id),
888 Expr::App(f, a) => expr_contains_fvar(f, fvars) || expr_contains_fvar(a, fvars),
889 Expr::Lam(_, _, ty, body) | Expr::Pi(_, _, ty, body) => {
890 expr_contains_fvar(ty, fvars) || expr_contains_fvar(body, fvars)
891 }
892 Expr::Let(_, ty, val, body) => {
893 expr_contains_fvar(ty, fvars)
894 || expr_contains_fvar(val, fvars)
895 || expr_contains_fvar(body, fvars)
896 }
897 Expr::Proj(_, _, inner) => expr_contains_fvar(inner, fvars),
898 _ => false,
899 }
900}
901#[allow(dead_code)]
905pub fn open_binder(body: &Expr, id: FVarId) -> Expr {
906 open_binder_at(body, &Expr::FVar(id), 0)
907}
908fn open_binder_at(expr: &Expr, fvar_expr: &Expr, depth: u32) -> Expr {
909 match expr {
910 Expr::BVar(n) => {
911 if *n == depth {
912 fvar_expr.clone()
913 } else {
914 expr.clone()
915 }
916 }
917 Expr::App(f, a) => {
918 let f2 = open_binder_at(f, fvar_expr, depth);
919 let a2 = open_binder_at(a, fvar_expr, depth);
920 Expr::App(Box::new(f2), Box::new(a2))
921 }
922 Expr::Lam(bi, name, ty, body) => {
923 let ty2 = open_binder_at(ty, fvar_expr, depth);
924 let body2 = open_binder_at(body, fvar_expr, depth + 1);
925 Expr::Lam(*bi, name.clone(), Box::new(ty2), Box::new(body2))
926 }
927 Expr::Pi(bi, name, ty, body) => {
928 let ty2 = open_binder_at(ty, fvar_expr, depth);
929 let body2 = open_binder_at(body, fvar_expr, depth + 1);
930 Expr::Pi(*bi, name.clone(), Box::new(ty2), Box::new(body2))
931 }
932 Expr::Let(name, ty, val, body) => {
933 let ty2 = open_binder_at(ty, fvar_expr, depth);
934 let val2 = open_binder_at(val, fvar_expr, depth);
935 let body2 = open_binder_at(body, fvar_expr, depth + 1);
936 Expr::Let(name.clone(), Box::new(ty2), Box::new(val2), Box::new(body2))
937 }
938 Expr::Proj(name, idx, inner) => {
939 let inner2 = open_binder_at(inner, fvar_expr, depth);
940 Expr::Proj(name.clone(), *idx, Box::new(inner2))
941 }
942 _ => expr.clone(),
943 }
944}
945#[cfg(test)]
946mod extended3_subst_tests {
947 use super::*;
948 use crate::{BinderInfo, Expr, FVarId, Literal, Name};
949 fn lit(n: u64) -> Expr {
950 Expr::Lit(Literal::Nat(n))
951 }
952 fn fvar(id: u64) -> Expr {
953 Expr::FVar(FVarId(id))
954 }
955 #[test]
956 fn test_substitution_insert_and_get() {
957 let mut s = Substitution::new();
958 s.insert(FVarId(0), lit(42));
959 assert_eq!(s.get(FVarId(0)), Some(&lit(42)));
960 assert_eq!(s.get(FVarId(1)), None);
961 }
962 #[test]
963 fn test_substitution_len() {
964 let mut s = Substitution::new();
965 assert_eq!(s.len(), 0);
966 s.insert(FVarId(0), lit(1));
967 assert_eq!(s.len(), 1);
968 }
969 #[test]
970 fn test_substitution_apply_fvar() {
971 let mut s = Substitution::new();
972 s.insert(FVarId(1), lit(99));
973 let expr = fvar(1);
974 assert_eq!(s.apply(&expr), lit(99));
975 }
976 #[test]
977 fn test_substitution_apply_no_match() {
978 let s = Substitution::new();
979 let expr = fvar(5);
980 assert_eq!(s.apply(&expr), fvar(5));
981 }
982 #[test]
983 fn test_substitution_remove() {
984 let mut s = Substitution::new();
985 s.insert(FVarId(0), lit(1));
986 s.remove(FVarId(0));
987 assert_eq!(s.len(), 0);
988 }
989 #[test]
990 fn test_substitution_restrict() {
991 let mut s = Substitution::new();
992 s.insert(FVarId(0), lit(1));
993 s.insert(FVarId(1), lit(2));
994 let r = s.restrict(&[FVarId(0)]);
995 assert_eq!(r.len(), 1);
996 assert!(r.get(FVarId(0)).is_some());
997 assert!(r.get(FVarId(1)).is_none());
998 }
999 #[test]
1000 fn test_substitute_fvars_parallel() {
1001 let expr = Expr::App(Box::new(fvar(0)), Box::new(fvar(1)));
1002 let result = substitute_fvars(&expr, &[FVarId(0), FVarId(1)], &[lit(10), lit(20)]);
1003 match result {
1004 Expr::App(f, a) => {
1005 assert_eq!(*f, lit(10));
1006 assert_eq!(*a, lit(20));
1007 }
1008 _ => panic!("Expected App"),
1009 }
1010 }
1011 #[test]
1012 fn test_expr_contains_fvar_true() {
1013 let expr = Expr::App(Box::new(fvar(3)), Box::new(lit(0)));
1014 assert!(expr_contains_fvar(&expr, &[FVarId(3)]));
1015 }
1016 #[test]
1017 fn test_expr_contains_fvar_false() {
1018 let expr = lit(5);
1019 assert!(!expr_contains_fvar(&expr, &[FVarId(0)]));
1020 }
1021 #[test]
1022 fn test_open_binder_simple() {
1023 let body = Expr::BVar(0);
1024 let result = open_binder(&body, FVarId(7));
1025 assert_eq!(result, fvar(7));
1026 }
1027 #[test]
1028 fn test_open_binder_nested() {
1029 let inner_body = Expr::BVar(0);
1030 let inner_lam = Expr::Lam(
1031 BinderInfo::Default,
1032 Name::str("_"),
1033 Box::new(Expr::Const(Name::str("Nat"), vec![])),
1034 Box::new(inner_body),
1035 );
1036 let outer_body = Expr::App(Box::new(inner_lam), Box::new(Expr::BVar(0)));
1037 let opened = open_binder(&outer_body, FVarId(99));
1038 match opened {
1039 Expr::App(f, arg) => {
1040 assert!(matches!(*f, Expr::Lam(_, _, _, _)));
1041 assert_eq!(*arg, fvar(99));
1042 }
1043 _ => panic!("Expected App"),
1044 }
1045 }
1046 #[test]
1047 fn test_substitution_compose() {
1048 let mut s1 = Substitution::new();
1049 s1.insert(FVarId(0), lit(10));
1050 let mut s2 = Substitution::new();
1051 s2.insert(FVarId(1), lit(20));
1052 let composed = s1.compose(&s2);
1053 assert_eq!(composed.get(FVarId(0)), Some(&lit(10)));
1054 assert_eq!(composed.get(FVarId(1)), Some(&lit(20)));
1055 }
1056 #[test]
1057 fn test_substitution_insert_replace() {
1058 let mut s = Substitution::new();
1059 s.insert(FVarId(0), lit(1));
1060 s.insert(FVarId(0), lit(2));
1061 assert_eq!(s.len(), 1);
1062 assert_eq!(s.get(FVarId(0)), Some(&lit(2)));
1063 }
1064}
1065#[cfg(test)]
1066mod tests_padding_infra {
1067 use super::*;
1068 #[test]
1069 fn test_stat_summary() {
1070 let mut ss = StatSummary::new();
1071 ss.record(10.0);
1072 ss.record(20.0);
1073 ss.record(30.0);
1074 assert_eq!(ss.count(), 3);
1075 assert!((ss.mean().expect("mean should succeed") - 20.0).abs() < 1e-9);
1076 assert_eq!(ss.min().expect("min should succeed") as i64, 10);
1077 assert_eq!(ss.max().expect("max should succeed") as i64, 30);
1078 }
1079 #[test]
1080 fn test_transform_stat() {
1081 let mut ts = TransformStat::new();
1082 ts.record_before(100.0);
1083 ts.record_after(80.0);
1084 let ratio = ts.mean_ratio().expect("ratio should be present");
1085 assert!((ratio - 0.8).abs() < 1e-9);
1086 }
1087 #[test]
1088 fn test_small_map() {
1089 let mut m: SmallMap<u32, &str> = SmallMap::new();
1090 m.insert(3, "three");
1091 m.insert(1, "one");
1092 m.insert(2, "two");
1093 assert_eq!(m.get(&2), Some(&"two"));
1094 assert_eq!(m.len(), 3);
1095 let keys = m.keys();
1096 assert_eq!(*keys[0], 1);
1097 assert_eq!(*keys[2], 3);
1098 }
1099 #[test]
1100 fn test_label_set() {
1101 let mut ls = LabelSet::new();
1102 ls.add("foo");
1103 ls.add("bar");
1104 ls.add("foo");
1105 assert_eq!(ls.count(), 2);
1106 assert!(ls.has("bar"));
1107 assert!(!ls.has("baz"));
1108 }
1109 #[test]
1110 fn test_config_node() {
1111 let mut root = ConfigNode::section("root");
1112 let child = ConfigNode::leaf("key", "value");
1113 root.add_child(child);
1114 assert_eq!(root.num_children(), 1);
1115 }
1116 #[test]
1117 fn test_versioned_record() {
1118 let mut vr = VersionedRecord::new(0u32);
1119 vr.update(1);
1120 vr.update(2);
1121 assert_eq!(*vr.current(), 2);
1122 assert_eq!(vr.version(), 2);
1123 assert!(vr.has_history());
1124 assert_eq!(*vr.at_version(0).expect("value should be present"), 0);
1125 }
1126 #[test]
1127 fn test_simple_dag() {
1128 let mut dag = SimpleDag::new(4);
1129 dag.add_edge(0, 1);
1130 dag.add_edge(1, 2);
1131 dag.add_edge(2, 3);
1132 assert!(dag.can_reach(0, 3));
1133 assert!(!dag.can_reach(3, 0));
1134 let order = dag.topological_sort().expect("order should be present");
1135 assert_eq!(order, vec![0, 1, 2, 3]);
1136 }
1137 #[test]
1138 fn test_focus_stack() {
1139 let mut fs: FocusStack<&str> = FocusStack::new();
1140 fs.focus("a");
1141 fs.focus("b");
1142 assert_eq!(fs.current(), Some(&"b"));
1143 assert_eq!(fs.depth(), 2);
1144 fs.blur();
1145 assert_eq!(fs.current(), Some(&"a"));
1146 }
1147}
1148#[cfg(test)]
1149mod tests_extra_iterators {
1150 use super::*;
1151 #[test]
1152 fn test_window_iterator() {
1153 let data = vec![1u32, 2, 3, 4, 5];
1154 let windows: Vec<_> = WindowIterator::new(&data, 3).collect();
1155 assert_eq!(windows.len(), 3);
1156 assert_eq!(windows[0], &[1, 2, 3]);
1157 assert_eq!(windows[2], &[3, 4, 5]);
1158 }
1159 #[test]
1160 fn test_non_empty_vec() {
1161 let mut nev = NonEmptyVec::singleton(10u32);
1162 nev.push(20);
1163 nev.push(30);
1164 assert_eq!(nev.len(), 3);
1165 assert_eq!(*nev.first(), 10);
1166 assert_eq!(*nev.last(), 30);
1167 }
1168}
1169#[cfg(test)]
1170mod tests_padding2 {
1171 use super::*;
1172 #[test]
1173 fn test_sliding_sum() {
1174 let mut ss = SlidingSum::new(3);
1175 ss.push(1.0);
1176 ss.push(2.0);
1177 ss.push(3.0);
1178 assert!((ss.sum() - 6.0).abs() < 1e-9);
1179 ss.push(4.0);
1180 assert!((ss.sum() - 9.0).abs() < 1e-9);
1181 assert_eq!(ss.count(), 3);
1182 }
1183 #[test]
1184 fn test_path_buf() {
1185 let mut pb = PathBuf::new();
1186 pb.push("src");
1187 pb.push("main");
1188 assert_eq!(pb.as_str(), "src/main");
1189 assert_eq!(pb.depth(), 2);
1190 pb.pop();
1191 assert_eq!(pb.as_str(), "src");
1192 }
1193 #[test]
1194 fn test_string_pool() {
1195 let mut pool = StringPool::new();
1196 let s = pool.take();
1197 assert!(s.is_empty());
1198 pool.give("hello".to_string());
1199 let s2 = pool.take();
1200 assert!(s2.is_empty());
1201 assert_eq!(pool.free_count(), 0);
1202 }
1203 #[test]
1204 fn test_transitive_closure() {
1205 let mut tc = TransitiveClosure::new(4);
1206 tc.add_edge(0, 1);
1207 tc.add_edge(1, 2);
1208 tc.add_edge(2, 3);
1209 assert!(tc.can_reach(0, 3));
1210 assert!(!tc.can_reach(3, 0));
1211 let r = tc.reachable_from(0);
1212 assert_eq!(r.len(), 4);
1213 }
1214 #[test]
1215 fn test_token_bucket() {
1216 let mut tb = TokenBucket::new(100, 10);
1217 assert_eq!(tb.available(), 100);
1218 assert!(tb.try_consume(50));
1219 assert_eq!(tb.available(), 50);
1220 assert!(!tb.try_consume(60));
1221 assert_eq!(tb.capacity(), 100);
1222 }
1223 #[test]
1224 fn test_rewrite_rule_set() {
1225 let mut rrs = RewriteRuleSet::new();
1226 rrs.add(RewriteRule::unconditional(
1227 "beta",
1228 "App(Lam(x, b), v)",
1229 "b[x:=v]",
1230 ));
1231 rrs.add(RewriteRule::conditional("comm", "a + b", "b + a"));
1232 assert_eq!(rrs.len(), 2);
1233 assert_eq!(rrs.unconditional_rules().len(), 1);
1234 assert_eq!(rrs.conditional_rules().len(), 1);
1235 assert!(rrs.get("beta").is_some());
1236 let disp = rrs
1237 .get("beta")
1238 .expect("element at \'beta\' should exist")
1239 .display();
1240 assert!(disp.contains("→"));
1241 }
1242}
1243#[cfg(test)]
1244mod tests_padding3 {
1245 use super::*;
1246 #[test]
1247 fn test_decision_node() {
1248 let tree = DecisionNode::Branch {
1249 key: "x".into(),
1250 val: "1".into(),
1251 yes_branch: Box::new(DecisionNode::Leaf("yes".into())),
1252 no_branch: Box::new(DecisionNode::Leaf("no".into())),
1253 };
1254 let mut ctx = std::collections::HashMap::new();
1255 ctx.insert("x".into(), "1".into());
1256 assert_eq!(tree.evaluate(&ctx), "yes");
1257 ctx.insert("x".into(), "2".into());
1258 assert_eq!(tree.evaluate(&ctx), "no");
1259 assert_eq!(tree.depth(), 1);
1260 }
1261 #[test]
1262 fn test_flat_substitution() {
1263 let mut sub = FlatSubstitution::new();
1264 sub.add("foo", "bar");
1265 sub.add("baz", "qux");
1266 assert_eq!(sub.apply("foo and baz"), "bar and qux");
1267 assert_eq!(sub.len(), 2);
1268 }
1269 #[test]
1270 fn test_stopwatch() {
1271 let mut sw = Stopwatch::start();
1272 sw.split();
1273 sw.split();
1274 assert_eq!(sw.num_splits(), 2);
1275 assert!(sw.elapsed_ms() >= 0.0);
1276 for &s in sw.splits() {
1277 assert!(s >= 0.0);
1278 }
1279 }
1280 #[test]
1281 fn test_either2() {
1282 let e: Either2<i32, &str> = Either2::First(42);
1283 assert!(e.is_first());
1284 let mapped = e.map_first(|x| x * 2);
1285 assert_eq!(mapped.first(), Some(84));
1286 let e2: Either2<i32, &str> = Either2::Second("hello");
1287 assert!(e2.is_second());
1288 assert_eq!(e2.second(), Some("hello"));
1289 }
1290 #[test]
1291 fn test_write_once() {
1292 let wo: WriteOnce<u32> = WriteOnce::new();
1293 assert!(!wo.is_written());
1294 assert!(wo.write(42));
1295 assert!(!wo.write(99));
1296 assert_eq!(wo.read(), Some(42));
1297 }
1298 #[test]
1299 fn test_sparse_vec() {
1300 let mut sv: SparseVec<i32> = SparseVec::new(100);
1301 sv.set(5, 10);
1302 sv.set(50, 20);
1303 assert_eq!(*sv.get(5), 10);
1304 assert_eq!(*sv.get(50), 20);
1305 assert_eq!(*sv.get(0), 0);
1306 assert_eq!(sv.nnz(), 2);
1307 sv.set(5, 0);
1308 assert_eq!(sv.nnz(), 1);
1309 }
1310 #[test]
1311 fn test_stack_calc() {
1312 let mut calc = StackCalc::new();
1313 calc.push(3);
1314 calc.push(4);
1315 calc.add();
1316 assert_eq!(calc.peek(), Some(7));
1317 calc.push(2);
1318 calc.mul();
1319 assert_eq!(calc.peek(), Some(14));
1320 }
1321}