Skip to main content

ariadnetor_core/
label.rs

1//! Lightweight label identifier (interned string)
2
3use dashmap::DashMap;
4use once_cell::sync::Lazy;
5use std::sync::atomic::{AtomicU64, Ordering};
6
7/// Interned identifier for a tensor-leg label: a cheap `Copy` handle
8/// backed by a process-global string interner.
9#[repr(transparent)]
10#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
11pub struct LabelId(u64);
12
13static INTERNER: Lazy<LabelInterner> = Lazy::new(LabelInterner::default);
14
15struct LabelInterner {
16    name_to_id: DashMap<String, u64>,
17    id_to_name: DashMap<u64, String>,
18    next_id: AtomicU64,
19}
20
21impl Default for LabelInterner {
22    fn default() -> Self {
23        Self {
24            name_to_id: DashMap::new(),
25            id_to_name: DashMap::new(),
26            next_id: AtomicU64::new(1),
27        }
28    }
29}
30
31impl LabelId {
32    /// Return the id for `name`, interning it on first use; idempotent
33    /// for equal names.
34    pub fn intern(name: &str) -> Self {
35        if let Some(id) = INTERNER.name_to_id.get(name) {
36            return LabelId(*id);
37        }
38        let id = INTERNER.next_id.fetch_add(1, Ordering::SeqCst);
39        INTERNER.name_to_id.insert(name.to_string(), id);
40        INTERNER.id_to_name.insert(id, name.to_string());
41        LabelId(id)
42    }
43
44    /// Resolve this id back to its interned name.
45    pub fn name(&self) -> String {
46        INTERNER
47            .id_to_name
48            .get(&self.0)
49            .map(|e| e.value().clone())
50            .unwrap_or_else(|| format!("<unknown:{}>", self.0))
51    }
52
53    /// Mint a fresh, unique label with an internal temporary name.
54    pub fn fresh() -> Self {
55        let id = INTERNER.next_id.fetch_add(1, Ordering::SeqCst);
56        let name = format!("_tmp_{}", id);
57        INTERNER.name_to_id.insert(name.clone(), id);
58        INTERNER.id_to_name.insert(id, name);
59        LabelId(id)
60    }
61
62    /// Return the label with one prime (`'`) appended.
63    pub fn prime(&self) -> Self {
64        Self::intern(&format!("{}'", self.name()))
65    }
66
67    /// Return the label with `n` primes (`'`) appended.
68    pub fn primes(&self, n: usize) -> Self {
69        Self::intern(&format!("{}{}", self.name(), "'".repeat(n)))
70    }
71
72    /// Remove a single trailing prime, if present; otherwise return the label unchanged.
73    pub fn unprime(&self) -> Self {
74        let name = self.name();
75        if let Some(stripped) = name.strip_suffix('\'') {
76            Self::intern(stripped)
77        } else {
78            *self
79        }
80    }
81
82    /// Strip all trailing primes, returning the base (unprimed) label.
83    pub fn base(&self) -> Self {
84        Self::intern(self.name().trim_end_matches('\''))
85    }
86}
87
88/// Intern a [`LabelId`] from a string expression.
89#[macro_export]
90macro_rules! label {
91    ($name:expr) => {
92        $crate::LabelId::intern($name)
93    };
94}
95
96/// Mint a fresh, unique [`LabelId`].
97#[macro_export]
98macro_rules! fresh {
99    () => {
100        $crate::LabelId::fresh()
101    };
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn test_intern_idempotent() {
110        let id1 = LabelId::intern("i");
111        let id2 = LabelId::intern("i");
112        assert_eq!(id1, id2);
113    }
114
115    #[test]
116    fn test_prime_operations() {
117        let i = LabelId::intern("test_i");
118        assert_eq!(i.prime().name(), "test_i'");
119        assert_eq!(i.primes(2).name(), "test_i''");
120    }
121
122    #[test]
123    fn test_fresh_unique() {
124        let f1 = LabelId::fresh();
125        let f2 = LabelId::fresh();
126        assert_ne!(f1, f2);
127    }
128}