Skip to main content

oxilean_kernel/prettyprint/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::{BinderInfo, Expr, Level, Name};
6
7use super::types::{
8    AnnotationTable, BiMap, ColorScheme, DiagMeta, Doc, DocBuilder, EscapeHelper, EventCounter,
9    ExprPrinter, FmtWidth, FrequencyTable, IdDispenser, IndentStyle, IntervalSet, LoopClock,
10    MemoSlot, PrettyConfig, PrettyDoc, PrettyPrinterState, PrettyTable, PrettyToken, PrintConfig,
11    SExprPrinter, ScopeStack, SimpleLruCache, Slot, SparseBitSet, StringInterner, Timestamp,
12    TokenPrinter, TypedId, WorkQueue, WorkStack,
13};
14
15/// Convert a level to a natural number if possible.
16pub(super) fn level_to_nat(level: &Level) -> Option<u32> {
17    match level {
18        Level::Zero => Some(0),
19        Level::Succ(l) => level_to_nat(l).map(|n| n + 1),
20        _ => None,
21    }
22}
23/// Decompose a level into (base, offset) where level = succ^offset(base).
24pub(super) fn level_to_offset(level: &Level) -> (&Level, u32) {
25    match level {
26        Level::Succ(l) => {
27            let (base, offset) = level_to_offset(l);
28            (base, offset + 1)
29        }
30        _ => (level, 0),
31    }
32}
33/// Collect function head and arguments from nested application.
34pub(super) fn collect_app_args(expr: &Expr) -> (&Expr, Vec<&Expr>) {
35    let mut args = Vec::new();
36    let mut e = expr;
37    while let Expr::App(f, a) = e {
38        args.push(a.as_ref());
39        e = f;
40    }
41    args.reverse();
42    (e, args)
43}
44/// Pretty print an expression with unicode symbols.
45pub fn print_expr(expr: &Expr) -> String {
46    let mut printer = ExprPrinter::new();
47    printer
48        .print(expr)
49        .expect("pretty-printer must succeed on valid expression");
50    printer.output()
51}
52/// Pretty print an expression without unicode symbols.
53pub fn print_expr_ascii(expr: &Expr) -> String {
54    let mut printer = ExprPrinter::new().with_unicode(false);
55    printer
56        .print(expr)
57        .expect("pretty-printer must succeed on valid expression");
58    printer.output()
59}
60/// Pretty print with a specific configuration.
61pub fn print_expr_with_config(expr: &Expr, config: PrintConfig) -> String {
62    let mut printer = ExprPrinter::with_config(config);
63    printer
64        .print(expr)
65        .expect("pretty-printer must succeed on valid expression");
66    printer.output()
67}
68/// Pretty print a level expression.
69pub fn print_level(level: &Level) -> String {
70    let mut printer = ExprPrinter::new();
71    printer
72        .print_level(level)
73        .expect("pretty-printer must succeed on valid level");
74    printer.output()
75}
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use crate::Literal;
80    #[test]
81    fn test_print_sort_prop() {
82        let expr = Expr::Sort(Level::zero());
83        let output = print_expr(&expr);
84        assert_eq!(output, "Prop");
85    }
86    #[test]
87    fn test_print_sort_type() {
88        let expr = Expr::Sort(Level::succ(Level::zero()));
89        let output = print_expr(&expr);
90        assert_eq!(output, "Type");
91    }
92    #[test]
93    fn test_print_sort_type_n() {
94        let expr = Expr::Sort(Level::succ(Level::succ(Level::zero())));
95        let output = print_expr(&expr);
96        assert_eq!(output, "Type 1");
97    }
98    #[test]
99    fn test_print_sort_param() {
100        let expr = Expr::Sort(Level::param(Name::str("u")));
101        let output = print_expr(&expr);
102        assert_eq!(output, "Sort u");
103    }
104    #[test]
105    fn test_print_bvar() {
106        let expr = Expr::BVar(0);
107        assert_eq!(print_expr(&expr), "#0");
108    }
109    #[test]
110    fn test_print_const() {
111        let expr = Expr::Const(Name::str("Nat"), vec![]);
112        assert_eq!(print_expr(&expr), "Nat");
113    }
114    #[test]
115    fn test_print_const_with_levels() {
116        let expr = Expr::Const(Name::str("List"), vec![Level::succ(Level::zero())]);
117        let config = PrintConfig::verbose();
118        let output = print_expr_with_config(&expr, config);
119        assert!(output.contains("List"));
120        assert!(output.contains("1"));
121    }
122    #[test]
123    fn test_print_lit() {
124        let expr = Expr::Lit(Literal::Nat(42));
125        assert_eq!(print_expr(&expr), "42");
126    }
127    #[test]
128    fn test_print_app() {
129        let f = Expr::Const(Name::str("f"), vec![]);
130        let a = Expr::Lit(Literal::Nat(1));
131        let app = Expr::App(Box::new(f), Box::new(a));
132        let output = print_expr(&app);
133        assert!(output.contains("f"));
134        assert!(output.contains("1"));
135    }
136    #[test]
137    fn test_print_lambda_unicode() {
138        let ty = Expr::Sort(Level::zero());
139        let body = Expr::BVar(0);
140        let lam = Expr::Lam(
141            BinderInfo::Default,
142            Name::str("x"),
143            Box::new(ty),
144            Box::new(body),
145        );
146        let output = print_expr(&lam);
147        assert!(output.contains("λ"));
148        assert!(output.contains("x"));
149    }
150    #[test]
151    fn test_print_lambda_ascii() {
152        let ty = Expr::Sort(Level::zero());
153        let body = Expr::BVar(0);
154        let lam = Expr::Lam(
155            BinderInfo::Default,
156            Name::str("x"),
157            Box::new(ty),
158            Box::new(body),
159        );
160        let output = print_expr_ascii(&lam);
161        assert!(output.contains("fun"));
162    }
163    #[test]
164    fn test_print_pi_arrow() {
165        let ty = Expr::Sort(Level::succ(Level::zero()));
166        let body = Expr::Sort(Level::succ(Level::zero()));
167        let pi = Expr::Pi(
168            BinderInfo::Default,
169            Name::str("_"),
170            Box::new(ty),
171            Box::new(body),
172        );
173        let output = print_expr(&pi);
174        assert!(output.contains("→"));
175    }
176    #[test]
177    fn test_print_pi_forall() {
178        let ty = Expr::Sort(Level::succ(Level::zero()));
179        let body = Expr::BVar(0);
180        let pi = Expr::Pi(
181            BinderInfo::Default,
182            Name::str("x"),
183            Box::new(ty),
184            Box::new(body),
185        );
186        let output = print_expr(&pi);
187        assert!(output.contains("∀"));
188        assert!(output.contains("x"));
189    }
190    #[test]
191    fn test_print_implicit_binder() {
192        let ty = Expr::Sort(Level::succ(Level::zero()));
193        let body = Expr::BVar(0);
194        let pi = Expr::Pi(
195            BinderInfo::Implicit,
196            Name::str("α"),
197            Box::new(ty),
198            Box::new(body),
199        );
200        let output = print_expr(&pi);
201        assert!(output.contains("{"));
202        assert!(output.contains("}"));
203    }
204    #[test]
205    fn test_print_inst_implicit_binder() {
206        let ty = Expr::Const(Name::str("Monad"), vec![]);
207        let body = Expr::BVar(0);
208        let pi = Expr::Pi(
209            BinderInfo::InstImplicit,
210            Name::str("m"),
211            Box::new(ty),
212            Box::new(body),
213        );
214        let output = print_expr(&pi);
215        assert!(output.contains("["));
216        assert!(output.contains("]"));
217    }
218    #[test]
219    fn test_print_level_nat() {
220        let level = Level::succ(Level::succ(Level::succ(Level::zero())));
221        let output = print_level(&level);
222        assert_eq!(output, "3");
223    }
224    #[test]
225    fn test_print_level_param_plus() {
226        let level = Level::succ(Level::param(Name::str("u")));
227        let output = print_level(&level);
228        assert_eq!(output, "u+1");
229    }
230    #[test]
231    fn test_print_level_mvar() {
232        let level = Level::MVar(crate::LevelMVarId(42));
233        let output = print_level(&level);
234        assert_eq!(output, "?u_42");
235    }
236    #[test]
237    fn test_print_config_verbose() {
238        let config = PrintConfig::verbose();
239        assert!(config.show_implicit);
240        assert!(config.show_universes);
241    }
242    #[test]
243    fn test_print_config_ascii() {
244        let config = PrintConfig::ascii();
245        assert!(!config.unicode);
246    }
247}
248/// Convert an `Expr` to a `Doc` for structured pretty printing.
249#[allow(dead_code)]
250pub fn expr_to_doc(expr: &Expr) -> Doc {
251    expr_to_doc_prec(expr, 0)
252}
253pub(super) fn expr_to_doc_prec(expr: &Expr, prec: u32) -> Doc {
254    match expr {
255        Expr::Sort(level) => Doc::text(format!("Sort({})", level)),
256        Expr::BVar(i) => Doc::text(format!("#{}", i)),
257        Expr::FVar(id) => Doc::text(format!("@{}", id.0)),
258        Expr::Const(name, _) => Doc::text(name.to_string()),
259        Expr::Lit(lit) => Doc::text(format!("{}", lit)),
260        Expr::App(f, a) => {
261            let fd = expr_to_doc_prec(f, 10);
262            let ad = expr_to_doc_prec(a, 11);
263            let inner = fd.concat(Doc::text(" ")).concat(ad);
264            if prec > 10 {
265                Doc::text("(").concat(inner).concat(Doc::text(")"))
266            } else {
267                inner
268            }
269        }
270        Expr::Lam(_, name, ty, body) => {
271            let header = Doc::text(format!("fun ({} : ", name))
272                .concat(expr_to_doc_prec(ty, 0))
273                .concat(Doc::text(") -> "));
274            let bd = expr_to_doc_prec(body, 0);
275            let inner = header.concat(bd);
276            if prec > 0 {
277                Doc::text("(").concat(inner).concat(Doc::text(")"))
278            } else {
279                inner
280            }
281        }
282        Expr::Pi(_, name, ty, body) => {
283            if name.is_anonymous() || *name == Name::str("_") {
284                let td = expr_to_doc_prec(ty, 25);
285                let bd = expr_to_doc_prec(body, 24);
286                let inner = td.concat(Doc::text(" -> ")).concat(bd);
287                if prec > 0 {
288                    Doc::text("(").concat(inner).concat(Doc::text(")"))
289                } else {
290                    inner
291                }
292            } else {
293                let header = Doc::text(format!("forall ({} : ", name))
294                    .concat(expr_to_doc_prec(ty, 0))
295                    .concat(Doc::text("), "));
296                let bd = expr_to_doc_prec(body, 0);
297                let inner = header.concat(bd);
298                if prec > 0 {
299                    Doc::text("(").concat(inner).concat(Doc::text(")"))
300                } else {
301                    inner
302                }
303            }
304        }
305        Expr::Let(name, ty, val, body) => {
306            let line1 = Doc::text(format!("let {} : ", name))
307                .concat(expr_to_doc_prec(ty, 0))
308                .concat(Doc::text(" := "))
309                .concat(expr_to_doc_prec(val, 0));
310            let line2 = Doc::text("in ").concat(expr_to_doc_prec(body, 0));
311            Doc::Nest(2, Box::new(line1.concat(Doc::line()).concat(line2)))
312        }
313        Expr::Proj(name, idx, e) => {
314            expr_to_doc_prec(e, 11).concat(Doc::text(format!(".{}.{}", name, idx)))
315        }
316    }
317}
318/// Pretty print a `Name` as a dot-separated string.
319#[allow(dead_code)]
320pub fn print_name_str(name: &Name) -> String {
321    name.to_string()
322}
323/// Check if a name is a simple (single-component) name.
324#[allow(dead_code)]
325pub fn is_simple_name(name: &Name) -> bool {
326    matches!(name, Name::Str(parent, _) if matches!(parent.as_ref(), Name::Anonymous))
327}
328/// Produce a one-line summary of an expression for debug output.
329#[allow(dead_code)]
330pub fn expr_summary(expr: &Expr) -> String {
331    match expr {
332        Expr::Sort(l) => format!("Sort({})", l),
333        Expr::BVar(i) => format!("BVar({})", i),
334        Expr::FVar(id) => format!("FVar({})", id.0),
335        Expr::Const(n, _) => format!("Const({})", n),
336        Expr::App(_, _) => {
337            let (head, args) = collect_app_args(expr);
338            format!("App({}, {} args)", expr_summary(head), args.len())
339        }
340        Expr::Lam(_, n, _, _) => format!("Lam({})", n),
341        Expr::Pi(_, n, _, _) => {
342            if n.is_anonymous() || *n == Name::str("_") {
343                "Pi(->)".to_string()
344            } else {
345                format!("Pi(forall {})", n)
346            }
347        }
348        Expr::Let(n, _, _, _) => format!("Let({})", n),
349        Expr::Lit(l) => format!("Lit({})", l),
350        Expr::Proj(n, i, _) => format!("Proj({}.{})", n, i),
351    }
352}
353/// ANSI color codes for terminal output.
354#[allow(dead_code)]
355pub mod ansi {
356    /// ANSI reset code.
357    pub const RESET: &str = "\x1b[0m";
358    /// ANSI bold code.
359    pub const BOLD: &str = "\x1b[1m";
360    /// ANSI dim code.
361    pub const DIM: &str = "\x1b[2m";
362    /// ANSI red color.
363    pub const RED: &str = "\x1b[31m";
364    /// ANSI green color.
365    pub const GREEN: &str = "\x1b[32m";
366    /// ANSI yellow color.
367    pub const YELLOW: &str = "\x1b[33m";
368    /// ANSI blue color.
369    pub const BLUE: &str = "\x1b[34m";
370    /// ANSI magenta color.
371    pub const MAGENTA: &str = "\x1b[35m";
372    /// ANSI cyan color.
373    pub const CYAN: &str = "\x1b[36m";
374}
375/// Colorize an expression string for terminal display.
376///
377/// Keywords are highlighted in cyan, literals in yellow, types in blue.
378#[allow(dead_code)]
379pub fn colorize(expr: &Expr) -> String {
380    match expr {
381        Expr::Sort(_) => format!("{}{}{}", ansi::BLUE, print_expr(expr), ansi::RESET),
382        Expr::Lit(_) => format!("{}{}{}", ansi::YELLOW, print_expr(expr), ansi::RESET),
383        Expr::Const(_, _) => {
384            format!("{}{}{}", ansi::GREEN, print_expr(expr), ansi::RESET)
385        }
386        _ => print_expr(expr),
387    }
388}
389#[cfg(test)]
390mod extra_prettyprint_tests {
391    use super::*;
392    use crate::Literal;
393    #[test]
394    fn test_doc_text_render() {
395        let d = Doc::text("hello");
396        assert_eq!(d.render(80), "hello");
397    }
398    #[test]
399    fn test_doc_concat() {
400        let d = Doc::text("foo").concat(Doc::text("bar"));
401        assert_eq!(d.render(80), "foobar");
402    }
403    #[test]
404    fn test_doc_line() {
405        let d = Doc::text("a").concat(Doc::line()).concat(Doc::text("b"));
406        let rendered = d.render(80);
407        assert!(rendered.contains('\n'));
408        assert!(rendered.contains('a'));
409        assert!(rendered.contains('b'));
410    }
411    #[test]
412    fn test_doc_nest() {
413        let d = Doc::nest(4, Doc::text("x").concat(Doc::line()).concat(Doc::text("y")));
414        let rendered = d.render(80);
415        if let Some(pos) = rendered.find('\n') {
416            let rest = &rendered[pos + 1..];
417            assert!(rest.starts_with("    "));
418        }
419    }
420    #[test]
421    fn test_expr_to_doc_sort() {
422        let e = Expr::Sort(Level::zero());
423        let d = expr_to_doc(&e);
424        let s = d.render(80);
425        assert_eq!(s, "Sort(0)");
426    }
427    #[test]
428    fn test_expr_to_doc_bvar() {
429        let d = expr_to_doc(&Expr::BVar(3));
430        assert_eq!(d.render(80), "#3");
431    }
432    #[test]
433    fn test_expr_to_doc_const() {
434        let d = expr_to_doc(&Expr::Const(Name::str("Nat"), vec![]));
435        assert_eq!(d.render(80), "Nat");
436    }
437    #[test]
438    fn test_expr_to_doc_app() {
439        let f = Expr::Const(Name::str("f"), vec![]);
440        let a = Expr::Lit(Literal::Nat(1));
441        let app = Expr::App(Box::new(f), Box::new(a));
442        let d = expr_to_doc(&app);
443        let s = d.render(80);
444        assert!(s.contains("f"));
445        assert!(s.contains("1"));
446    }
447    #[test]
448    fn test_expr_summary_sort() {
449        let s = expr_summary(&Expr::Sort(Level::zero()));
450        assert_eq!(s, "Sort(0)");
451    }
452    #[test]
453    fn test_expr_summary_app() {
454        let f = Expr::Const(Name::str("g"), vec![]);
455        let a = Expr::Lit(Literal::Nat(5));
456        let b = Expr::Lit(Literal::Nat(6));
457        let app = Expr::App(Box::new(Expr::App(Box::new(f), Box::new(a))), Box::new(b));
458        let s = expr_summary(&app);
459        assert!(s.contains("2 args"));
460        assert!(s.contains("g"));
461    }
462    #[test]
463    fn test_expr_summary_pi_arrow() {
464        let pi = Expr::Pi(
465            BinderInfo::Default,
466            Name::str("_"),
467            Box::new(Expr::Sort(Level::zero())),
468            Box::new(Expr::Sort(Level::zero())),
469        );
470        let s = expr_summary(&pi);
471        assert!(s.contains("->"));
472    }
473    #[test]
474    fn test_expr_summary_let() {
475        let e = Expr::Let(
476            Name::str("x"),
477            Box::new(Expr::Sort(Level::zero())),
478            Box::new(Expr::BVar(0)),
479            Box::new(Expr::BVar(0)),
480        );
481        let s = expr_summary(&e);
482        assert!(s.contains("x"));
483    }
484    #[test]
485    fn test_is_simple_name_true() {
486        let n = Name::Str(Box::new(Name::Anonymous), "foo".to_string());
487        assert!(is_simple_name(&n));
488    }
489    #[test]
490    fn test_is_simple_name_false() {
491        let n = Name::Str(
492            Box::new(Name::Str(Box::new(Name::Anonymous), "A".to_string())),
493            "b".to_string(),
494        );
495        assert!(!is_simple_name(&n));
496    }
497    #[test]
498    fn test_print_name_str() {
499        let n = Name::str("MyName");
500        assert_eq!(print_name_str(&n), "MyName");
501    }
502    #[test]
503    fn test_colorize_sort_contains_text() {
504        let e = Expr::Sort(Level::zero());
505        let c = colorize(&e);
506        assert!(c.contains("Prop") || c.contains("Sort"));
507    }
508    #[test]
509    fn test_colorize_lit_contains_number() {
510        let e = Expr::Lit(crate::Literal::Nat(42));
511        let c = colorize(&e);
512        assert!(c.contains("42"));
513    }
514    #[test]
515    fn test_print_expr_let() {
516        let e = Expr::Let(
517            Name::str("x"),
518            Box::new(Expr::Sort(Level::zero())),
519            Box::new(Expr::Lit(crate::Literal::Nat(1))),
520            Box::new(Expr::BVar(0)),
521        );
522        let s = print_expr(&e);
523        assert!(s.contains("let"));
524        assert!(s.contains("x"));
525    }
526    #[test]
527    fn test_print_expr_proj() {
528        let e = Expr::Proj(Name::str("Prod"), 0, Box::new(Expr::BVar(0)));
529        let s = print_expr(&e);
530        assert!(s.contains("Prod"));
531    }
532}
533#[cfg(test)]
534mod tests_prettyprint_extra {
535    use super::*;
536    #[test]
537    fn test_color_scheme() {
538        let cs = ColorScheme::DEFAULT;
539        assert!(cs.is_colored());
540        assert!(!ColorScheme::MONO.is_colored());
541    }
542    #[test]
543    fn test_indent_style() {
544        let s2 = IndentStyle::Spaces(2);
545        assert_eq!(s2.one_level(), "  ");
546        assert_eq!(s2.for_depth(3), "      ");
547        let tabs = IndentStyle::Tabs;
548        assert_eq!(tabs.one_level(), "\t");
549    }
550    #[test]
551    fn test_pretty_doc_render() {
552        let doc = PrettyDoc::concat(
553            PrettyDoc::text("fun "),
554            PrettyDoc::concat(
555                PrettyDoc::text("x"),
556                PrettyDoc::concat(PrettyDoc::text(" ->"), PrettyDoc::Newline),
557            ),
558        );
559        let rendered = doc.render(80, IndentStyle::Spaces(2));
560        assert!(rendered.contains("fun x ->"));
561    }
562    #[test]
563    fn test_pretty_token_raw() {
564        let tok = PrettyToken::Keyword("theorem".to_string());
565        assert_eq!(tok.raw_text(), "theorem");
566        let space = PrettyToken::Space;
567        assert_eq!(space.raw_text(), " ");
568    }
569    #[test]
570    fn test_token_printer() {
571        let config = PrettyConfig::default_config();
572        let printer = TokenPrinter::new(config);
573        let tokens = vec![
574            PrettyToken::Keyword("theorem".into()),
575            PrettyToken::Space,
576            PrettyToken::Ident("foo".into()),
577        ];
578        let result = printer.render(&tokens);
579        assert_eq!(result, "theorem foo");
580    }
581    #[test]
582    fn test_sexpr_printer() {
583        let p = SExprPrinter::new();
584        let s = p.app("Pi", &["x", "Nat", "body"]);
585        assert_eq!(s, "(Pi x Nat body)");
586        let empty = p.app("Unit", &[]);
587        assert_eq!(empty, "Unit");
588    }
589    #[test]
590    fn test_fmt_width() {
591        assert_eq!(FmtWidth::decimal_width(0), 1);
592        assert_eq!(FmtWidth::decimal_width(999), 3);
593        assert_eq!(FmtWidth::pad_right("hi", 5), "hi   ");
594        assert_eq!(FmtWidth::pad_left("hi", 5), "   hi");
595        assert_eq!(FmtWidth::center("hi", 6), "  hi  ");
596    }
597    #[test]
598    fn test_pretty_table() {
599        let mut tbl = PrettyTable::new(vec!["Name".into(), "Value".into()]);
600        tbl.add_row(vec!["alpha".into(), "1".into()]);
601        tbl.add_row(vec!["beta".into(), "2".into()]);
602        let rendered = tbl.render();
603        assert!(rendered.contains("Name"));
604        assert!(rendered.contains("alpha"));
605    }
606}
607#[cfg(test)]
608mod tests_prettyprint_extra2 {
609    use super::*;
610    #[test]
611    fn test_printer_state() {
612        let mut ps = PrettyPrinterState::new(80, IndentStyle::Spaces(2));
613        ps.write("hello");
614        assert_eq!(ps.col, 5);
615        ps.push_indent();
616        ps.newline();
617        assert_eq!(ps.col, 2);
618        let out = ps.finish();
619        assert!(out.contains("hello"));
620    }
621    #[test]
622    fn test_doc_builder() {
623        let doc = DocBuilder::text("fun")
624            .then_text(" x")
625            .then_text(" ->")
626            .then_newline()
627            .then_text("  body")
628            .build();
629        let rendered = doc.render(80, IndentStyle::Spaces(2));
630        assert!(rendered.contains("fun x ->"));
631    }
632}
633#[cfg(test)]
634mod tests_escape_helper {
635    use super::*;
636    #[test]
637    fn test_escape_unescape() {
638        let original = "hello\nworld\t\"end\"";
639        let escaped = EscapeHelper::escape_str(original);
640        assert!(escaped.starts_with('"'));
641        let unescaped = EscapeHelper::unescape_str(&escaped);
642        assert_eq!(unescaped, original);
643    }
644}
645#[cfg(test)]
646mod tests_common_infra {
647    use super::*;
648    #[test]
649    fn test_event_counter() {
650        let mut ec = EventCounter::new();
651        ec.inc("hit");
652        ec.inc("hit");
653        ec.inc("miss");
654        assert_eq!(ec.get("hit"), 2);
655        assert_eq!(ec.get("miss"), 1);
656        assert_eq!(ec.total(), 3);
657        ec.reset();
658        assert_eq!(ec.total(), 0);
659    }
660    #[test]
661    fn test_diag_meta() {
662        let mut m = DiagMeta::new();
663        m.add("os", "linux");
664        m.add("arch", "x86_64");
665        assert_eq!(m.get("os"), Some("linux"));
666        assert_eq!(m.len(), 2);
667        let s = m.to_string();
668        assert!(s.contains("os=linux"));
669    }
670    #[test]
671    fn test_scope_stack() {
672        let mut ss = ScopeStack::new();
673        ss.push("Nat");
674        ss.push("succ");
675        assert_eq!(ss.current(), Some("succ"));
676        assert_eq!(ss.depth(), 2);
677        assert_eq!(ss.path(), "Nat.succ");
678        ss.pop();
679        assert_eq!(ss.current(), Some("Nat"));
680    }
681    #[test]
682    fn test_annotation_table() {
683        let mut tbl = AnnotationTable::new();
684        tbl.annotate("doc", "first line");
685        tbl.annotate("doc", "second line");
686        assert_eq!(tbl.get_all("doc").len(), 2);
687        assert!(tbl.has("doc"));
688        assert!(!tbl.has("other"));
689    }
690    #[test]
691    fn test_work_stack() {
692        let mut ws = WorkStack::new();
693        ws.push(1u32);
694        ws.push(2u32);
695        assert_eq!(ws.pop(), Some(2));
696        assert_eq!(ws.len(), 1);
697    }
698    #[test]
699    fn test_work_queue() {
700        let mut wq = WorkQueue::new();
701        wq.enqueue(1u32);
702        wq.enqueue(2u32);
703        assert_eq!(wq.dequeue(), Some(1));
704        assert_eq!(wq.len(), 1);
705    }
706    #[test]
707    fn test_sparse_bit_set() {
708        let mut bs = SparseBitSet::new(128);
709        bs.set(5);
710        bs.set(63);
711        bs.set(64);
712        assert!(bs.get(5));
713        assert!(bs.get(63));
714        assert!(bs.get(64));
715        assert!(!bs.get(0));
716        assert_eq!(bs.count_ones(), 3);
717        bs.clear(5);
718        assert!(!bs.get(5));
719    }
720    #[test]
721    fn test_loop_clock() {
722        let mut clk = LoopClock::start();
723        for _ in 0..10 {
724            clk.tick();
725        }
726        assert_eq!(clk.iters(), 10);
727        assert!(clk.elapsed_us() >= 0.0);
728    }
729}
730#[cfg(test)]
731mod tests_extra_data_structures {
732    use super::*;
733    #[test]
734    fn test_simple_lru_cache() {
735        let mut cache: SimpleLruCache<&str, u32> = SimpleLruCache::new(3);
736        cache.put("a", 1);
737        cache.put("b", 2);
738        cache.put("c", 3);
739        assert_eq!(cache.get(&"a"), Some(&1));
740        cache.put("d", 4);
741        assert!(cache.len() <= 3);
742    }
743    #[test]
744    fn test_string_interner() {
745        let mut si = StringInterner::new();
746        let id1 = si.intern("hello");
747        let id2 = si.intern("hello");
748        assert_eq!(id1, id2);
749        let id3 = si.intern("world");
750        assert_ne!(id1, id3);
751        assert_eq!(si.get(id1), Some("hello"));
752        assert_eq!(si.len(), 2);
753    }
754    #[test]
755    fn test_frequency_table() {
756        let mut ft = FrequencyTable::new();
757        ft.record("a");
758        ft.record("b");
759        ft.record("a");
760        ft.record("a");
761        assert_eq!(ft.freq(&"a"), 3);
762        assert_eq!(ft.freq(&"b"), 1);
763        assert_eq!(ft.most_frequent(), Some((&"a", 3)));
764        assert_eq!(ft.total(), 4);
765        assert_eq!(ft.distinct(), 2);
766    }
767    #[test]
768    fn test_bimap() {
769        let mut bm: BiMap<u32, &str> = BiMap::new();
770        bm.insert(1, "one");
771        bm.insert(2, "two");
772        assert_eq!(bm.get_b(&1), Some(&"one"));
773        assert_eq!(bm.get_a(&"two"), Some(&2));
774        assert_eq!(bm.len(), 2);
775    }
776}
777#[cfg(test)]
778mod tests_interval_set {
779    use super::*;
780    #[test]
781    fn test_interval_set() {
782        let mut s = IntervalSet::new();
783        s.add(1, 5);
784        s.add(3, 8);
785        assert_eq!(s.num_intervals(), 1);
786        assert_eq!(s.cardinality(), 8);
787        assert!(s.contains(4));
788        assert!(!s.contains(9));
789        s.add(10, 15);
790        assert_eq!(s.num_intervals(), 2);
791    }
792}
793/// Returns the current timestamp.
794#[allow(dead_code)]
795pub fn now_us() -> Timestamp {
796    let us = std::time::SystemTime::UNIX_EPOCH
797        .elapsed()
798        .map(|d| d.as_micros() as u64)
799        .unwrap_or(0);
800    Timestamp::from_us(us)
801}
802#[cfg(test)]
803mod tests_typed_utilities {
804    use super::*;
805    #[test]
806    fn test_timestamp() {
807        let t1 = Timestamp::from_us(1000);
808        let t2 = Timestamp::from_us(1500);
809        assert_eq!(t2.elapsed_since(t1), 500);
810        assert!(t1 < t2);
811    }
812    #[test]
813    fn test_typed_id() {
814        struct Foo;
815        let id: TypedId<Foo> = TypedId::new(42);
816        assert_eq!(id.raw(), 42);
817        assert_eq!(format!("{id}"), "#42");
818    }
819    #[test]
820    fn test_id_dispenser() {
821        struct Bar;
822        let mut disp: IdDispenser<Bar> = IdDispenser::new();
823        let a = disp.next();
824        let b = disp.next();
825        assert_eq!(a.raw(), 0);
826        assert_eq!(b.raw(), 1);
827        assert_eq!(disp.count(), 2);
828    }
829    #[test]
830    fn test_slot() {
831        let mut slot: Slot<u32> = Slot::empty();
832        assert!(!slot.is_filled());
833        slot.fill(99);
834        assert!(slot.is_filled());
835        assert_eq!(slot.get(), Some(&99));
836        let v = slot.take();
837        assert_eq!(v, Some(99));
838        assert!(!slot.is_filled());
839    }
840    #[test]
841    #[should_panic]
842    fn test_slot_double_fill() {
843        let mut slot: Slot<u32> = Slot::empty();
844        slot.fill(1);
845        slot.fill(2);
846    }
847    #[test]
848    fn test_memo_slot() {
849        let mut ms: MemoSlot<u32> = MemoSlot::new();
850        assert!(!ms.is_cached());
851        let val = ms.get_or_compute(|| 42);
852        assert_eq!(*val, 42);
853        assert!(ms.is_cached());
854        ms.invalidate();
855        assert!(!ms.is_cached());
856    }
857}