Skip to main content

oxilean_kernel/conversion/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::reduce::TransparencyMode;
6use crate::{Environment, Expr, Reducer};
7
8use std::collections::HashMap;
9
10use super::functions::coercion_head_matches;
11
12/// A simple directed acyclic graph.
13#[allow(dead_code)]
14pub struct SimpleDag {
15    /// `edges[i]` is the list of direct successors of node `i`.
16    edges: Vec<Vec<usize>>,
17}
18#[allow(dead_code)]
19impl SimpleDag {
20    /// Creates a DAG with `n` nodes and no edges.
21    pub fn new(n: usize) -> Self {
22        Self {
23            edges: vec![Vec::new(); n],
24        }
25    }
26    /// Adds an edge from `from` to `to`.
27    pub fn add_edge(&mut self, from: usize, to: usize) {
28        if from < self.edges.len() {
29            self.edges[from].push(to);
30        }
31    }
32    /// Returns the successors of `node`.
33    pub fn successors(&self, node: usize) -> &[usize] {
34        self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
35    }
36    /// Returns `true` if `from` can reach `to` via DFS.
37    pub fn can_reach(&self, from: usize, to: usize) -> bool {
38        let mut visited = vec![false; self.edges.len()];
39        self.dfs(from, to, &mut visited)
40    }
41    fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
42        if cur == target {
43            return true;
44        }
45        if cur >= visited.len() || visited[cur] {
46            return false;
47        }
48        visited[cur] = true;
49        for &next in self.successors(cur) {
50            if self.dfs(next, target, visited) {
51                return true;
52            }
53        }
54        false
55    }
56    /// Returns the topological order of nodes, or `None` if a cycle is detected.
57    pub fn topological_sort(&self) -> Option<Vec<usize>> {
58        let n = self.edges.len();
59        let mut in_degree = vec![0usize; n];
60        for succs in &self.edges {
61            for &s in succs {
62                if s < n {
63                    in_degree[s] += 1;
64                }
65            }
66        }
67        let mut queue: std::collections::VecDeque<usize> =
68            (0..n).filter(|&i| in_degree[i] == 0).collect();
69        let mut order = Vec::new();
70        while let Some(node) = queue.pop_front() {
71            order.push(node);
72            for &s in self.successors(node) {
73                if s < n {
74                    in_degree[s] -= 1;
75                    if in_degree[s] == 0 {
76                        queue.push_back(s);
77                    }
78                }
79            }
80        }
81        if order.len() == n {
82            Some(order)
83        } else {
84            None
85        }
86    }
87    /// Returns the number of nodes.
88    pub fn num_nodes(&self) -> usize {
89        self.edges.len()
90    }
91}
92/// A token bucket rate limiter.
93#[allow(dead_code)]
94pub struct TokenBucket {
95    capacity: u64,
96    tokens: u64,
97    refill_per_ms: u64,
98    last_refill: crate::wall_clock::Instant,
99}
100#[allow(dead_code)]
101impl TokenBucket {
102    /// Creates a new token bucket.
103    pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
104        Self {
105            capacity,
106            tokens: capacity,
107            refill_per_ms,
108            last_refill: crate::wall_clock::Instant::now(),
109        }
110    }
111    /// Attempts to consume `n` tokens.  Returns `true` on success.
112    pub fn try_consume(&mut self, n: u64) -> bool {
113        self.refill();
114        if self.tokens >= n {
115            self.tokens -= n;
116            true
117        } else {
118            false
119        }
120    }
121    fn refill(&mut self) {
122        let now = crate::wall_clock::Instant::now();
123        let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
124        if elapsed_ms > 0 {
125            let new_tokens = elapsed_ms * self.refill_per_ms;
126            self.tokens = (self.tokens + new_tokens).min(self.capacity);
127            self.last_refill = now;
128        }
129    }
130    /// Returns the number of currently available tokens.
131    pub fn available(&self) -> u64 {
132        self.tokens
133    }
134    /// Returns the bucket capacity.
135    pub fn capacity(&self) -> u64 {
136        self.capacity
137    }
138}
139/// A write-once cell.
140#[allow(dead_code)]
141pub struct WriteOnce<T> {
142    value: std::cell::Cell<Option<T>>,
143}
144#[allow(dead_code)]
145impl<T: Copy> WriteOnce<T> {
146    /// Creates an empty write-once cell.
147    pub fn new() -> Self {
148        Self {
149            value: std::cell::Cell::new(None),
150        }
151    }
152    /// Writes a value.  Returns `false` if already written.
153    pub fn write(&self, val: T) -> bool {
154        if self.value.get().is_some() {
155            return false;
156        }
157        self.value.set(Some(val));
158        true
159    }
160    /// Returns the value if written.
161    pub fn read(&self) -> Option<T> {
162        self.value.get()
163    }
164    /// Returns `true` if the value has been written.
165    pub fn is_written(&self) -> bool {
166        self.value.get().is_some()
167    }
168}
169/// A hierarchical configuration tree.
170#[allow(dead_code)]
171pub struct ConfigNode {
172    key: String,
173    value: Option<String>,
174    children: Vec<ConfigNode>,
175}
176#[allow(dead_code)]
177impl ConfigNode {
178    /// Creates a leaf config node with a value.
179    pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
180        Self {
181            key: key.into(),
182            value: Some(value.into()),
183            children: Vec::new(),
184        }
185    }
186    /// Creates a section node with children.
187    pub fn section(key: impl Into<String>) -> Self {
188        Self {
189            key: key.into(),
190            value: None,
191            children: Vec::new(),
192        }
193    }
194    /// Adds a child node.
195    pub fn add_child(&mut self, child: ConfigNode) {
196        self.children.push(child);
197    }
198    /// Returns the key.
199    pub fn key(&self) -> &str {
200        &self.key
201    }
202    /// Returns the value, or `None` for section nodes.
203    pub fn value(&self) -> Option<&str> {
204        self.value.as_deref()
205    }
206    /// Returns the number of children.
207    pub fn num_children(&self) -> usize {
208        self.children.len()
209    }
210    /// Looks up a dot-separated path.
211    pub fn lookup(&self, path: &str) -> Option<&str> {
212        let mut parts = path.splitn(2, '.');
213        let head = parts.next()?;
214        let tail = parts.next();
215        if head != self.key {
216            return None;
217        }
218        match tail {
219            None => self.value.as_deref(),
220            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
221        }
222    }
223    fn lookup_relative(&self, path: &str) -> Option<&str> {
224        let mut parts = path.splitn(2, '.');
225        let head = parts.next()?;
226        let tail = parts.next();
227        if head != self.key {
228            return None;
229        }
230        match tail {
231            None => self.value.as_deref(),
232            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
233        }
234    }
235}
236/// Conversion result with diagnostic information.
237#[derive(Debug, Clone)]
238pub enum ConvResult {
239    /// The expressions are definitionally equal.
240    Equal,
241    /// The expressions are not definitionally equal.
242    NotEqual,
243    /// Timeout: conversion check exceeded the step limit.
244    /// Timeout: conversion check exceeded the step limit.
245    Timeout {
246        /// Number of steps performed before timeout.
247        steps: usize,
248    },
249    /// Error during conversion.
250    Error(String),
251}
252impl ConvResult {
253    /// Check if the result is Equal.
254    pub fn is_equal(&self) -> bool {
255        matches!(self, ConvResult::Equal)
256    }
257    /// Check if the result is definitive (not timed out or errored).
258    pub fn is_definitive(&self) -> bool {
259        matches!(self, ConvResult::Equal | ConvResult::NotEqual)
260    }
261}
262/// A versioned record that stores a history of values.
263#[allow(dead_code)]
264pub struct VersionedRecord<T: Clone> {
265    /// Every earlier value, oldest first: version `n` is `earlier[n]`.
266    earlier: Vec<T>,
267    /// The latest value (version `earlier.len()`), declared last so it drops last.
268    current: T,
269}
270#[allow(dead_code)]
271impl<T: Clone> VersionedRecord<T> {
272    /// Creates a new record with an initial value.
273    pub fn new(initial: T) -> Self {
274        Self {
275            earlier: Vec::new(),
276            current: initial,
277        }
278    }
279    /// Updates the record with a new version.
280    pub fn update(&mut self, val: T) {
281        let previous = std::mem::replace(&mut self.current, val);
282        self.earlier.push(previous);
283    }
284    /// Returns the current (latest) value.
285    pub fn current(&self) -> &T {
286        &self.current
287    }
288    /// Returns the value at version `n` (0-indexed), or `None`.
289    pub fn at_version(&self, n: usize) -> Option<&T> {
290        match self.earlier.get(n) {
291            Some(value) => Some(value),
292            None if n == self.earlier.len() => Some(&self.current),
293            None => None,
294        }
295    }
296    /// Returns the version number of the current value.
297    pub fn version(&self) -> usize {
298        self.earlier.len()
299    }
300    /// Returns `true` if more than one version exists.
301    pub fn has_history(&self) -> bool {
302        !self.earlier.is_empty()
303    }
304}
305/// A simple decision tree node for rule dispatching.
306#[allow(dead_code)]
307#[allow(missing_docs)]
308pub enum DecisionNode {
309    /// A leaf with an action string.
310    Leaf(String),
311    /// An interior node: check `key` equals `val` → `yes_branch`, else `no_branch`.
312    Branch {
313        key: String,
314        val: String,
315        yes_branch: Box<DecisionNode>,
316        no_branch: Box<DecisionNode>,
317    },
318}
319#[allow(dead_code)]
320impl DecisionNode {
321    /// Evaluates the decision tree with the given context.
322    pub fn evaluate(&self, ctx: &std::collections::HashMap<String, String>) -> &str {
323        match self {
324            DecisionNode::Leaf(action) => action.as_str(),
325            DecisionNode::Branch {
326                key,
327                val,
328                yes_branch,
329                no_branch,
330            } => {
331                let actual = ctx.get(key).map(|s| s.as_str()).unwrap_or("");
332                if actual == val.as_str() {
333                    yes_branch.evaluate(ctx)
334                } else {
335                    no_branch.evaluate(ctx)
336                }
337            }
338        }
339    }
340    /// Returns the depth of the decision tree.
341    pub fn depth(&self) -> usize {
342        match self {
343            DecisionNode::Leaf(_) => 0,
344            DecisionNode::Branch {
345                yes_branch,
346                no_branch,
347                ..
348            } => 1 + yes_branch.depth().max(no_branch.depth()),
349        }
350    }
351}
352/// Conversion checker for definitional equality.
353///
354/// Wraps a Reducer and performs structural comparison after WHNF.
355pub struct ConversionChecker {
356    /// Reducer for normalization
357    reducer: Reducer,
358    /// Maximum number of reduction steps
359    max_steps: usize,
360    /// Current transparency mode
361    transparency: TransparencyMode,
362}
363impl ConversionChecker {
364    /// Create a new conversion checker with default transparency.
365    pub fn new() -> Self {
366        Self {
367            reducer: Reducer::new(),
368            max_steps: 10000,
369            transparency: TransparencyMode::Default,
370        }
371    }
372    /// Create a conversion checker with a specific transparency mode.
373    pub fn with_transparency(mode: TransparencyMode) -> Self {
374        let mut checker = Self::new();
375        checker.set_transparency(mode);
376        checker
377    }
378    /// Set the transparency mode.
379    pub fn set_transparency(&mut self, mode: TransparencyMode) {
380        self.transparency = mode;
381        self.reducer.set_transparency(mode);
382    }
383    /// Get the current transparency mode.
384    pub fn transparency(&self) -> TransparencyMode {
385        self.transparency
386    }
387    /// Check if two expressions are convertible (without environment).
388    pub fn is_convertible(&mut self, e1: &Expr, e2: &Expr) -> bool {
389        self.is_convertible_impl(e1, e2, 0)
390    }
391    /// Check if two expressions are convertible (with environment).
392    pub fn is_convertible_in_env(&mut self, e1: &Expr, e2: &Expr, env: &Environment) -> bool {
393        self.is_convertible_env_impl(e1, e2, env, 0)
394    }
395    /// Implementation with depth tracking (no environment).
396    fn is_convertible_impl(&mut self, e1: &Expr, e2: &Expr, depth: usize) -> bool {
397        if depth > self.max_steps {
398            return false;
399        }
400        if e1 == e2 {
401            return true;
402        }
403        let whnf1 = self.reducer.whnf(e1);
404        let whnf2 = self.reducer.whnf(e2);
405        self.structural_eq(&whnf1, &whnf2, depth)
406    }
407    /// Implementation with depth tracking (with environment).
408    fn is_convertible_env_impl(
409        &mut self,
410        e1: &Expr,
411        e2: &Expr,
412        env: &Environment,
413        depth: usize,
414    ) -> bool {
415        if depth > self.max_steps {
416            return false;
417        }
418        if e1 == e2 {
419            return true;
420        }
421        let whnf1 = self.reducer.whnf_env(e1, env);
422        let whnf2 = self.reducer.whnf_env(e2, env);
423        self.structural_eq_env(&whnf1, &whnf2, env, depth)
424    }
425    /// Structural equality check after WHNF.
426    fn structural_eq(&mut self, e1: &Expr, e2: &Expr, depth: usize) -> bool {
427        if e1 == e2 {
428            return true;
429        }
430        match (e1, e2) {
431            (Expr::BVar(i1), Expr::BVar(i2)) => i1 == i2,
432            (Expr::FVar(f1), Expr::FVar(f2)) => f1 == f2,
433            (Expr::Const(n1, ls1), Expr::Const(n2, ls2)) => {
434                n1 == n2
435                    && ls1.len() == ls2.len()
436                    && ls1.iter().zip(ls2.iter()).all(|(a, b)| a == b)
437            }
438            (Expr::Sort(l1), Expr::Sort(l2)) => crate::level::is_equivalent(l1, l2),
439            (Expr::Lit(l1), Expr::Lit(l2)) => l1 == l2,
440            (Expr::App(f1, a1), Expr::App(f2, a2)) => {
441                self.is_convertible_impl(f1, f2, depth + 1)
442                    && self.is_convertible_impl(a1, a2, depth + 1)
443            }
444            (Expr::Lam(_, _, ty1, body1), Expr::Lam(_, _, ty2, body2)) => {
445                self.is_convertible_impl(ty1, ty2, depth + 1)
446                    && self.is_convertible_impl(body1, body2, depth + 1)
447            }
448            (Expr::Pi(_, _, ty1, body1), Expr::Pi(_, _, ty2, body2)) => {
449                self.is_convertible_impl(ty1, ty2, depth + 1)
450                    && self.is_convertible_impl(body1, body2, depth + 1)
451            }
452            (Expr::Let(_, ty1, val1, body1), Expr::Let(_, ty2, val2, body2)) => {
453                self.is_convertible_impl(ty1, ty2, depth + 1)
454                    && self.is_convertible_impl(val1, val2, depth + 1)
455                    && self.is_convertible_impl(body1, body2, depth + 1)
456            }
457            (Expr::Proj(n1, i1, e1), Expr::Proj(n2, i2, e2)) => {
458                n1 == n2 && i1 == i2 && self.is_convertible_impl(e1, e2, depth + 1)
459            }
460            _ => false,
461        }
462    }
463    /// Structural equality check with environment.
464    fn structural_eq_env(&mut self, e1: &Expr, e2: &Expr, env: &Environment, depth: usize) -> bool {
465        if e1 == e2 {
466            return true;
467        }
468        match (e1, e2) {
469            (Expr::BVar(i1), Expr::BVar(i2)) => i1 == i2,
470            (Expr::FVar(f1), Expr::FVar(f2)) => f1 == f2,
471            (Expr::Const(n1, ls1), Expr::Const(n2, ls2)) => {
472                n1 == n2
473                    && ls1.len() == ls2.len()
474                    && ls1.iter().zip(ls2.iter()).all(|(a, b)| a == b)
475            }
476            (Expr::Sort(l1), Expr::Sort(l2)) => crate::level::is_equivalent(l1, l2),
477            (Expr::Lit(l1), Expr::Lit(l2)) => l1 == l2,
478            (Expr::App(f1, a1), Expr::App(f2, a2)) => {
479                self.is_convertible_env_impl(f1, f2, env, depth + 1)
480                    && self.is_convertible_env_impl(a1, a2, env, depth + 1)
481            }
482            (Expr::Lam(_, _, ty1, body1), Expr::Lam(_, _, ty2, body2)) => {
483                self.is_convertible_env_impl(ty1, ty2, env, depth + 1)
484                    && self.is_convertible_env_impl(body1, body2, env, depth + 1)
485            }
486            (Expr::Pi(_, _, ty1, body1), Expr::Pi(_, _, ty2, body2)) => {
487                self.is_convertible_env_impl(ty1, ty2, env, depth + 1)
488                    && self.is_convertible_env_impl(body1, body2, env, depth + 1)
489            }
490            (Expr::Let(_, ty1, val1, body1), Expr::Let(_, ty2, val2, body2)) => {
491                self.is_convertible_env_impl(ty1, ty2, env, depth + 1)
492                    && self.is_convertible_env_impl(val1, val2, env, depth + 1)
493                    && self.is_convertible_env_impl(body1, body2, env, depth + 1)
494            }
495            (Expr::Proj(n1, i1, e1), Expr::Proj(n2, i2, e2)) => {
496                n1 == n2 && i1 == i2 && self.is_convertible_env_impl(e1, e2, env, depth + 1)
497            }
498            _ => false,
499        }
500    }
501    /// Set the maximum number of reduction steps.
502    pub fn set_max_steps(&mut self, max: usize) {
503        self.max_steps = max;
504    }
505    /// Get the maximum number of reduction steps.
506    pub fn max_steps(&self) -> usize {
507        self.max_steps
508    }
509}
510/// A simple key-value store backed by a sorted Vec for small maps.
511#[allow(dead_code)]
512pub struct SmallMap<K: Ord + Clone, V: Clone> {
513    entries: Vec<(K, V)>,
514}
515#[allow(dead_code)]
516impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
517    /// Creates a new empty small map.
518    pub fn new() -> Self {
519        Self {
520            entries: Vec::new(),
521        }
522    }
523    /// Inserts or replaces the value for `key`.
524    pub fn insert(&mut self, key: K, val: V) {
525        match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
526            Ok(i) => self.entries[i].1 = val,
527            Err(i) => self.entries.insert(i, (key, val)),
528        }
529    }
530    /// Returns the value for `key`, or `None`.
531    pub fn get(&self, key: &K) -> Option<&V> {
532        self.entries
533            .binary_search_by_key(&key, |(k, _)| k)
534            .ok()
535            .map(|i| &self.entries[i].1)
536    }
537    /// Returns the number of entries.
538    pub fn len(&self) -> usize {
539        self.entries.len()
540    }
541    /// Returns `true` if empty.
542    pub fn is_empty(&self) -> bool {
543        self.entries.is_empty()
544    }
545    /// Returns all keys.
546    pub fn keys(&self) -> Vec<&K> {
547        self.entries.iter().map(|(k, _)| k).collect()
548    }
549    /// Returns all values.
550    pub fn values(&self) -> Vec<&V> {
551        self.entries.iter().map(|(_, v)| v).collect()
552    }
553}
554/// A set of rewrite rules.
555#[allow(dead_code)]
556pub struct RewriteRuleSet {
557    rules: Vec<RewriteRule>,
558}
559#[allow(dead_code)]
560impl RewriteRuleSet {
561    /// Creates an empty rule set.
562    pub fn new() -> Self {
563        Self { rules: Vec::new() }
564    }
565    /// Adds a rule.
566    pub fn add(&mut self, rule: RewriteRule) {
567        self.rules.push(rule);
568    }
569    /// Returns the number of rules.
570    pub fn len(&self) -> usize {
571        self.rules.len()
572    }
573    /// Returns `true` if the set is empty.
574    pub fn is_empty(&self) -> bool {
575        self.rules.is_empty()
576    }
577    /// Returns all conditional rules.
578    pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
579        self.rules.iter().filter(|r| r.conditional).collect()
580    }
581    /// Returns all unconditional rules.
582    pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
583        self.rules.iter().filter(|r| !r.conditional).collect()
584    }
585    /// Looks up a rule by name.
586    pub fn get(&self, name: &str) -> Option<&RewriteRule> {
587        self.rules.iter().find(|r| r.name == name)
588    }
589}
590/// Coercion descriptor: maps a source type to a target type via a coercion function.
591#[derive(Debug, Clone)]
592#[allow(dead_code)]
593pub struct Coercion {
594    /// Name of the coercion function.
595    pub name: crate::Name,
596    /// Source type (the type being coerced from).
597    pub source: crate::Expr,
598    /// Target type (the type being coerced to).
599    pub target: crate::Expr,
600    /// Priority (higher = preferred when multiple coercions apply).
601    pub priority: i32,
602}
603/// A non-empty list (at least one element guaranteed).
604#[allow(dead_code)]
605pub struct NonEmptyVec<T> {
606    head: T,
607    tail: Vec<T>,
608}
609#[allow(dead_code)]
610impl<T> NonEmptyVec<T> {
611    /// Creates a non-empty vec with a single element.
612    pub fn singleton(val: T) -> Self {
613        Self {
614            head: val,
615            tail: Vec::new(),
616        }
617    }
618    /// Pushes an element.
619    pub fn push(&mut self, val: T) {
620        self.tail.push(val);
621    }
622    /// Returns a reference to the first element.
623    pub fn first(&self) -> &T {
624        &self.head
625    }
626    /// Returns a reference to the last element.
627    pub fn last(&self) -> &T {
628        self.tail.last().unwrap_or(&self.head)
629    }
630    /// Returns the number of elements.
631    pub fn len(&self) -> usize {
632        1 + self.tail.len()
633    }
634    /// Returns whether the collection is empty.
635    pub fn is_empty(&self) -> bool {
636        self.len() == 0
637    }
638    /// Returns all elements as a Vec.
639    pub fn to_vec(&self) -> Vec<&T> {
640        let mut v = vec![&self.head];
641        v.extend(self.tail.iter());
642        v
643    }
644}
645/// A min-heap implemented as a binary heap.
646#[allow(dead_code)]
647pub struct MinHeap<T: Ord> {
648    data: Vec<T>,
649}
650#[allow(dead_code)]
651impl<T: Ord> MinHeap<T> {
652    /// Creates a new empty min-heap.
653    pub fn new() -> Self {
654        Self { data: Vec::new() }
655    }
656    /// Inserts an element.
657    pub fn push(&mut self, val: T) {
658        self.data.push(val);
659        self.sift_up(self.data.len() - 1);
660    }
661    /// Removes and returns the minimum element.
662    pub fn pop(&mut self) -> Option<T> {
663        if self.data.is_empty() {
664            return None;
665        }
666        let n = self.data.len();
667        self.data.swap(0, n - 1);
668        let min = self.data.pop();
669        if !self.data.is_empty() {
670            self.sift_down(0);
671        }
672        min
673    }
674    /// Returns a reference to the minimum element.
675    pub fn peek(&self) -> Option<&T> {
676        self.data.first()
677    }
678    /// Returns the number of elements.
679    pub fn len(&self) -> usize {
680        self.data.len()
681    }
682    /// Returns `true` if empty.
683    pub fn is_empty(&self) -> bool {
684        self.data.is_empty()
685    }
686    fn sift_up(&mut self, mut i: usize) {
687        while i > 0 {
688            let parent = (i - 1) / 2;
689            if self.data[i] < self.data[parent] {
690                self.data.swap(i, parent);
691                i = parent;
692            } else {
693                break;
694            }
695        }
696    }
697    fn sift_down(&mut self, mut i: usize) {
698        let n = self.data.len();
699        loop {
700            let left = 2 * i + 1;
701            let right = 2 * i + 2;
702            let mut smallest = i;
703            if left < n && self.data[left] < self.data[smallest] {
704                smallest = left;
705            }
706            if right < n && self.data[right] < self.data[smallest] {
707                smallest = right;
708            }
709            if smallest == i {
710                break;
711            }
712            self.data.swap(i, smallest);
713            i = smallest;
714        }
715    }
716}
717/// A fixed-size sliding window that computes a running sum.
718#[allow(dead_code)]
719pub struct SlidingSum {
720    window: Vec<f64>,
721    capacity: usize,
722    pos: usize,
723    sum: f64,
724    count: usize,
725}
726#[allow(dead_code)]
727impl SlidingSum {
728    /// Creates a sliding sum with the given window size.
729    pub fn new(capacity: usize) -> Self {
730        Self {
731            window: vec![0.0; capacity],
732            capacity,
733            pos: 0,
734            sum: 0.0,
735            count: 0,
736        }
737    }
738    /// Adds a value to the window, removing the oldest if full.
739    pub fn push(&mut self, val: f64) {
740        let oldest = self.window[self.pos];
741        self.sum -= oldest;
742        self.sum += val;
743        self.window[self.pos] = val;
744        self.pos = (self.pos + 1) % self.capacity;
745        if self.count < self.capacity {
746            self.count += 1;
747        }
748    }
749    /// Returns the current window sum.
750    pub fn sum(&self) -> f64 {
751        self.sum
752    }
753    /// Returns the window mean, or `None` if empty.
754    pub fn mean(&self) -> Option<f64> {
755        if self.count == 0 {
756            None
757        } else {
758            Some(self.sum / self.count as f64)
759        }
760    }
761    /// Returns the current window size (number of valid elements).
762    pub fn count(&self) -> usize {
763        self.count
764    }
765}
766/// A simple stack-based calculator for arithmetic expressions.
767#[allow(dead_code)]
768pub struct StackCalc {
769    stack: Vec<i64>,
770}
771#[allow(dead_code)]
772impl StackCalc {
773    /// Creates a new empty calculator.
774    pub fn new() -> Self {
775        Self { stack: Vec::new() }
776    }
777    /// Pushes an integer literal.
778    pub fn push(&mut self, n: i64) {
779        self.stack.push(n);
780    }
781    /// Replaces the top two values, `b` on top of `a`, with `op(a, b)` and
782    /// returns it.  Returns `None`, leaving the stack unchanged, if it holds
783    /// fewer than two values.
784    fn apply_binary(&mut self, op: impl FnOnce(i64, i64) -> i64) -> Option<i64> {
785        let (rest_len, a, b) = match self.stack[..] {
786            [ref rest @ .., a, b] => (rest.len(), a, b),
787            _ => return None,
788        };
789        let value = op(a, b);
790        self.stack.truncate(rest_len);
791        self.stack.push(value);
792        Some(value)
793    }
794    /// Adds the top two values and returns the sum, which replaces them.
795    /// Returns `None`, leaving the stack unchanged, if it holds fewer than
796    /// two values.
797    pub fn add(&mut self) -> Option<i64> {
798        self.apply_binary(|a, b| a + b)
799    }
800    /// Subtracts top from second and returns the difference, which replaces
801    /// them.  Returns `None`, leaving the stack unchanged, if it holds fewer
802    /// than two values.
803    pub fn sub(&mut self) -> Option<i64> {
804        self.apply_binary(|a, b| a - b)
805    }
806    /// Multiplies the top two values and returns the product, which replaces
807    /// them.  Returns `None`, leaving the stack unchanged, if it holds fewer
808    /// than two values.
809    pub fn mul(&mut self) -> Option<i64> {
810        self.apply_binary(|a, b| a * b)
811    }
812    /// Peeks the top value.
813    pub fn peek(&self) -> Option<i64> {
814        self.stack.last().copied()
815    }
816    /// Returns the stack depth.
817    pub fn depth(&self) -> usize {
818        self.stack.len()
819    }
820}
821/// A flat list of substitution pairs `(from, to)`.
822#[allow(dead_code)]
823pub struct FlatSubstitution {
824    pairs: Vec<(String, String)>,
825}
826#[allow(dead_code)]
827impl FlatSubstitution {
828    /// Creates an empty substitution.
829    pub fn new() -> Self {
830        Self { pairs: Vec::new() }
831    }
832    /// Adds a pair.
833    pub fn add(&mut self, from: impl Into<String>, to: impl Into<String>) {
834        self.pairs.push((from.into(), to.into()));
835    }
836    /// Applies all substitutions to `s` (leftmost-first order).
837    pub fn apply(&self, s: &str) -> String {
838        let mut result = s.to_string();
839        for (from, to) in &self.pairs {
840            result = result.replace(from.as_str(), to.as_str());
841        }
842        result
843    }
844    /// Returns the number of pairs.
845    pub fn len(&self) -> usize {
846        self.pairs.len()
847    }
848    /// Returns `true` if empty.
849    pub fn is_empty(&self) -> bool {
850        self.pairs.is_empty()
851    }
852}
853/// A generic counter that tracks min/max/sum for statistical summaries.
854#[allow(dead_code)]
855pub struct StatSummary {
856    count: u64,
857    sum: f64,
858    min: f64,
859    max: f64,
860}
861#[allow(dead_code)]
862impl StatSummary {
863    /// Creates an empty summary.
864    pub fn new() -> Self {
865        Self {
866            count: 0,
867            sum: 0.0,
868            min: f64::INFINITY,
869            max: f64::NEG_INFINITY,
870        }
871    }
872    /// Records a sample.
873    pub fn record(&mut self, val: f64) {
874        self.count += 1;
875        self.sum += val;
876        if val < self.min {
877            self.min = val;
878        }
879        if val > self.max {
880            self.max = val;
881        }
882    }
883    /// Returns the mean, or `None` if no samples.
884    pub fn mean(&self) -> Option<f64> {
885        if self.count == 0 {
886            None
887        } else {
888            Some(self.sum / self.count as f64)
889        }
890    }
891    /// Returns the minimum, or `None` if no samples.
892    pub fn min(&self) -> Option<f64> {
893        if self.count == 0 {
894            None
895        } else {
896            Some(self.min)
897        }
898    }
899    /// Returns the maximum, or `None` if no samples.
900    pub fn max(&self) -> Option<f64> {
901        if self.count == 0 {
902            None
903        } else {
904            Some(self.max)
905        }
906    }
907    /// Returns the count of recorded samples.
908    pub fn count(&self) -> u64 {
909        self.count
910    }
911}
912/// A dependency closure builder (transitive closure via BFS).
913#[allow(dead_code)]
914pub struct TransitiveClosure {
915    adj: Vec<Vec<usize>>,
916    n: usize,
917}
918#[allow(dead_code)]
919impl TransitiveClosure {
920    /// Creates a transitive closure builder for `n` nodes.
921    pub fn new(n: usize) -> Self {
922        Self {
923            adj: vec![Vec::new(); n],
924            n,
925        }
926    }
927    /// Adds a direct edge.
928    pub fn add_edge(&mut self, from: usize, to: usize) {
929        if from < self.n {
930            self.adj[from].push(to);
931        }
932    }
933    /// Computes all nodes reachable from `start` (including `start`).
934    pub fn reachable_from(&self, start: usize) -> Vec<usize> {
935        let mut visited = vec![false; self.n];
936        let mut queue = std::collections::VecDeque::new();
937        queue.push_back(start);
938        while let Some(node) = queue.pop_front() {
939            if node >= self.n || visited[node] {
940                continue;
941            }
942            visited[node] = true;
943            for &next in &self.adj[node] {
944                queue.push_back(next);
945            }
946        }
947        (0..self.n).filter(|&i| visited[i]).collect()
948    }
949    /// Returns `true` if `from` can transitively reach `to`.
950    pub fn can_reach(&self, from: usize, to: usize) -> bool {
951        self.reachable_from(from).contains(&to)
952    }
953}
954/// A reusable scratch buffer for path computations.
955#[allow(dead_code)]
956pub struct PathBuf {
957    components: Vec<String>,
958}
959#[allow(dead_code)]
960impl PathBuf {
961    /// Creates a new empty path buffer.
962    pub fn new() -> Self {
963        Self {
964            components: Vec::new(),
965        }
966    }
967    /// Pushes a component.
968    pub fn push(&mut self, comp: impl Into<String>) {
969        self.components.push(comp.into());
970    }
971    /// Pops the last component.
972    pub fn pop(&mut self) {
973        self.components.pop();
974    }
975    /// Returns the current path as a `/`-separated string.
976    pub fn as_str(&self) -> String {
977        self.components.join("/")
978    }
979    /// Returns the depth of the path.
980    pub fn depth(&self) -> usize {
981        self.components.len()
982    }
983    /// Clears the path.
984    pub fn clear(&mut self) {
985        self.components.clear();
986    }
987}
988/// A sparse vector: stores only non-default elements.
989#[allow(dead_code)]
990pub struct SparseVec<T: Default + Clone + PartialEq> {
991    entries: std::collections::HashMap<usize, T>,
992    default_: T,
993    logical_len: usize,
994}
995#[allow(dead_code)]
996impl<T: Default + Clone + PartialEq> SparseVec<T> {
997    /// Creates a new sparse vector with logical length `len`.
998    pub fn new(len: usize) -> Self {
999        Self {
1000            entries: std::collections::HashMap::new(),
1001            default_: T::default(),
1002            logical_len: len,
1003        }
1004    }
1005    /// Sets element at `idx`.
1006    pub fn set(&mut self, idx: usize, val: T) {
1007        if val == self.default_ {
1008            self.entries.remove(&idx);
1009        } else {
1010            self.entries.insert(idx, val);
1011        }
1012    }
1013    /// Gets element at `idx`.
1014    pub fn get(&self, idx: usize) -> &T {
1015        self.entries.get(&idx).unwrap_or(&self.default_)
1016    }
1017    /// Returns the logical length.
1018    pub fn len(&self) -> usize {
1019        self.logical_len
1020    }
1021    /// Returns whether the collection is empty.
1022    pub fn is_empty(&self) -> bool {
1023        self.len() == 0
1024    }
1025    /// Returns the number of non-default elements.
1026    pub fn nnz(&self) -> usize {
1027        self.entries.len()
1028    }
1029}
1030/// A window iterator that yields overlapping windows of size `n`.
1031#[allow(dead_code)]
1032pub struct WindowIterator<'a, T> {
1033    pub(super) data: &'a [T],
1034    pub(super) pos: usize,
1035    pub(super) window: usize,
1036}
1037#[allow(dead_code)]
1038impl<'a, T> WindowIterator<'a, T> {
1039    /// Creates a new window iterator.
1040    pub fn new(data: &'a [T], window: usize) -> Self {
1041        Self {
1042            data,
1043            pos: 0,
1044            window,
1045        }
1046    }
1047}
1048/// A pool of reusable string buffers.
1049#[allow(dead_code)]
1050pub struct StringPool {
1051    free: Vec<String>,
1052}
1053#[allow(dead_code)]
1054impl StringPool {
1055    /// Creates a new empty string pool.
1056    pub fn new() -> Self {
1057        Self { free: Vec::new() }
1058    }
1059    /// Takes a string from the pool (may be empty).
1060    pub fn take(&mut self) -> String {
1061        self.free.pop().unwrap_or_default()
1062    }
1063    /// Returns a string to the pool.
1064    pub fn give(&mut self, mut s: String) {
1065        s.clear();
1066        self.free.push(s);
1067    }
1068    /// Returns the number of free strings in the pool.
1069    pub fn free_count(&self) -> usize {
1070        self.free.len()
1071    }
1072}
1073/// A small fixed-size set implemented as a bit array.
1074#[allow(dead_code)]
1075pub struct BitSet64 {
1076    bits: u64,
1077}
1078#[allow(dead_code)]
1079impl BitSet64 {
1080    /// Creates an empty set.
1081    pub const fn new() -> Self {
1082        Self { bits: 0 }
1083    }
1084    /// Inserts element `i` (0–63).
1085    pub fn insert(&mut self, i: u32) {
1086        if i < 64 {
1087            self.bits |= 1u64 << i;
1088        }
1089    }
1090    /// Removes element `i`.
1091    pub fn remove(&mut self, i: u32) {
1092        if i < 64 {
1093            self.bits &= !(1u64 << i);
1094        }
1095    }
1096    /// Returns `true` if `i` is in the set.
1097    pub fn contains(&self, i: u32) -> bool {
1098        i < 64 && (self.bits >> i) & 1 != 0
1099    }
1100    /// Returns the cardinality.
1101    pub fn len(&self) -> u32 {
1102        self.bits.count_ones()
1103    }
1104    /// Returns `true` if empty.
1105    pub fn is_empty(&self) -> bool {
1106        self.bits == 0
1107    }
1108    /// Returns the union with `other`.
1109    pub fn union(self, other: BitSet64) -> BitSet64 {
1110        BitSet64 {
1111            bits: self.bits | other.bits,
1112        }
1113    }
1114    /// Returns the intersection with `other`.
1115    pub fn intersect(self, other: BitSet64) -> BitSet64 {
1116        BitSet64 {
1117            bits: self.bits & other.bits,
1118        }
1119    }
1120}
1121/// A trie-based prefix counter.
1122#[allow(dead_code)]
1123pub struct PrefixCounter {
1124    children: std::collections::HashMap<char, PrefixCounter>,
1125    count: usize,
1126}
1127#[allow(dead_code)]
1128impl PrefixCounter {
1129    /// Creates an empty prefix counter.
1130    pub fn new() -> Self {
1131        Self {
1132            children: std::collections::HashMap::new(),
1133            count: 0,
1134        }
1135    }
1136    /// Records a string.
1137    pub fn record(&mut self, s: &str) {
1138        self.count += 1;
1139        let mut node = self;
1140        for c in s.chars() {
1141            node = node.children.entry(c).or_default();
1142            node.count += 1;
1143        }
1144    }
1145    /// Returns how many strings have been recorded that start with `prefix`.
1146    pub fn count_with_prefix(&self, prefix: &str) -> usize {
1147        let mut node = self;
1148        for c in prefix.chars() {
1149            match node.children.get(&c) {
1150                Some(n) => node = n,
1151                None => return 0,
1152            }
1153        }
1154        node.count
1155    }
1156}
1157/// Represents a rewrite rule `lhs → rhs`.
1158#[allow(dead_code)]
1159#[allow(missing_docs)]
1160pub struct RewriteRule {
1161    /// The name of the rule.
1162    pub name: String,
1163    /// A string representation of the LHS pattern.
1164    pub lhs: String,
1165    /// A string representation of the RHS.
1166    pub rhs: String,
1167    /// Whether this is a conditional rule (has side conditions).
1168    pub conditional: bool,
1169}
1170#[allow(dead_code)]
1171impl RewriteRule {
1172    /// Creates an unconditional rewrite rule.
1173    pub fn unconditional(
1174        name: impl Into<String>,
1175        lhs: impl Into<String>,
1176        rhs: impl Into<String>,
1177    ) -> Self {
1178        Self {
1179            name: name.into(),
1180            lhs: lhs.into(),
1181            rhs: rhs.into(),
1182            conditional: false,
1183        }
1184    }
1185    /// Creates a conditional rewrite rule.
1186    pub fn conditional(
1187        name: impl Into<String>,
1188        lhs: impl Into<String>,
1189        rhs: impl Into<String>,
1190    ) -> Self {
1191        Self {
1192            name: name.into(),
1193            lhs: lhs.into(),
1194            rhs: rhs.into(),
1195            conditional: true,
1196        }
1197    }
1198    /// Returns a textual representation.
1199    pub fn display(&self) -> String {
1200        format!("{}: {} → {}", self.name, self.lhs, self.rhs)
1201    }
1202}
1203/// Registry of registered coercions.
1204#[derive(Default)]
1205#[allow(dead_code)]
1206pub struct CoercionTable {
1207    coercions: Vec<Coercion>,
1208}
1209impl CoercionTable {
1210    /// Create an empty coercion table.
1211    pub fn new() -> Self {
1212        Self {
1213            coercions: Vec::new(),
1214        }
1215    }
1216    /// Register a new coercion.
1217    pub fn register(&mut self, coercion: Coercion) {
1218        let pos = self
1219            .coercions
1220            .partition_point(|c| c.priority >= coercion.priority);
1221        self.coercions.insert(pos, coercion);
1222    }
1223    /// Find coercions applicable to a given source type.
1224    pub fn find(&self, source_head: &crate::Name) -> Vec<&Coercion> {
1225        self.coercions
1226            .iter()
1227            .filter(|c| coercion_head_matches(&c.source, source_head))
1228            .collect()
1229    }
1230    /// Number of registered coercions.
1231    pub fn len(&self) -> usize {
1232        self.coercions.len()
1233    }
1234    /// Check if the table is empty.
1235    pub fn is_empty(&self) -> bool {
1236        self.coercions.is_empty()
1237    }
1238    /// Remove all coercions.
1239    pub fn clear(&mut self) {
1240        self.coercions.clear();
1241    }
1242}
1243/// A tagged union for representing a simple two-case discriminated union.
1244#[allow(dead_code)]
1245pub enum Either2<A, B> {
1246    /// The first alternative.
1247    First(A),
1248    /// The second alternative.
1249    Second(B),
1250}
1251#[allow(dead_code)]
1252impl<A, B> Either2<A, B> {
1253    /// Returns `true` if this is the first alternative.
1254    pub fn is_first(&self) -> bool {
1255        matches!(self, Either2::First(_))
1256    }
1257    /// Returns `true` if this is the second alternative.
1258    pub fn is_second(&self) -> bool {
1259        matches!(self, Either2::Second(_))
1260    }
1261    /// Returns the first value if present.
1262    pub fn first(self) -> Option<A> {
1263        match self {
1264            Either2::First(a) => Some(a),
1265            _ => None,
1266        }
1267    }
1268    /// Returns the second value if present.
1269    pub fn second(self) -> Option<B> {
1270        match self {
1271            Either2::Second(b) => Some(b),
1272            _ => None,
1273        }
1274    }
1275    /// Maps over the first alternative.
1276    pub fn map_first<C, F: FnOnce(A) -> C>(self, f: F) -> Either2<C, B> {
1277        match self {
1278            Either2::First(a) => Either2::First(f(a)),
1279            Either2::Second(b) => Either2::Second(b),
1280        }
1281    }
1282}
1283/// A mutable reference stack for tracking the current "focus" in a tree traversal.
1284#[allow(dead_code)]
1285pub struct FocusStack<T> {
1286    items: Vec<T>,
1287}
1288#[allow(dead_code)]
1289impl<T> FocusStack<T> {
1290    /// Creates an empty focus stack.
1291    pub fn new() -> Self {
1292        Self { items: Vec::new() }
1293    }
1294    /// Focuses on `item`.
1295    pub fn focus(&mut self, item: T) {
1296        self.items.push(item);
1297    }
1298    /// Blurs (pops) the current focus.
1299    pub fn blur(&mut self) -> Option<T> {
1300        self.items.pop()
1301    }
1302    /// Returns the current focus, or `None`.
1303    pub fn current(&self) -> Option<&T> {
1304        self.items.last()
1305    }
1306    /// Returns the focus depth.
1307    pub fn depth(&self) -> usize {
1308        self.items.len()
1309    }
1310    /// Returns `true` if there is no current focus.
1311    pub fn is_empty(&self) -> bool {
1312        self.items.is_empty()
1313    }
1314}
1315/// A type-erased function pointer with arity tracking.
1316#[allow(dead_code)]
1317pub struct RawFnPtr {
1318    /// The raw function pointer (stored as usize for type erasure).
1319    ptr: usize,
1320    arity: usize,
1321    name: String,
1322}
1323#[allow(dead_code)]
1324impl RawFnPtr {
1325    /// Creates a new raw function pointer descriptor.
1326    pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
1327        Self {
1328            ptr,
1329            arity,
1330            name: name.into(),
1331        }
1332    }
1333    /// Returns the arity.
1334    pub fn arity(&self) -> usize {
1335        self.arity
1336    }
1337    /// Returns the name.
1338    pub fn name(&self) -> &str {
1339        &self.name
1340    }
1341    /// Returns the raw pointer value.
1342    pub fn raw(&self) -> usize {
1343        self.ptr
1344    }
1345}
1346/// A counter that can measure elapsed time between snapshots.
1347#[allow(dead_code)]
1348pub struct Stopwatch {
1349    start: crate::wall_clock::Instant,
1350    splits: Vec<f64>,
1351}
1352#[allow(dead_code)]
1353impl Stopwatch {
1354    /// Creates and starts a new stopwatch.
1355    pub fn start() -> Self {
1356        Self {
1357            start: crate::wall_clock::Instant::now(),
1358            splits: Vec::new(),
1359        }
1360    }
1361    /// Records a split time (elapsed since start).
1362    pub fn split(&mut self) {
1363        self.splits.push(self.elapsed_ms());
1364    }
1365    /// Returns total elapsed milliseconds since start.
1366    pub fn elapsed_ms(&self) -> f64 {
1367        self.start.elapsed().as_secs_f64() * 1000.0
1368    }
1369    /// Returns all recorded split times.
1370    pub fn splits(&self) -> &[f64] {
1371        &self.splits
1372    }
1373    /// Returns the number of splits.
1374    pub fn num_splits(&self) -> usize {
1375        self.splits.len()
1376    }
1377}
1378/// A simple mutable key-value store for test fixtures.
1379#[allow(dead_code)]
1380pub struct Fixture {
1381    data: std::collections::HashMap<String, String>,
1382}
1383#[allow(dead_code)]
1384impl Fixture {
1385    /// Creates an empty fixture.
1386    pub fn new() -> Self {
1387        Self {
1388            data: std::collections::HashMap::new(),
1389        }
1390    }
1391    /// Sets a key.
1392    pub fn set(&mut self, key: impl Into<String>, val: impl Into<String>) {
1393        self.data.insert(key.into(), val.into());
1394    }
1395    /// Gets a value.
1396    pub fn get(&self, key: &str) -> Option<&str> {
1397        self.data.get(key).map(|s| s.as_str())
1398    }
1399    /// Returns the number of entries.
1400    pub fn len(&self) -> usize {
1401        self.data.len()
1402    }
1403    /// Returns whether the collection is empty.
1404    pub fn is_empty(&self) -> bool {
1405        self.len() == 0
1406    }
1407}
1408/// A pair of `StatSummary` values tracking before/after a transformation.
1409#[allow(dead_code)]
1410pub struct TransformStat {
1411    before: StatSummary,
1412    after: StatSummary,
1413}
1414#[allow(dead_code)]
1415impl TransformStat {
1416    /// Creates a new transform stat recorder.
1417    pub fn new() -> Self {
1418        Self {
1419            before: StatSummary::new(),
1420            after: StatSummary::new(),
1421        }
1422    }
1423    /// Records a before value.
1424    pub fn record_before(&mut self, v: f64) {
1425        self.before.record(v);
1426    }
1427    /// Records an after value.
1428    pub fn record_after(&mut self, v: f64) {
1429        self.after.record(v);
1430    }
1431    /// Returns the mean reduction ratio (after/before).
1432    pub fn mean_ratio(&self) -> Option<f64> {
1433        let b = self.before.mean()?;
1434        let a = self.after.mean()?;
1435        if b.abs() < f64::EPSILON {
1436            return None;
1437        }
1438        Some(a / b)
1439    }
1440}
1441/// A counter for tracking how many items are in each of `N` buckets.
1442#[allow(dead_code)]
1443pub struct BucketCounter<const N: usize> {
1444    buckets: [u64; N],
1445}
1446#[allow(dead_code)]
1447impl<const N: usize> BucketCounter<N> {
1448    /// Creates a zeroed bucket counter.
1449    pub const fn new() -> Self {
1450        Self { buckets: [0u64; N] }
1451    }
1452    /// Increments bucket `i`.
1453    pub fn inc(&mut self, i: usize) {
1454        if i < N {
1455            self.buckets[i] += 1;
1456        }
1457    }
1458    /// Returns the count for bucket `i`.
1459    pub fn get(&self, i: usize) -> u64 {
1460        if i < N {
1461            self.buckets[i]
1462        } else {
1463            0
1464        }
1465    }
1466    /// Returns the total count across all buckets.
1467    pub fn total(&self) -> u64 {
1468        self.buckets.iter().sum()
1469    }
1470    /// Returns the index of the bucket with the highest count.
1471    pub fn argmax(&self) -> usize {
1472        self.buckets
1473            .iter()
1474            .enumerate()
1475            .max_by_key(|(_, &v)| v)
1476            .map(|(i, _)| i)
1477            .unwrap_or(0)
1478    }
1479}
1480/// A label set for a graph node.
1481#[allow(dead_code)]
1482pub struct LabelSet {
1483    labels: Vec<String>,
1484}
1485#[allow(dead_code)]
1486impl LabelSet {
1487    /// Creates a new empty label set.
1488    pub fn new() -> Self {
1489        Self { labels: Vec::new() }
1490    }
1491    /// Adds a label (deduplicates).
1492    pub fn add(&mut self, label: impl Into<String>) {
1493        let s = label.into();
1494        if !self.labels.contains(&s) {
1495            self.labels.push(s);
1496        }
1497    }
1498    /// Returns `true` if `label` is present.
1499    pub fn has(&self, label: &str) -> bool {
1500        self.labels.iter().any(|l| l == label)
1501    }
1502    /// Returns the count of labels.
1503    pub fn count(&self) -> usize {
1504        self.labels.len()
1505    }
1506    /// Returns all labels.
1507    pub fn all(&self) -> &[String] {
1508        &self.labels
1509    }
1510}