Skip to main content

cljrs_value/
value.rs

1use std::cmp::Ordering;
2use std::fmt;
3use std::sync::{Arc, Mutex};
4
5use crate::collections::{
6    PersistentArrayMap, PersistentHashMap, PersistentHashSet, PersistentList, PersistentQueue,
7    PersistentVector, SortedMap, SortedSet, TransientMap, TransientSet, TransientVector,
8};
9use crate::error::{ExceptionInfo, ValueError};
10use crate::hash::{
11    ClojureHash, hash_combine_ordered, hash_combine_unordered, hash_i64, hash_string, hash_u128,
12};
13use crate::keyword::Keyword;
14use crate::regex::{Matcher, Pattern};
15use crate::resource::ResourceHandle;
16use crate::shared::SharedAtom;
17use crate::symbol::Symbol;
18use crate::types::{
19    Agent, Atom, BoundFn, CljxCons, CljxFn, CljxFuture, CljxPromise, Delay, LazySeq, MultiFn,
20    Namespace, NativeFn, Protocol, ProtocolFn, Var, Volatile,
21};
22use cljrs_gc::{GcPtr, MarkVisitor, Trace};
23use num_bigint::BigInt;
24use num_traits::ToPrimitive;
25
26/// A GC-traced mutable array of Values (backs `object-array`).
27#[derive(Debug)]
28pub struct ObjectArray(pub Mutex<Vec<Value>>);
29
30impl ObjectArray {
31    pub fn new(v: Vec<Value>) -> Self {
32        Self(Mutex::new(v))
33    }
34}
35
36impl Trace for ObjectArray {
37    fn trace(&self, visitor: &mut MarkVisitor) {
38        {
39            let guard = self.0.lock().unwrap();
40            for v in guard.iter() {
41                v.trace(visitor);
42            }
43        }
44    }
45
46    fn gc_size_extra(&self) -> usize {
47        let guard = self.0.lock().unwrap();
48        guard.capacity() * std::mem::size_of::<Value>()
49    }
50}
51
52/// Heap representation of a UUID's 128-bit value.
53///
54/// UUID equality is value-based, while `identical?` compares the containing
55/// `GcPtr`; keeping the bits in a traced leaf provides both semantics.
56#[derive(Debug)]
57pub struct UuidValue(pub u128);
58
59impl Trace for UuidValue {
60    fn trace(&self, _: &mut MarkVisitor) {}
61}
62
63/// The central runtime type: every Clojure value is a `Value`.
64///
65/// Small scalars (`Nil`, `Bool`, `Long`, `Double`, `Char`) are stored inline.
66/// All heap-allocated types go behind `GcPtr` so that `clone()` is O(1).
67#[derive(Clone, Debug)]
68pub enum Value {
69    // ── Scalars ───────────────────────────────────────────────────────────────
70    Nil,
71    Bool(bool),
72    Long(i64),
73    Double(f64),
74    BigInt(GcPtr<BigInt>),
75    BigDecimal(GcPtr<bigdecimal::BigDecimal>),
76    Ratio(GcPtr<num_rational::Ratio<BigInt>>),
77    Char(char),
78    Str(GcPtr<String>),
79    Uuid(GcPtr<UuidValue>),
80    Pattern(GcPtr<Pattern>),
81    Matcher(GcPtr<Matcher>),
82
83    // ── Identifiers ───────────────────────────────────────────────────────────
84    Symbol(GcPtr<Symbol>),
85    Keyword(GcPtr<Keyword>),
86
87    // ── Collections ───────────────────────────────────────────────────────────
88    List(GcPtr<PersistentList>),
89    Vector(GcPtr<PersistentVector>),
90    /// Small maps (≤8 entries) are stored as an ArrayMap; larger ones as a HashMap.
91    /// Also contains sorted map.
92    Map(MapValue),
93    Set(SetValue),
94    Queue(GcPtr<PersistentQueue>),
95
96    // Transients
97    TransientMap(GcPtr<TransientMap>),
98    TransientSet(GcPtr<TransientSet>),
99    TransientVector(GcPtr<TransientVector>),
100
101    // Arrays
102    IntArray(GcPtr<Mutex<Vec<i32>>>),
103    LongArray(GcPtr<Mutex<Vec<i64>>>),
104    ShortArray(GcPtr<Mutex<Vec<i16>>>),
105    ByteArray(GcPtr<Mutex<Vec<i8>>>),
106    FloatArray(GcPtr<Mutex<Vec<f32>>>),
107    DoubleArray(GcPtr<Mutex<Vec<f64>>>),
108    BooleanArray(GcPtr<Mutex<Vec<bool>>>),
109    CharArray(GcPtr<Mutex<Vec<char>>>),
110    ObjectArray(GcPtr<ObjectArray>),
111
112    // ── Functions ─────────────────────────────────────────────────────────────
113    NativeFunction(GcPtr<NativeFn>),
114    Fn(GcPtr<CljxFn>),
115    Macro(GcPtr<CljxFn>),
116    BoundFn(GcPtr<BoundFn>),
117
118    // ── Mutable state ─────────────────────────────────────────────────────────
119    Var(GcPtr<Var>),
120    Atom(GcPtr<Atom>),
121    /// Cross-isolate mutable reference backed by `Arc<ArcSwap<SharedValue>>`.
122    /// Unlike `Atom`, a `SharedAtom` can cross the isolate boundary (the `Arc`
123    /// is cloned, not deep-copied) and its contents are lock-free CAS-swapped.
124    SharedAtom(std::sync::Arc<SharedAtom>),
125    /// Immutable refcounted byte buffer (the BEAM off-heap-binary trick).
126    /// Shared without copy across the isolate boundary; refcount drops to zero
127    /// only when every isolate releases its reference.
128    ByteBlob(std::sync::Arc<[u8]>),
129
130    // ── Reduced (early termination sentinel for reduce/transduce) ───────────
131    Reduced(Box<Value>),
132
133    // ── Other ─────────────────────────────────────────────────────────────────
134    Namespace(GcPtr<Namespace>),
135
136    // ── Lazy sequences ────────────────────────────────────────────────────────
137    /// A deferred sequence cell — realized at most once.
138    LazySeq(GcPtr<LazySeq>),
139    /// A realized cons cell whose tail may itself be lazy.
140    Cons(GcPtr<CljxCons>),
141
142    // ── Protocols & Multimethods ──────────────────────────────────────────────
143    Protocol(GcPtr<Protocol>),
144    ProtocolFn(GcPtr<ProtocolFn>),
145    MultiFn(GcPtr<MultiFn>),
146
147    // ── Concurrency primitives ────────────────────────────────────────────────
148    Volatile(GcPtr<Volatile>),
149    Delay(GcPtr<Delay>),
150    Promise(GcPtr<CljxPromise>),
151    Future(GcPtr<CljxFuture>),
152    Agent(GcPtr<Agent>),
153
154    // ── Records / reify instances ─────────────────────────────────────────────
155    TypeInstance(GcPtr<TypeInstance>),
156
157    // ── Native Rust objects (GcPtr-managed, for interop) ─────────────────────
158    NativeObject(GcPtr<crate::native_object::NativeObjectBox>),
159
160    // ── I/O resources (Arc-ref-counted, NOT GcPtr) ───────────────────────────
161    Resource(ResourceHandle),
162
163    // ── Metadata wrapper ─────────────────────────────────────────────────────
164    /// A value with attached metadata. Transparent for equality, hashing, display.
165    WithMeta(Box<Value>, Box<Value>),
166
167    // Errors
168    Error(GcPtr<ExceptionInfo>),
169}
170
171/// A map value: either a small array-map or a HAMT-based hash-map.
172#[derive(Clone, Debug)]
173pub enum MapValue {
174    Array(GcPtr<PersistentArrayMap>),
175    Hash(GcPtr<PersistentHashMap>),
176    Sorted(GcPtr<SortedMap>),
177}
178
179impl MapValue {
180    pub fn empty() -> Self {
181        MapValue::Array(GcPtr::new(PersistentArrayMap::empty()))
182    }
183
184    /// Build a map from pre-evaluated key-value pairs.
185    ///
186    /// Chooses the optimal representation based on size: ArrayMap for small
187    /// maps (≤8 entries), HashTrieMap for larger ones. This avoids N
188    /// intermediate allocations that `empty() + assoc + assoc + ...` would
189    /// create.
190    pub fn from_pairs(pairs: Vec<(Value, Value)>) -> Self {
191        use crate::collections::array_map::AssocResult;
192
193        // Check for duplicates by building through assoc (last wins).
194        match PersistentArrayMap::from_pairs(pairs) {
195            AssocResult::Array(m) => MapValue::Array(GcPtr::new(m)),
196            AssocResult::Promote(pairs) => {
197                MapValue::Hash(GcPtr::new(PersistentHashMap::from_pairs(pairs)))
198            }
199        }
200    }
201
202    /// Build a map from a flat evaluated entries vector `[k0, v0, k1, v1, ...]`.
203    ///
204    /// Similar to `from_pairs` but takes flat key-value entries. Handles
205    /// duplicate keys (last wins via assoc). Avoids intermediate allocations.
206    pub fn from_flat_entries(entries: Vec<Value>) -> Self {
207        debug_assert!(entries.len().is_multiple_of(2));
208        // We need to handle duplicate keys, so build through assoc.
209        let pairs: Vec<(Value, Value)> = entries
210            .chunks(2)
211            .map(|chunk| (chunk[0].clone(), chunk[1].clone()))
212            .collect();
213        Self::from_pairs(pairs)
214    }
215
216    pub fn get(&self, key: &Value) -> Option<Value> {
217        match self {
218            MapValue::Array(m) => m.get().get(key).cloned(),
219            MapValue::Hash(m) => m.get().get(key).cloned(),
220            MapValue::Sorted(m) => m.get().get(key).cloned(),
221        }
222    }
223
224    pub fn count(&self) -> usize {
225        match self {
226            MapValue::Array(m) => m.get().count(),
227            MapValue::Hash(m) => m.get().count(),
228            MapValue::Sorted(m) => m.get().count(),
229        }
230    }
231
232    pub fn assoc(&self, k: Value, v: Value) -> Self {
233        match self {
234            MapValue::Array(m) => match m.get().assoc(k, v) {
235                crate::collections::array_map::AssocResult::Array(new_m) => {
236                    MapValue::Array(GcPtr::new(new_m))
237                }
238                crate::collections::array_map::AssocResult::Promote(pairs) => {
239                    let hm = PersistentHashMap::from_pairs(pairs);
240                    MapValue::Hash(GcPtr::new(hm))
241                }
242            },
243            MapValue::Hash(m) => MapValue::Hash(GcPtr::new(m.get().assoc(k, v))),
244            MapValue::Sorted(m) => MapValue::Sorted(GcPtr::new(m.get().assoc(k, v))),
245        }
246    }
247
248    pub fn dissoc(&self, key: &Value) -> Self {
249        match self {
250            MapValue::Array(m) => MapValue::Array(GcPtr::new(m.get().dissoc(key))),
251            MapValue::Hash(m) => MapValue::Hash(GcPtr::new(m.get().dissoc(key))),
252            MapValue::Sorted(m) => MapValue::Sorted(GcPtr::new(m.get().dissoc(key))),
253        }
254    }
255
256    pub fn contains_key(&self, key: &Value) -> bool {
257        match self {
258            MapValue::Array(m) => m.get().contains_key(key),
259            MapValue::Hash(m) => m.get().contains_key(key),
260            MapValue::Sorted(m) => m.get().contains_key(key),
261        }
262    }
263
264    /// Iterate over all `(key, value)` pairs.
265    pub fn for_each<F: FnMut(&Value, &Value)>(&self, mut f: F) {
266        match self {
267            MapValue::Array(m) => {
268                for (k, v) in m.get().iter() {
269                    f(k, v);
270                }
271            }
272            MapValue::Hash(m) => {
273                for (k, v) in m.get().iter() {
274                    f(k, v);
275                }
276            }
277            MapValue::Sorted(m) => {
278                for (k, v) in m.get().iter() {
279                    f(k, v);
280                }
281            }
282        }
283    }
284
285    /// Iterate over key/value pairs.
286    pub fn iter(&self) -> Box<dyn Iterator<Item = (&Value, &Value)> + '_> {
287        match self {
288            MapValue::Array(m) => Box::new(m.get().iter()),
289            MapValue::Hash(m) => Box::new(m.get().iter()),
290            MapValue::Sorted(m) => Box::new(m.get().iter()),
291        }
292    }
293}
294
295/// A set value, either a hash set or a sorted set.
296#[derive(Clone, Debug)]
297pub enum SetValue {
298    Hash(GcPtr<PersistentHashSet>),
299    Sorted(GcPtr<SortedSet>),
300}
301
302impl SetValue {
303    pub fn empty() -> Self {
304        Self::Hash(GcPtr::new(PersistentHashSet::empty()))
305    }
306
307    pub fn count(&self) -> usize {
308        match self {
309            SetValue::Hash(m) => m.get().count(),
310            SetValue::Sorted(m) => m.get().count(),
311        }
312    }
313
314    pub fn is_empty(&self) -> bool {
315        match self {
316            SetValue::Hash(m) => m.get().is_empty(),
317            SetValue::Sorted(m) => m.get().is_empty(),
318        }
319    }
320
321    pub fn contains(&self, key: &Value) -> bool {
322        match self {
323            SetValue::Hash(m) => m.get().contains(key),
324            SetValue::Sorted(m) => m.get().contains(key),
325        }
326    }
327
328    pub fn conj(&self, value: Value) -> Self {
329        match self {
330            SetValue::Hash(m) => SetValue::Hash(GcPtr::new(m.get().conj(value))),
331            SetValue::Sorted(m) => SetValue::Sorted(GcPtr::new(m.get().conj(value))),
332        }
333    }
334
335    pub fn conj_mut(&mut self, value: Value) -> &mut Self {
336        match self {
337            SetValue::Hash(m) => {
338                m.get_mut().conj_mut(value);
339            }
340            SetValue::Sorted(s) => {
341                s.get_mut().conj_mut(value);
342            }
343        }
344        self
345    }
346
347    pub fn disj(&self, value: &Value) -> Self {
348        match self {
349            SetValue::Hash(m) => SetValue::Hash(GcPtr::new(m.get().disj(value))),
350            SetValue::Sorted(m) => SetValue::Sorted(GcPtr::new(m.get().disj(value))),
351        }
352    }
353
354    pub fn iter(&self) -> Box<dyn Iterator<Item = &Value> + '_> {
355        match self {
356            SetValue::Hash(s) => Box::new(s.get().iter()),
357            SetValue::Sorted(s) => Box::new(s.get().iter()),
358        }
359    }
360}
361
362impl cljrs_gc::Trace for SetValue {
363    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
364        use cljrs_gc::GcVisitor as _;
365        match self {
366            SetValue::Hash(s) => visitor.visit(s),
367            SetValue::Sorted(s) => visitor.visit(s),
368        }
369    }
370}
371
372// ── Equality ──────────────────────────────────────────────────────────────────
373
374impl Eq for Value {}
375
376impl PartialEq for Value {
377    fn eq(&self, other: &Self) -> bool {
378        // Strip metadata — it is ignored for equality in Clojure.
379        if let Value::WithMeta(inner, _) = self {
380            return inner.as_ref() == other;
381        }
382        if let Value::WithMeta(inner, _) = other {
383            return self == inner.as_ref();
384        }
385        // Unwrap Reduced for equality.
386        if let Value::Reduced(inner) = self {
387            return inner.as_ref() == other;
388        }
389        if let Value::Reduced(inner) = other {
390            return self == inner.as_ref();
391        }
392        // Identity shortcut: same GcPtr → equal without realizing.
393        // Required for infinite lazy seqs: (let [r (range)] (= r r)) must not hang.
394        if let (Value::LazySeq(a), Value::LazySeq(b)) = (self, other)
395            && GcPtr::ptr_eq(a, b)
396        {
397            return true;
398        }
399        // Realize lazy sequences before comparing.
400        // A lazy-seq that realizes to nil is an empty sequence, which is equal
401        // to both nil and any empty sequential collection (matching Clojure).
402        if let Value::LazySeq(ls) = self {
403            let realized = ls.get().realize();
404            if realized == Value::Nil && other.is_sequential() {
405                return value_to_seq_vec(other).is_empty();
406            }
407            return realized == *other;
408        }
409        if let Value::LazySeq(ls) = other {
410            let realized = ls.get().realize();
411            if realized == Value::Nil && self.is_sequential() {
412                return value_to_seq_vec(self).is_empty();
413            }
414            return *self == realized;
415        }
416        match (self, other) {
417            (Value::Nil, Value::Nil) => true,
418            (Value::Bool(a), Value::Bool(b)) => a == b,
419            // Numeric cross-type equality.
420            (Value::Long(a), Value::Long(b)) => a == b,
421            (Value::Long(a), Value::BigInt(b)) => BigInt::from(*a) == *b.get(),
422            (Value::BigInt(a), Value::Long(b)) => *a.get() == BigInt::from(*b),
423            (Value::BigInt(a), Value::BigInt(b)) => a.get() == b.get(),
424            (Value::Double(a), Value::Double(b)) => a == b, // NaN != NaN
425            (Value::Long(a), Value::Double(b)) => b.fract() == 0.0 && b.to_i64() == Some(*a),
426            (Value::Double(a), Value::Long(b)) => a.fract() == 0.0 && a.to_i64() == Some(*b),
427            (Value::BigDecimal(a), Value::BigDecimal(b)) => a.get() == b.get(),
428            (Value::Ratio(a), Value::Ratio(b)) => a.get() == b.get(),
429            (Value::Char(a), Value::Char(b)) => a == b,
430            (Value::Str(a), Value::Str(b)) => a.get() == b.get(),
431            (Value::Symbol(a), Value::Symbol(b)) => a.get() == b.get(),
432            (Value::Keyword(a), Value::Keyword(b)) => a.get() == b.get(),
433            // Collection equality.
434            (Value::List(a), Value::List(b)) => a.get() == b.get(),
435            (Value::Vector(a), Value::Vector(b)) => a.get() == b.get(),
436            (Value::Set(a), Value::Set(b)) => sets_equal(a, b),
437            (Value::Queue(a), Value::Queue(b)) => a.get() == b.get(),
438            (Value::Map(a), Value::Map(b)) => maps_equal(a, b),
439            // Sequential cross-type equality: '(1 2) == [1 2].
440            (Value::List(_), Value::Vector(_)) | (Value::Vector(_), Value::List(_)) => {
441                seq_equal(self, other)
442            }
443            // Cons cells: compare element by element.
444            (Value::Cons(_), _) | (_, Value::Cons(_)) => seq_equal(self, other),
445            // Pointer equality for functions.
446            (Value::Fn(a), Value::Fn(b)) => std::ptr::eq(a.get() as *const _, b.get() as *const _),
447            (Value::Macro(a), Value::Macro(b)) => {
448                std::ptr::eq(a.get() as *const _, b.get() as *const _)
449            }
450            (Value::NativeFunction(a), Value::NativeFunction(b)) => {
451                std::ptr::eq(a.get() as *const _, b.get() as *const _)
452            }
453            // Pointer equality for protocol/multimethod objects.
454            (Value::Protocol(a), Value::Protocol(b)) => {
455                std::ptr::eq(a.get() as *const _, b.get() as *const _)
456            }
457            (Value::ProtocolFn(a), Value::ProtocolFn(b)) => {
458                std::ptr::eq(a.get() as *const _, b.get() as *const _)
459            }
460            (Value::MultiFn(a), Value::MultiFn(b)) => {
461                std::ptr::eq(a.get() as *const _, b.get() as *const _)
462            }
463            // Pointer equality for concurrency primitives.
464            (Value::Volatile(a), Value::Volatile(b)) => {
465                std::ptr::eq(a.get() as *const _, b.get() as *const _)
466            }
467            (Value::Delay(a), Value::Delay(b)) => {
468                std::ptr::eq(a.get() as *const _, b.get() as *const _)
469            }
470            (Value::Promise(a), Value::Promise(b)) => {
471                std::ptr::eq(a.get() as *const _, b.get() as *const _)
472            }
473            (Value::Future(a), Value::Future(b)) => {
474                std::ptr::eq(a.get() as *const _, b.get() as *const _)
475            }
476            (Value::Agent(a), Value::Agent(b)) => {
477                std::ptr::eq(a.get() as *const _, b.get() as *const _)
478            }
479            (Value::Atom(a), Value::Atom(b)) => {
480                std::ptr::eq(a.get() as *const _, b.get() as *const _)
481            }
482            (Value::Var(a), Value::Var(b)) => {
483                std::ptr::eq(a.get() as *const _, b.get() as *const _)
484            }
485            (Value::Namespace(a), Value::Namespace(b)) => {
486                std::ptr::eq(a.get() as *const _, b.get() as *const _)
487            }
488            // UUID equality: same u128 value.
489            (Value::Uuid(a), Value::Uuid(b)) => a.get().0 == b.get().0,
490            // Regex pattern equality: compare source string (matches Clojure JVM behavior
491            // where two patterns are equal iff their source strings are equal).
492            (Value::Pattern(a), Value::Pattern(b)) => a.get().as_str() == b.get().as_str(),
493            // NativeObject equality: pointer identity.
494            (Value::NativeObject(a), Value::NativeObject(b)) => {
495                std::ptr::eq(a.get() as *const _, b.get() as *const _)
496            }
497            // Resource equality: pointer identity.
498            (Value::Resource(a), Value::Resource(b)) => Arc::ptr_eq(&a.0, &b.0),
499            // Record equality: same tag and same fields.
500            (Value::TypeInstance(a), Value::TypeInstance(b)) => {
501                a.get().type_tag == b.get().type_tag && maps_equal(&a.get().fields, &b.get().fields)
502            }
503            (Value::Error(a), Value::Error(b)) => {
504                std::ptr::eq(a.get() as *const _, b.get() as *const _)
505            }
506            // SharedAtom: pointer identity (same Arc → same atom).
507            (Value::SharedAtom(a), Value::SharedAtom(b)) => Arc::ptr_eq(a, b),
508            // ByteBlob: pointer identity (same Arc → same underlying buffer).
509            (Value::ByteBlob(a), Value::ByteBlob(b)) => Arc::ptr_eq(a, b),
510            _ => false,
511        }
512    }
513}
514
515fn maps_equal(a: &MapValue, b: &MapValue) -> bool {
516    if a.count() != b.count() {
517        return false;
518    }
519    let mut equal = true;
520    a.for_each(|k, v| {
521        if equal {
522            match b.get(k) {
523                Some(bv) if &bv == v => {}
524                _ => equal = false,
525            }
526        }
527    });
528    equal
529}
530
531fn sets_equal(a: &SetValue, b: &SetValue) -> bool {
532    if a.count() != b.count() {
533        return false;
534    }
535    for k in a.iter() {
536        if !b.contains(k) {
537            return false;
538        }
539    }
540    true
541}
542
543fn seq_equal(a: &Value, b: &Value) -> bool {
544    let a_items = value_to_seq_vec(a);
545    let b_items = value_to_seq_vec(b);
546    a_items.len() == b_items.len() && a_items.iter().zip(b_items.iter()).all(|(x, y)| x == y)
547}
548
549fn value_to_seq_vec(v: &Value) -> Vec<Value> {
550    // Iteratively unwrap lazy seqs.
551    let mut v = v.clone();
552    while let Value::LazySeq(ls) = &v {
553        v = ls.get().realize();
554    }
555    match &v {
556        Value::List(l) => l.get().iter().cloned().collect(),
557        Value::Vector(v) => v.get().iter().cloned().collect(),
558        Value::LazySeq(_) => unreachable!("unwrapped above"),
559        Value::Cons(c) => {
560            let mut result = vec![c.get().head.clone()];
561            let mut tail = c.get().tail.clone();
562            loop {
563                match tail {
564                    Value::Nil => break,
565                    Value::List(l) => {
566                        result.extend(l.get().iter().cloned());
567                        break;
568                    }
569                    Value::Cons(next_c) => {
570                        result.push(next_c.get().head.clone());
571                        tail = next_c.get().tail.clone();
572                    }
573                    Value::LazySeq(ls) => {
574                        tail = ls.get().realize();
575                    }
576                    _ => break,
577                }
578            }
579            result
580        }
581        _ => vec![],
582    }
583}
584
585// ── Hashing ───────────────────────────────────────────────────────────────────
586
587impl ClojureHash for Value {
588    fn clojure_hash(&self) -> u32 {
589        match self {
590            Value::WithMeta(inner, _) => inner.clojure_hash(),
591            Value::Reduced(inner) => inner.clojure_hash(),
592            Value::Nil => 0,
593            Value::Bool(b) => {
594                if *b { 1231 } else { 1237 } // Java Boolean.hashCode
595            }
596            Value::Long(n) => hash_i64(*n),
597            Value::Double(f) => {
598                // Whole-number doubles hash like their Long equivalent.
599                if f.fract() == 0.0
600                    && f.is_finite()
601                    && let Some(n) = num_traits::ToPrimitive::to_i64(f)
602                {
603                    return hash_i64(n);
604                }
605                hash_i64(f.to_bits() as i64)
606            }
607            Value::BigInt(n) => {
608                // Hash like Long if it fits.
609                if let Some(l) = n.get().to_i64() {
610                    return hash_i64(l);
611                }
612                // Otherwise hash the decimal string (simplified).
613                hash_string(&n.get().to_string())
614            }
615            Value::Char(c) => *c as u32,
616            Value::Str(s) => hash_string(s.get()),
617            Value::Pattern(r) => hash_string(r.get().as_str()),
618            Value::Matcher(m) => hash_string(m.get().pattern.get().as_str()),
619            Value::Keyword(k) => hash_string(&k.get().to_string()),
620            Value::Symbol(s) => hash_string(&s.get().to_string()),
621            Value::Uuid(u) => hash_u128(u.get().0),
622            Value::NativeObject(obj) => {
623                let ptr = obj.get() as *const _ as usize;
624                hash_i64(ptr as i64)
625            }
626            Value::Resource(r) => {
627                let ptr = Arc::as_ptr(&r.0) as *const () as usize;
628                hash_i64(ptr as i64)
629            }
630            Value::List(l) => {
631                let mut h: u32 = 1;
632                for v in l.get().iter() {
633                    h = hash_combine_ordered(h, v.clojure_hash());
634                }
635                h
636            }
637            Value::Vector(v) => {
638                let mut h: u32 = 1;
639                for item in v.get().iter() {
640                    h = hash_combine_ordered(h, item.clojure_hash());
641                }
642                h
643            }
644            Value::Map(m) => {
645                let mut h: u32 = 0;
646                m.for_each(|k, v| {
647                    h = hash_combine_unordered(
648                        h,
649                        hash_combine_ordered(k.clojure_hash(), v.clojure_hash()),
650                    );
651                });
652                h
653            }
654            Value::Set(s) => {
655                let mut h: u32 = 0;
656                for k in s.iter() {
657                    h = hash_combine_unordered(h, k.clojure_hash());
658                }
659                h
660            }
661            Value::TransientMap(m) => m.get().clojure_hash(),
662            Value::TransientSet(s) => s.get().clojure_hash(),
663            Value::TransientVector(v) => v.get().clojure_hash(),
664
665            // Arrays
666            Value::BooleanArray(a) => {
667                let mut h: u32 = 0;
668                for b in a.get().lock().unwrap().iter() {
669                    h = hash_combine_ordered(h, if *b { 1231 } else { 1237 })
670                }
671                h
672            }
673            Value::ByteArray(a) => {
674                let mut h: u32 = 0;
675                for b in a.get().lock().unwrap().iter() {
676                    h = hash_combine_ordered(h, *b as u32)
677                }
678                h
679            }
680            Value::ShortArray(a) => {
681                let mut h: u32 = 0;
682                for item in a.get().lock().unwrap().iter() {
683                    h = hash_combine_ordered(h, *item as u32)
684                }
685                h
686            }
687            Value::IntArray(a) => {
688                let mut h: u32 = 0;
689                for item in a.get().lock().unwrap().iter() {
690                    h = hash_combine_ordered(h, *item as u32)
691                }
692                h
693            }
694            Value::CharArray(a) => {
695                let mut h: u32 = 0;
696                for item in a.get().lock().unwrap().iter() {
697                    h = hash_combine_ordered(h, *item as u32)
698                }
699                h
700            }
701            Value::LongArray(a) => {
702                let mut h: u32 = 0;
703                for item in a.get().lock().unwrap().iter() {
704                    let v = *item;
705                    h = hash_combine_ordered(h, hash_i64(v));
706                }
707                h
708            }
709            Value::FloatArray(a) => {
710                let mut h: u32 = 0;
711                for item in a.get().lock().unwrap().iter() {
712                    let f = *item;
713                    h = hash_combine_ordered(
714                        h,
715                        if f.fract() == 0.0
716                            && f.is_finite()
717                            && let Some(n) = ToPrimitive::to_i64(item)
718                        {
719                            hash_i64(n)
720                        } else {
721                            hash_i64(f.to_bits() as i64)
722                        },
723                    )
724                }
725                h
726            }
727            Value::DoubleArray(a) => {
728                let mut h: u32 = 0;
729                for item in a.get().lock().unwrap().iter() {
730                    let f = *item;
731                    h = hash_combine_ordered(
732                        h,
733                        if f.fract() == 0.0
734                            && f.is_finite()
735                            && let Some(n) = ToPrimitive::to_i64(item)
736                        {
737                            hash_i64(n)
738                        } else {
739                            hash_i64(f.to_bits() as i64)
740                        },
741                    )
742                }
743                h
744            }
745            Value::ObjectArray(a) => {
746                let mut h: u32 = 0;
747                for item in a.get().0.lock().unwrap().iter() {
748                    h = hash_combine_ordered(h, item.clojure_hash())
749                }
750                h
751            }
752
753            Value::SharedAtom(a) => Arc::as_ptr(a) as u32,
754            Value::ByteBlob(b) => {
755                let mut h: u32 = 0;
756                for byte in b.iter() {
757                    h = hash_combine_ordered(h, hash_i64(*byte as i64));
758                }
759                h
760            }
761
762            // For non-data types, use pointer identity.
763            Value::Fn(f) => f.get() as *const _ as u32,
764            Value::BoundFn(f) => f.get() as *const _ as u32,
765            Value::NativeFunction(f) => f.get() as *const _ as u32,
766            Value::Var(v) => v.get() as *const _ as u32,
767            Value::Atom(a) => a.get() as *const _ as u32,
768            Value::Namespace(n) => n.get() as *const _ as u32,
769            Value::Queue(q) => {
770                let mut h: u32 = 1;
771                for v in q.get().iter() {
772                    h = hash_combine_ordered(h, v.clojure_hash());
773                }
774                h
775            }
776            Value::Macro(f) => f.get() as *const _ as u32,
777            Value::BigDecimal(d) => hash_string(&d.get().to_string()),
778            Value::Ratio(r) => hash_string(&r.get().to_string()),
779            Value::LazySeq(ls) => ls.get().realize().clojure_hash(),
780            Value::Protocol(p) => p.get() as *const _ as u32,
781            Value::ProtocolFn(pf) => pf.get() as *const _ as u32,
782            Value::MultiFn(mf) => mf.get() as *const _ as u32,
783            Value::Cons(_) => {
784                // Hash like an ordered sequence.
785                let mut h: u32 = 1;
786                for v in value_to_seq_vec(self) {
787                    h = hash_combine_ordered(h, v.clojure_hash());
788                }
789                h
790            }
791            // Pointer identity for concurrency primitives.
792            Value::Volatile(v) => v.get() as *const _ as u32,
793            Value::Delay(d) => d.get() as *const _ as u32,
794            Value::Promise(p) => p.get() as *const _ as u32,
795            Value::Future(fu) => fu.get() as *const _ as u32,
796            Value::Agent(a) => a.get() as *const _ as u32,
797            // Record hash: combine type tag hash with fields hash.
798            Value::TypeInstance(ti) => {
799                let tag_hash = hash_string(&ti.get().type_tag);
800                let mut fields_hash: u32 = 0;
801                ti.get().fields.for_each(|k, v| {
802                    fields_hash = hash_combine_unordered(
803                        fields_hash,
804                        hash_combine_ordered(k.clojure_hash(), v.clojure_hash()),
805                    );
806                });
807                hash_combine_ordered(tag_hash, fields_hash)
808            }
809            Value::Error(e) => e.get().clojure_hash(),
810        }
811    }
812}
813
814// Implement std::hash::Hash by delegating to ClojureHash.
815impl std::hash::Hash for Value {
816    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
817        self.clojure_hash().hash(state);
818    }
819}
820
821// ── Display / pr-str ──────────────────────────────────────────────────────────
822
823impl fmt::Display for Value {
824    /// Prints in `pr-str` style (readable): strings are quoted, chars use `\` notation.
825    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
826        pr_str(self, f, true)
827    }
828}
829
830/// A wrapper for printing a Value non-readably (for `str`, `println`).
831pub struct PrintValue<'a>(pub &'a Value);
832
833impl fmt::Display for PrintValue<'_> {
834    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
835        pr_str(self.0, f, false)
836    }
837}
838
839/// Print a value.  `readably = true` quotes strings and escapes chars.
840pub fn pr_str(v: &Value, f: &mut fmt::Formatter<'_>, readably: bool) -> fmt::Result {
841    match v {
842        Value::WithMeta(inner, _) => pr_str(inner, f, readably),
843        Value::Reduced(inner) => {
844            write!(f, "#reduced ")?;
845            pr_str(inner, f, readably)
846        }
847        Value::Nil => write!(f, "nil"),
848        Value::Bool(b) => write!(f, "{b}"),
849        Value::Long(n) => write!(f, "{n}"),
850        Value::Double(d) => {
851            if d.is_infinite() {
852                if readably {
853                    if *d > 0.0 {
854                        write!(f, "##Inf")
855                    } else {
856                        write!(f, "##-Inf")
857                    }
858                } else if *d > 0.0 {
859                    write!(f, "Infinity")
860                } else {
861                    write!(f, "-Infinity")
862                }
863            } else if d.is_nan() {
864                if readably {
865                    write!(f, "##NaN")
866                } else {
867                    write!(f, "NaN")
868                }
869            } else if d.fract() == 0.0 && d.abs() < 1e15 {
870                write!(f, "{d:.1}")
871            } else {
872                write!(f, "{d}")
873            }
874        }
875        Value::BigInt(n) => {
876            if readably {
877                write!(f, "{}N", n.get())
878            } else {
879                write!(f, "{}", n.get())
880            }
881        }
882        Value::BigDecimal(d) => {
883            let dec = d.get();
884            let s = format!("{}", dec);
885            // bigdecimal Display omits trailing zeros for zero values (e.g. 0.0 → "0").
886            // Preserve scale: if the value has fractional digits but displays without a dot, add them.
887            if !s.contains('.') && dec.fractional_digit_count() > 0 {
888                let zeros = "0".repeat(dec.fractional_digit_count() as usize);
889                if readably {
890                    write!(f, "{s}.{zeros}M")
891                } else {
892                    write!(f, "{s}.{zeros}")
893                }
894            } else if readably {
895                write!(f, "{s}M")
896            } else {
897                write!(f, "{s}")
898            }
899        }
900        Value::Ratio(r) => write!(f, "{}", r.get()),
901        Value::Uuid(u) => {
902            let uuid = uuid::Uuid::from_u128(u.get().0);
903            if readably {
904                write!(f, "#uuid \"{}\"", uuid)
905            } else {
906                write!(f, "{}", uuid)
907            }
908        }
909        Value::Char(c) => {
910            if readably {
911                match c {
912                    '\n' => write!(f, "\\newline"),
913                    '\t' => write!(f, "\\tab"),
914                    ' ' => write!(f, "\\space"),
915                    '\r' => write!(f, "\\return"),
916                    c => write!(f, "\\{c}"),
917                }
918            } else {
919                write!(f, "{c}")
920            }
921        }
922        Value::Str(s) => {
923            if readably {
924                write!(f, "\"")?;
925                for c in s.get().chars() {
926                    match c {
927                        '"' => write!(f, "\\\"")?,
928                        '\\' => write!(f, "\\\\")?,
929                        '\n' => write!(f, "\\n")?,
930                        '\t' => write!(f, "\\t")?,
931                        '\r' => write!(f, "\\r")?,
932                        c => write!(f, "{c}")?,
933                    }
934                }
935                write!(f, "\"")
936            } else {
937                write!(f, "{}", s.get())
938            }
939        }
940        Value::Pattern(r) => {
941            if readably {
942                write!(f, "#\"")?;
943                write!(f, "{}", r.get().as_str())?;
944                write!(f, "\"")
945            } else {
946                write!(f, "#<{}>", r.get())
947            }
948        }
949        Value::Matcher(_) => write!(f, "#<Matcher>"),
950        Value::Symbol(s) => write!(f, "{}", s.get()),
951        Value::Keyword(k) => write!(f, "{}", k.get()),
952        Value::List(l) => {
953            write!(f, "(")?;
954            let mut first = true;
955            for item in l.get().iter() {
956                if !first {
957                    write!(f, " ")?;
958                }
959                pr_str(item, f, readably)?;
960                first = false;
961            }
962            write!(f, ")")
963        }
964        Value::Vector(v) => {
965            write!(f, "[")?;
966            let mut first = true;
967            for item in v.get().iter() {
968                if !first {
969                    write!(f, " ")?;
970                }
971                pr_str(item, f, readably)?;
972                first = false;
973            }
974            write!(f, "]")
975        }
976        Value::Map(m) => {
977            write!(f, "{{")?;
978            let mut first = true;
979            m.for_each(|k, v| {
980                // Ignore fmt errors inside the closure — limitations of fmt.
981                if !first {
982                    let _ = write!(f, ", ");
983                }
984                let _ = pr_str(k, f, readably);
985                let _ = write!(f, " ");
986                let _ = pr_str(v, f, readably);
987                first = false;
988            });
989            write!(f, "}}")
990        }
991        Value::Set(s) => {
992            write!(f, "#{{")?;
993            let mut first = true;
994            for item in s.iter() {
995                if !first {
996                    write!(f, " ")?;
997                }
998                pr_str(item, f, readably)?;
999                first = false;
1000            }
1001            write!(f, "}}")
1002        }
1003        Value::BooleanArray(_)
1004        | Value::ByteArray(_)
1005        | Value::ShortArray(_)
1006        | Value::IntArray(_)
1007        | Value::LongArray(_)
1008        | Value::CharArray(_)
1009        | Value::FloatArray(_)
1010        | Value::DoubleArray(_)
1011        | Value::ObjectArray(_) => write!(f, "#[array]"),
1012        Value::Queue(q) => {
1013            // Printed as a list with a type tag.
1014            write!(f, "#queue (")?;
1015            let mut first = true;
1016            for item in q.get().iter() {
1017                if !first {
1018                    write!(f, " ")?;
1019                }
1020                pr_str(item, f, readably)?;
1021                first = false;
1022            }
1023            write!(f, ")")
1024        }
1025        Value::LazySeq(ls) => pr_str(&ls.get().realize(), f, readably),
1026        Value::Cons(c) => {
1027            write!(f, "(")?;
1028            pr_str(&c.get().head, f, readably)?;
1029            let mut tail = c.get().tail.clone();
1030            loop {
1031                match tail {
1032                    Value::Nil => break,
1033                    Value::List(l) => {
1034                        for item in l.get().iter() {
1035                            write!(f, " ")?;
1036                            pr_str(item, f, readably)?;
1037                        }
1038                        break;
1039                    }
1040                    Value::Cons(next_c) => {
1041                        write!(f, " ")?;
1042                        pr_str(&next_c.get().head, f, readably)?;
1043                        tail = next_c.get().tail.clone();
1044                    }
1045                    Value::LazySeq(ls) => {
1046                        tail = ls.get().realize();
1047                    }
1048                    other => {
1049                        write!(f, " . ")?;
1050                        pr_str(&other, f, readably)?;
1051                        break;
1052                    }
1053                }
1054            }
1055            write!(f, ")")
1056        }
1057        Value::NativeFunction(nf) => write!(f, "#<NativeFn {}>", nf.get().name),
1058        Value::BoundFn(_) => write!(f, "#<BoundFn>"),
1059        Value::Fn(fun) => match &fun.get().name {
1060            Some(n) => write!(f, "#<Fn {n}>"),
1061            None => write!(f, "#<Fn>"),
1062        },
1063        Value::Macro(m) => match &m.get().name {
1064            Some(n) => write!(f, "#<Macro {n}>"),
1065            None => write!(f, "#<Macro>"),
1066        },
1067        Value::Var(v) => write!(f, "#'{}/{}", v.get().namespace, v.get().name),
1068        Value::Atom(a) => write!(f, "#<Atom {}>", a.get().deref()),
1069        Value::SharedAtom(a) => {
1070            write!(f, "#<SharedAtom {}>", a.deref_val().type_name())
1071        }
1072        Value::ByteBlob(b) => write!(f, "#<ByteBlob {} bytes>", b.len()),
1073        Value::Namespace(n) => write!(f, "#<Namespace {}>", n.get().name),
1074        Value::Protocol(p) => write!(f, "#<Protocol {}>", p.get().name),
1075        Value::ProtocolFn(pf) => {
1076            write!(
1077                f,
1078                "#<fn {}/{}>",
1079                pf.get().protocol.get().name,
1080                pf.get().method_name
1081            )
1082        }
1083        Value::MultiFn(mf) => write!(f, "#<MultiFn {}>", mf.get().name),
1084        Value::Volatile(_) => write!(f, "#<Volatile>"),
1085        Value::Delay(_) => write!(f, "#<Delay>"),
1086        Value::Promise(_) => write!(f, "#<Promise>"),
1087        Value::Future(_) => write!(f, "#<Future>"),
1088        Value::Agent(_) => write!(f, "#<Agent>"),
1089        Value::TypeInstance(ti) => {
1090            let ti = ti.get();
1091            write!(f, "#{}{{", ti.type_tag)?;
1092            let mut first = true;
1093            ti.fields.for_each(|k, v| {
1094                if !first {
1095                    let _ = write!(f, ", ");
1096                }
1097                let _ = pr_str(k, f, readably);
1098                let _ = write!(f, " ");
1099                let _ = pr_str(v, f, readably);
1100                first = false;
1101            });
1102            write!(f, "}}")
1103        }
1104        Value::NativeObject(obj) => {
1105            write!(f, "#<{} {:?}>", obj.get().type_tag(), obj.get().inner())
1106        }
1107        Value::Resource(r) => {
1108            if r.is_closed() {
1109                write!(f, "#<{} (closed)>", r.resource_type())
1110            } else {
1111                write!(f, "#<{}>", r.resource_type())
1112            }
1113        }
1114        Value::TransientMap(_) => write!(f, "#<TransientMap>"),
1115        Value::TransientSet(_) => write!(f, "#<TransientSet>"),
1116        Value::TransientVector(_) => write!(f, "#<TransientVector>"),
1117        Value::Error(e) => {
1118            write!(f, "#error ")?;
1119            let map = e.get().to_map().map_err(|_| fmt::Error {})?;
1120            pr_str(&map, f, readably)
1121        }
1122    }
1123}
1124
1125// ── Keyword-argument rest normalization ──────────────────────────────────────
1126
1127impl Value {
1128    /// Normalize a variadic rest sequence into the value a *map-shaped* rest
1129    /// pattern (`[& {:keys [a b]}]`) destructures against.
1130    ///
1131    /// This is Clojure's `seq-to-map-for-destructuring`, and every execution
1132    /// tier must agree on it — the tree-walker calls it from `bind_fn_params`,
1133    /// the IR interpreter and the compiled tiers reach it through
1134    /// `KnownFn::KwargsMap` / `rt_kwargs_map`.  A divergence here is issue
1135    /// #368.
1136    ///
1137    /// - Zero or one argument: the lone value *is* the map, returned verbatim.
1138    ///   `(f {:a 1})` therefore keeps the caller's map identity — a sorted map
1139    ///   stays sorted, metadata stays attached — and `(f)` / `(f nil)` bind
1140    ///   `nil`, which reads like an empty map for every `:keys` lookup.
1141    /// - Otherwise the arguments are alternating key/value pairs, optionally
1142    ///   followed by a trailing map whose entries win over the pairs before it
1143    ///   (Clojure 1.11's keyword-argument calling convention).
1144    ///
1145    /// An odd argument count with no trailing map is a caller error, reported
1146    /// the way Clojure reports it rather than dropping the dangling key.
1147    pub fn from_kwargs_rest(mut entries: Vec<Value>) -> Result<Value, ValueError> {
1148        if entries.len() < 2 {
1149            return Ok(entries.pop().unwrap_or(Value::Nil));
1150        }
1151
1152        // A trailing map is only a trailing map when what precedes it pairs up
1153        // evenly; `(f :a {:x 1})` passes a map as `:a`'s *value*.  Match through
1154        // any `WithMeta` wrapper — a map carrying metadata is still a map.
1155        let trailing = match entries.last().map(Value::unwrap_meta) {
1156            Some(Value::Map(_)) if !entries.len().is_multiple_of(2) => entries.pop(),
1157            _ => None,
1158        };
1159
1160        if !entries.len().is_multiple_of(2) {
1161            let key = entries.last().expect("odd length implies a last entry");
1162            return Err(ValueError::Other(format!(
1163                "No value supplied for key: {}",
1164                PrintValue(key)
1165            )));
1166        }
1167
1168        let mut result = MapValue::from_flat_entries(entries);
1169        if let Some(Value::Map(map)) = trailing.as_ref().map(Value::unwrap_meta) {
1170            for (key, value) in map.iter() {
1171                result = result.assoc(key.clone(), value.clone());
1172            }
1173        }
1174        Ok(Value::Map(result))
1175    }
1176}
1177
1178// ── Metadata helpers ─────────────────────────────────────────────────────────
1179
1180impl Value {
1181    /// Strip any `WithMeta` wrapper, returning the underlying value.
1182    pub fn unwrap_meta(&self) -> &Value {
1183        match self {
1184            Value::WithMeta(inner, _) => inner.unwrap_meta(),
1185            other => other,
1186        }
1187    }
1188
1189    /// Return metadata if present, or `None`.
1190    pub fn get_meta(&self) -> Option<&Value> {
1191        match self {
1192            Value::WithMeta(_, meta) => Some(meta),
1193            _ => None,
1194        }
1195    }
1196
1197    /// Return a new value with metadata attached.
1198    pub fn with_meta(self, meta: Value) -> Value {
1199        match self {
1200            Value::WithMeta(inner, _) => Value::WithMeta(inner, Box::new(meta)),
1201            other => Value::WithMeta(Box::new(other), Box::new(meta)),
1202        }
1203    }
1204}
1205
1206// ── type_name helper ──────────────────────────────────────────────────────────
1207
1208impl Value {
1209    /// A human-readable type name for error messages.
1210    pub fn type_name(&self) -> &'static str {
1211        match self {
1212            Value::WithMeta(inner, _) => inner.type_name(),
1213            Value::Reduced(_) => "reduced",
1214            Value::Nil => "nil",
1215            Value::Bool(_) => "boolean",
1216            Value::Long(_) => "long",
1217            Value::Double(_) => "double",
1218            Value::BigInt(_) => "bigint",
1219            Value::BigDecimal(_) => "bigdecimal",
1220            Value::Ratio(_) => "ratio",
1221            Value::Char(_) => "char",
1222            Value::Str(_) => "string",
1223            Value::Pattern(_) => "pattern",
1224            Value::Matcher(_) => "matcher",
1225            Value::Symbol(_) => "symbol",
1226            Value::Keyword(_) => "keyword",
1227            Value::Uuid(_) => "uuid",
1228            Value::List(_) => "list",
1229            Value::Vector(_) => "vector",
1230            Value::Map(_) => "map",
1231            Value::Set(_) => "set",
1232            Value::Queue(_) => "queue",
1233            Value::NativeFunction(_)
1234            | Value::Fn(_)
1235            | Value::BoundFn(_)
1236            | Value::Macro(_)
1237            | Value::ProtocolFn(_)
1238            | Value::MultiFn(_) => "fn",
1239            Value::Var(_) => "var",
1240            Value::Atom(_) => "atom",
1241            Value::SharedAtom(_) => "shared-atom",
1242            Value::ByteBlob(_) => "byte-blob",
1243            Value::Namespace(_) => "namespace",
1244            Value::LazySeq(_) => "lazyseq",
1245            Value::Cons(_) => "cons",
1246            Value::Protocol(_) => "protocol",
1247            Value::Volatile(_) => "volatile",
1248            Value::Delay(_) => "delay",
1249            Value::Promise(_) => "promise",
1250            Value::Future(_) => "future",
1251            Value::Agent(_) => "agent",
1252            Value::TypeInstance(_) => "record",
1253            Value::NativeObject(_) => "native-object",
1254            Value::BooleanArray(_) => "boolean-array",
1255            Value::ByteArray(_) => "byte-array",
1256            Value::ShortArray(_) => "short-array",
1257            Value::IntArray(_) => "int-array",
1258            Value::LongArray(_) => "long-array",
1259            Value::FloatArray(_) => "float-array",
1260            Value::DoubleArray(_) => "double-array",
1261            Value::CharArray(_) => "char-array",
1262            Value::ObjectArray(_) => "object-array",
1263            Value::Resource(r) => r.resource_type(),
1264            Value::TransientMap(_) => "transient-map",
1265            Value::TransientSet(_) => "transient-set",
1266            Value::TransientVector(_) => "transient-vector",
1267            Value::Error(_) => "error",
1268        }
1269    }
1270
1271    /// Convenience: wrap a `&str` in `Value::Str`.
1272    pub fn string(s: impl Into<String>) -> Self {
1273        Value::Str(GcPtr::new(s.into()))
1274    }
1275
1276    /// Construct a UUID value with distinct object identity.
1277    pub fn uuid(value: u128) -> Self {
1278        Value::Uuid(GcPtr::new(UuidValue(value)))
1279    }
1280
1281    /// Convenience: build a `[key val]` map entry (a tagged 2-element vector).
1282    pub fn map_entry(key: Value, val: Value) -> Self {
1283        Value::Vector(GcPtr::new(PersistentVector::map_entry(key, val)))
1284    }
1285
1286    /// True only for map entries (`[k v]` pairs from seq'ing a map, `find`,
1287    /// or the `map-entry` constructor) — not for plain 2-element vectors.
1288    pub fn is_map_entry(&self) -> bool {
1289        matches!(self.unwrap_meta(), Value::Vector(v) if v.get().is_map_entry())
1290    }
1291
1292    /// Convenience: wrap a `Symbol`.
1293    pub fn symbol(s: Symbol) -> Self {
1294        Value::Symbol(GcPtr::new(s))
1295    }
1296
1297    /// Convenience: wrap a `Keyword`.
1298    pub fn keyword(k: Keyword) -> Self {
1299        Value::Keyword(GcPtr::new(k))
1300    }
1301
1302    /// True for sequential collections (list, vector, lazy seq, cons).
1303    pub fn is_sequential(&self) -> bool {
1304        matches!(
1305            self,
1306            Value::List(_) | Value::Vector(_) | Value::LazySeq(_) | Value::Cons(_)
1307        )
1308    }
1309
1310    /// True for any collection.
1311    pub fn is_coll(&self) -> bool {
1312        self.unwrap_meta().is_coll_inner()
1313    }
1314
1315    fn is_coll_inner(&self) -> bool {
1316        matches!(
1317            self,
1318            Value::List(_)
1319                | Value::Vector(_)
1320                | Value::Map(_)
1321                | Value::Set(_)
1322                | Value::Queue(_)
1323                | Value::LazySeq(_)
1324                | Value::Cons(_)
1325        )
1326    }
1327}
1328
1329impl cljrs_gc::Trace for Value {
1330    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1331        use cljrs_gc::GcVisitor as _;
1332        match self {
1333            Value::Reduced(inner) => inner.trace(visitor),
1334            Value::WithMeta(inner, meta) => {
1335                inner.trace(visitor);
1336                meta.trace(visitor);
1337            }
1338            Value::Nil | Value::Bool(_) | Value::Long(_) | Value::Double(_) | Value::Char(_) => {}
1339            Value::Uuid(p) => visitor.visit(p),
1340            Value::BigInt(p) => visitor.visit(p),
1341            Value::BigDecimal(p) => visitor.visit(p),
1342            Value::Ratio(p) => visitor.visit(p),
1343            Value::Str(p) => visitor.visit(p),
1344            Value::Pattern(p) => visitor.visit(p),
1345            Value::Matcher(m) => visitor.visit(m),
1346            Value::Symbol(p) => visitor.visit(p),
1347            Value::Keyword(p) => visitor.visit(p),
1348            Value::List(p) => visitor.visit(p),
1349            Value::Vector(p) => visitor.visit(p),
1350            Value::Map(m) => m.trace(visitor),
1351            Value::Set(s) => s.trace(visitor),
1352            Value::Queue(p) => visitor.visit(p),
1353            Value::NativeFunction(p) => visitor.visit(p),
1354            Value::BoundFn(p) => visitor.visit(p),
1355            Value::Fn(p) | Value::Macro(p) => visitor.visit(p),
1356            Value::Var(p) => visitor.visit(p),
1357            Value::Atom(p) => visitor.visit(p),
1358            Value::Namespace(p) => visitor.visit(p),
1359            Value::LazySeq(p) => visitor.visit(p),
1360            Value::Cons(p) => visitor.visit(p),
1361            Value::Protocol(p) => visitor.visit(p),
1362            Value::ProtocolFn(p) => visitor.visit(p),
1363            Value::MultiFn(p) => visitor.visit(p),
1364            Value::Volatile(p) => visitor.visit(p),
1365            Value::Delay(p) => visitor.visit(p),
1366            Value::Promise(p) => visitor.visit(p),
1367            Value::Future(p) => visitor.visit(p),
1368            Value::Agent(p) => visitor.visit(p),
1369            Value::TypeInstance(p) => visitor.visit(p),
1370            Value::ObjectArray(p) => visitor.visit(p),
1371            // Primitive arrays have no child Values, but their boxes live on
1372            // the GC heap and must themselves be marked.
1373            Value::BooleanArray(p) => visitor.visit(p),
1374            Value::ByteArray(p) => visitor.visit(p),
1375            Value::ShortArray(p) => visitor.visit(p),
1376            Value::IntArray(p) => visitor.visit(p),
1377            Value::LongArray(p) => visitor.visit(p),
1378            Value::FloatArray(p) => visitor.visit(p),
1379            Value::DoubleArray(p) => visitor.visit(p),
1380            Value::CharArray(p) => visitor.visit(p),
1381            Value::NativeObject(p) => visitor.visit(p),
1382            // Resource, SharedAtom, and ByteBlob are Arc-ref-counted outside the
1383            // GC heap — nothing to trace through the GC visitor.
1384            Value::Resource(_) | Value::SharedAtom(_) | Value::ByteBlob(_) => {}
1385            Value::TransientMap(m) => visitor.visit(m),
1386            Value::TransientVector(p) => visitor.visit(p),
1387            Value::TransientSet(m) => visitor.visit(m),
1388            Value::Error(e) => visitor.visit(e),
1389        }
1390    }
1391}
1392
1393impl cljrs_gc::Trace for MapValue {
1394    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1395        use cljrs_gc::GcVisitor as _;
1396        match self {
1397            MapValue::Array(p) => visitor.visit(p),
1398            MapValue::Hash(p) => visitor.visit(p),
1399            MapValue::Sorted(p) => visitor.visit(p),
1400        }
1401    }
1402}
1403
1404// ── TypeInstance ──────────────────────────────────────────────────────────────
1405
1406/// A record or reify instance.  `type_tag` identifies the concrete type;
1407/// `fields` holds the key/value pairs (keyword → value).
1408#[derive(Clone, Debug)]
1409pub struct TypeInstance {
1410    pub type_tag: Arc<str>,
1411    pub fields: MapValue,
1412    /// Mutable `deftype` fields (`^:unsynchronized-mutable` /
1413    /// `^:volatile-mutable`), held in an interior-mutable cell — an `Atom` over
1414    /// a keyword→value map — so `set!` can update them in place. `None` for
1415    /// `defrecord`, `reify`, and immutable `deftype`s. Instances are `!Send`,
1416    /// so one shared cell needs no stronger volatility than an `Atom`.
1417    pub mutable: Option<GcPtr<crate::types::Atom>>,
1418}
1419
1420impl cljrs_gc::Trace for TypeInstance {
1421    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1422        use cljrs_gc::GcVisitor as _;
1423        self.fields.trace(visitor);
1424        if let Some(m) = &self.mutable {
1425            visitor.visit(m);
1426        }
1427    }
1428}
1429
1430#[allow(clippy::items_after_test_module)]
1431#[cfg(test)]
1432mod tests {
1433    use super::*;
1434
1435    fn kw(s: &str) -> Value {
1436        Value::keyword(Keyword::simple(s))
1437    }
1438    #[allow(dead_code)]
1439    fn sym(s: &str) -> Value {
1440        Value::symbol(Symbol::simple(s))
1441    }
1442    fn int(n: i64) -> Value {
1443        Value::Long(n)
1444    }
1445    fn s(v: &str) -> Value {
1446        Value::string(v)
1447    }
1448
1449    // ── Equality ──────────────────────────────────────────────────────────────
1450
1451    #[test]
1452    fn test_nil_eq() {
1453        assert_eq!(Value::Nil, Value::Nil);
1454        assert_ne!(Value::Nil, int(0));
1455    }
1456
1457    #[test]
1458    fn test_numeric_cross_type() {
1459        let big1 = Value::BigInt(GcPtr::new(BigInt::from(1i64)));
1460        assert_eq!(int(1), big1.clone());
1461        assert_eq!(big1, int(1));
1462        // 1.0 == 1
1463        assert_eq!(Value::Double(1.0), int(1));
1464        assert_eq!(int(1), Value::Double(1.0));
1465        // 1.5 != 1
1466        assert_ne!(Value::Double(1.5), int(1));
1467    }
1468
1469    #[test]
1470    fn test_nan_not_equal_to_itself() {
1471        let nan = Value::Double(f64::NAN);
1472        assert_ne!(nan, nan.clone());
1473    }
1474
1475    #[test]
1476    fn test_string_equality() {
1477        assert_eq!(s("hello"), s("hello"));
1478        assert_ne!(s("hello"), s("world"));
1479    }
1480
1481    #[test]
1482    fn test_list_vector_seq_equality() {
1483        let list = Value::List(GcPtr::new(PersistentList::from_iter([int(1), int(2)])));
1484        let vec = Value::Vector(GcPtr::new(PersistentVector::from_iter([int(1), int(2)])));
1485        // Clojure: (= '(1 2) [1 2]) => true
1486        assert_eq!(list, vec);
1487    }
1488
1489    #[test]
1490    fn test_map_equality_order_independent() {
1491        let mut a = MapValue::empty();
1492        a = a.assoc(kw("a"), int(1));
1493        a = a.assoc(kw("b"), int(2));
1494
1495        let mut b = MapValue::empty();
1496        b = b.assoc(kw("b"), int(2));
1497        b = b.assoc(kw("a"), int(1));
1498
1499        assert_eq!(Value::Map(a), Value::Map(b));
1500    }
1501
1502    // ── Hashing ───────────────────────────────────────────────────────────────
1503
1504    #[test]
1505    fn test_hash_consistency() {
1506        let big1 = Value::BigInt(GcPtr::new(BigInt::from(1i64)));
1507        assert_eq!(int(1).clojure_hash(), big1.clojure_hash());
1508    }
1509
1510    #[test]
1511    fn test_hash_whole_double() {
1512        // (= 1 1.0) → true, so (hash 1) == (hash 1.0)
1513        assert_eq!(int(1).clojure_hash(), Value::Double(1.0).clojure_hash());
1514    }
1515
1516    // ── Display ───────────────────────────────────────────────────────────────
1517
1518    #[test]
1519    fn test_pr_str_nil() {
1520        assert_eq!(Value::Nil.to_string(), "nil");
1521    }
1522
1523    #[test]
1524    fn test_pr_str_string() {
1525        assert_eq!(s("hello").to_string(), "\"hello\"");
1526        assert_eq!(s("a\"b").to_string(), "\"a\\\"b\"");
1527    }
1528
1529    #[test]
1530    fn test_pr_str_char() {
1531        assert_eq!(Value::Char('a').to_string(), "\\a");
1532        assert_eq!(Value::Char('\n').to_string(), "\\newline");
1533    }
1534
1535    #[test]
1536    fn test_pr_str_list() {
1537        let l = Value::List(GcPtr::new(PersistentList::from_iter([int(1), int(2)])));
1538        assert_eq!(l.to_string(), "(1 2)");
1539    }
1540
1541    #[test]
1542    fn test_pr_str_vector() {
1543        let v = Value::Vector(GcPtr::new(PersistentVector::from_iter([int(1), int(2)])));
1544        assert_eq!(v.to_string(), "[1 2]");
1545    }
1546
1547    #[test]
1548    #[allow(clippy::approx_constant)]
1549    fn test_pr_str_double() {
1550        assert_eq!(Value::Double(1.0).to_string(), "1.0");
1551        assert_eq!(Value::Double(3.14).to_string(), "3.14");
1552        assert_eq!(Value::Double(f64::INFINITY).to_string(), "##Inf");
1553        assert_eq!(Value::Double(f64::NEG_INFINITY).to_string(), "##-Inf");
1554        assert_eq!(Value::Double(f64::NAN).to_string(), "##NaN");
1555    }
1556
1557    // ── Keyword-argument rest normalization ──────────────────────────────────
1558
1559    fn kwargs(entries: Vec<Value>) -> Value {
1560        Value::from_kwargs_rest(entries).expect("well-formed kwargs")
1561    }
1562
1563    fn map_of(entries: Vec<Value>) -> Value {
1564        Value::Map(MapValue::from_flat_entries(entries))
1565    }
1566
1567    #[test]
1568    fn kwargs_rest_empty_and_lone_values_pass_through() {
1569        // `(f)` and `(f nil)` both bind nil, as in Clojure — `:as` sees the
1570        // rest value itself, so an empty map here would be observable.
1571        assert_eq!(kwargs(vec![]), Value::Nil);
1572        assert_eq!(kwargs(vec![Value::Nil]), Value::Nil);
1573        // A lone argument is the map verbatim, whatever it is: no rebuild, so
1574        // map identity (and with it sortedness and metadata) survives, and a
1575        // non-map is handed on for `get` to return nil against.
1576        assert_eq!(kwargs(vec![kw("a")]), kw("a"));
1577        let sorted = Value::Map(MapValue::Sorted(GcPtr::new(SortedMap::from_pairs(vec![
1578            (kw("b"), int(1)),
1579            (kw("a"), int(2)),
1580        ]))));
1581        assert!(matches!(
1582            kwargs(vec![sorted.clone()]),
1583            Value::Map(MapValue::Sorted(_))
1584        ));
1585        let with_meta = map_of(vec![kw("a"), int(1)]).with_meta(map_of(vec![kw("t"), int(1)]));
1586        assert_eq!(kwargs(vec![with_meta.clone()]), with_meta);
1587    }
1588
1589    #[test]
1590    fn kwargs_rest_pairs_build_a_map() {
1591        assert_eq!(
1592            kwargs(vec![kw("a"), int(1), kw("b"), int(2)]),
1593            map_of(vec![kw("a"), int(1), kw("b"), int(2)])
1594        );
1595        // Duplicate keys: last wins, as for a map literal built by assoc.
1596        assert_eq!(
1597            kwargs(vec![kw("a"), int(1), kw("a"), int(2)]),
1598            map_of(vec![kw("a"), int(2)])
1599        );
1600    }
1601
1602    #[test]
1603    fn kwargs_rest_trailing_map_merges_and_wins() {
1604        // `(f :a 1 {:b 2})` — the trailing map's entries join the pairs...
1605        assert_eq!(
1606            kwargs(vec![kw("a"), int(1), map_of(vec![kw("b"), int(2)])]),
1607            map_of(vec![kw("a"), int(1), kw("b"), int(2)])
1608        );
1609        // ...and beat them on a clash.
1610        assert_eq!(
1611            kwargs(vec![kw("a"), int(1), map_of(vec![kw("a"), int(9)])]),
1612            map_of(vec![kw("a"), int(9)])
1613        );
1614        // A map carrying metadata is still a trailing map: `with_meta` wraps
1615        // rather than annotates, so matching the raw `Value` would miss it.
1616        let meta_map = map_of(vec![kw("b"), int(2)]).with_meta(map_of(vec![kw("t"), int(1)]));
1617        assert_eq!(
1618            kwargs(vec![kw("a"), int(1), meta_map]),
1619            map_of(vec![kw("a"), int(1), kw("b"), int(2)])
1620        );
1621    }
1622
1623    #[test]
1624    fn kwargs_rest_map_in_value_position_is_not_a_trailing_map() {
1625        // Even arity: the map is `:a`'s value, not a trailing map to merge.
1626        let inner = map_of(vec![kw("x"), int(1)]);
1627        assert_eq!(
1628            kwargs(vec![kw("a"), inner.clone()]),
1629            map_of(vec![kw("a"), inner])
1630        );
1631    }
1632
1633    #[test]
1634    fn kwargs_rest_dangling_key_is_an_error() {
1635        // Clojure's message, and an error rather than the silently dropped
1636        // argument a bare `chunks(2)` would produce.
1637        let err = Value::from_kwargs_rest(vec![kw("a"), int(1), kw("b")])
1638            .expect_err("odd arity with no trailing map");
1639        assert_eq!(err.to_string(), "No value supplied for key: :b");
1640    }
1641}
1642
1643// Ord impl for Value
1644
1645impl PartialOrd for Value {
1646    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1647        Some(self.cmp(other))
1648    }
1649}
1650
1651impl Ord for Value {
1652    fn cmp(&self, other: &Self) -> Ordering {
1653        // Strip metadata for comparison.
1654        let a = self.unwrap_meta();
1655        let b = other.unwrap_meta();
1656        if !std::ptr::eq(a, self) || !std::ptr::eq(b, other) {
1657            return a.cmp(b);
1658        }
1659        // Same-type fast paths first, then cross-type numeric, then type-discriminant fallback.
1660        match (self, other) {
1661            // ── Nil ──
1662            (Value::Nil, Value::Nil) => Ordering::Equal,
1663
1664            // ── Booleans ──
1665            (Value::Bool(a), Value::Bool(b)) => a.cmp(b),
1666
1667            // ── Numerics (same type) ──
1668            (Value::Long(a), Value::Long(b)) => a.cmp(b),
1669            (Value::Double(a), Value::Double(b)) => total_cmp_f64(*a, *b),
1670            (Value::BigInt(a), Value::BigInt(b)) => a.get().cmp(b.get()),
1671            (Value::BigDecimal(a), Value::BigDecimal(b)) => {
1672                // BigDecimal doesn't impl Ord; compare via PartialOrd then string fallback.
1673                a.get()
1674                    .partial_cmp(b.get())
1675                    .unwrap_or_else(|| a.get().to_string().cmp(&b.get().to_string()))
1676            }
1677            (Value::Ratio(a), Value::Ratio(b)) => a.get().cmp(b.get()),
1678
1679            // ── Cross-type numerics (promote to common type) ──
1680            (Value::Long(a), Value::Double(b)) => total_cmp_f64(*a as f64, *b),
1681            (Value::Double(a), Value::Long(b)) => total_cmp_f64(*a, *b as f64),
1682            (Value::Long(a), Value::BigInt(b)) => BigInt::from(*a).cmp(b.get()),
1683            (Value::BigInt(a), Value::Long(b)) => a.get().cmp(&BigInt::from(*b)),
1684            (Value::Long(a), Value::Ratio(b)) => {
1685                num_rational::Ratio::from(BigInt::from(*a)).cmp(b.get())
1686            }
1687            (Value::Ratio(a), Value::Long(b)) => {
1688                a.get().cmp(&num_rational::Ratio::from(BigInt::from(*b)))
1689            }
1690            (Value::BigInt(a), Value::Ratio(b)) => {
1691                num_rational::Ratio::from(a.get().clone()).cmp(b.get())
1692            }
1693            (Value::Ratio(a), Value::BigInt(b)) => {
1694                a.get().cmp(&num_rational::Ratio::from(b.get().clone()))
1695            }
1696            (Value::Double(a), Value::BigInt(b)) => {
1697                total_cmp_f64(*a, b.get().to_f64().unwrap_or(f64::MAX))
1698            }
1699            (Value::BigInt(a), Value::Double(b)) => {
1700                total_cmp_f64(a.get().to_f64().unwrap_or(f64::MAX), *b)
1701            }
1702            (Value::Double(a), Value::Ratio(b)) => {
1703                total_cmp_f64(*a, b.get().to_f64().unwrap_or(f64::MAX))
1704            }
1705            (Value::Ratio(a), Value::Double(b)) => {
1706                total_cmp_f64(a.get().to_f64().unwrap_or(f64::MAX), *b)
1707            }
1708            (Value::Long(a), Value::BigDecimal(b)) => {
1709                let ad = bigdecimal::BigDecimal::from(*a);
1710                ad.partial_cmp(b.get())
1711                    .unwrap_or_else(|| ad.to_string().cmp(&b.get().to_string()))
1712            }
1713            (Value::BigDecimal(a), Value::Long(b)) => {
1714                let bd = bigdecimal::BigDecimal::from(*b);
1715                a.get()
1716                    .partial_cmp(&bd)
1717                    .unwrap_or_else(|| a.get().to_string().cmp(&bd.to_string()))
1718            }
1719            (Value::Double(a), Value::BigDecimal(b)) => {
1720                match bigdecimal::BigDecimal::try_from(*a) {
1721                    Ok(ad) => ad
1722                        .partial_cmp(b.get())
1723                        .unwrap_or_else(|| ad.to_string().cmp(&b.get().to_string())),
1724                    Err(_) => {
1725                        // NaN or infinity
1726                        if a.is_nan() {
1727                            Ordering::Greater
1728                        } else if *a < 0.0 {
1729                            Ordering::Less
1730                        } else {
1731                            Ordering::Greater
1732                        }
1733                    }
1734                }
1735            }
1736            (Value::BigDecimal(a), Value::Double(b)) => {
1737                match bigdecimal::BigDecimal::try_from(*b) {
1738                    Ok(bd) => a
1739                        .get()
1740                        .partial_cmp(&bd)
1741                        .unwrap_or_else(|| a.get().to_string().cmp(&bd.to_string())),
1742                    Err(_) => {
1743                        if b.is_nan() {
1744                            Ordering::Less
1745                        } else if *b < 0.0 {
1746                            Ordering::Greater
1747                        } else {
1748                            Ordering::Less
1749                        }
1750                    }
1751                }
1752            }
1753            (Value::BigInt(a), Value::BigDecimal(b)) => {
1754                let ad = bigdecimal::BigDecimal::from(a.get().clone());
1755                ad.partial_cmp(b.get())
1756                    .unwrap_or_else(|| ad.to_string().cmp(&b.get().to_string()))
1757            }
1758            (Value::BigDecimal(a), Value::BigInt(b)) => {
1759                let bd = bigdecimal::BigDecimal::from(b.get().clone());
1760                a.get()
1761                    .partial_cmp(&bd)
1762                    .unwrap_or_else(|| a.get().to_string().cmp(&bd.to_string()))
1763            }
1764            (Value::Ratio(a), Value::BigDecimal(b)) => {
1765                let af = a.get().to_f64().unwrap_or(f64::MAX);
1766                match bigdecimal::BigDecimal::try_from(af) {
1767                    Ok(ad) => ad
1768                        .partial_cmp(b.get())
1769                        .unwrap_or_else(|| ad.to_string().cmp(&b.get().to_string())),
1770                    Err(_) => Ordering::Greater,
1771                }
1772            }
1773            (Value::BigDecimal(a), Value::Ratio(b)) => {
1774                let bf = b.get().to_f64().unwrap_or(f64::MAX);
1775                match bigdecimal::BigDecimal::try_from(bf) {
1776                    Ok(bd) => a
1777                        .get()
1778                        .partial_cmp(&bd)
1779                        .unwrap_or_else(|| a.get().to_string().cmp(&bd.to_string())),
1780                    Err(_) => Ordering::Less,
1781                }
1782            }
1783
1784            // ── Characters ──
1785            (Value::Char(a), Value::Char(b)) => a.cmp(b),
1786
1787            // ── Strings ──
1788            (Value::Str(a), Value::Str(b)) => a.get().cmp(b.get()),
1789
1790            // ── Symbols ──
1791            (Value::Symbol(a), Value::Symbol(b)) => cmp_ns_name(
1792                &a.get().namespace,
1793                &a.get().name,
1794                &b.get().namespace,
1795                &b.get().name,
1796            ),
1797
1798            // ── Keywords ──
1799            (Value::Keyword(a), Value::Keyword(b)) => cmp_ns_name(
1800                &a.get().namespace,
1801                &a.get().name,
1802                &b.get().namespace,
1803                &b.get().name,
1804            ),
1805
1806            // ── Sequential collections: element-by-element ──
1807            (Value::Vector(a), Value::Vector(b)) => iter_cmp(a.get().iter(), b.get().iter()),
1808            (Value::List(a), Value::List(b)) => iter_cmp(a.get().iter(), b.get().iter()),
1809
1810            // ── Sets: compare by size, then elements ──
1811            (Value::Set(a), Value::Set(b)) => a.count().cmp(&b.count()),
1812
1813            // ── Maps: compare by size ──
1814            (Value::Map(a), Value::Map(b)) => a.count().cmp(&b.count()),
1815
1816            // ── Different types: order by type discriminant for a consistent total order ──
1817            _ => type_discriminant(self).cmp(&type_discriminant(other)),
1818        }
1819    }
1820}
1821
1822/// Compare two iterators of Values element-by-element.
1823fn iter_cmp<'a>(
1824    mut a: impl Iterator<Item = &'a Value>,
1825    mut b: impl Iterator<Item = &'a Value>,
1826) -> Ordering {
1827    loop {
1828        match (a.next(), b.next()) {
1829            (None, None) => return Ordering::Equal,
1830            (None, Some(_)) => return Ordering::Less,
1831            (Some(_), None) => return Ordering::Greater,
1832            (Some(x), Some(y)) => {
1833                let c = x.cmp(y);
1834                if c != Ordering::Equal {
1835                    return c;
1836                }
1837            }
1838        }
1839    }
1840}
1841
1842/// Total ordering for f64: NaN sorts after everything else, otherwise use IEEE total_order.
1843fn total_cmp_f64(a: f64, b: f64) -> Ordering {
1844    a.total_cmp(&b)
1845}
1846
1847/// Compare namespace-qualified names: namespace first (None < Some), then name.
1848fn cmp_ns_name(
1849    ns_a: &Option<Arc<str>>,
1850    name_a: &Arc<str>,
1851    ns_b: &Option<Arc<str>>,
1852    name_b: &Arc<str>,
1853) -> Ordering {
1854    match (ns_a, ns_b) {
1855        (None, None) => name_a.cmp(name_b),
1856        (None, Some(_)) => Ordering::Less,
1857        (Some(_), None) => Ordering::Greater,
1858        (Some(a), Some(b)) => a.cmp(b).then_with(|| name_a.cmp(name_b)),
1859    }
1860}
1861
1862/// Assign a stable integer to each Value variant for cross-type ordering.
1863fn type_discriminant(v: &Value) -> u8 {
1864    match v {
1865        Value::WithMeta(inner, _) => type_discriminant(inner),
1866        Value::Reduced(inner) => type_discriminant(inner),
1867        Value::Nil => 0,
1868        Value::Bool(_) => 1,
1869        Value::Long(_)
1870        | Value::Double(_)
1871        | Value::BigInt(_)
1872        | Value::BigDecimal(_)
1873        | Value::Ratio(_) => 2,
1874        Value::Char(_) => 3,
1875        Value::Str(_) => 4,
1876        Value::Symbol(_) => 5,
1877        Value::Keyword(_) => 6,
1878        Value::List(_) => 7,
1879        Value::Vector(_) => 8,
1880        Value::Map(_) => 9,
1881        Value::Set(_) => 10,
1882        Value::Queue(_) => 11,
1883        Value::LazySeq(_) => 12,
1884        Value::Cons(_) => 13,
1885        Value::NativeFunction(_) => 14,
1886        Value::BoundFn(_) => 14,
1887        Value::Fn(_) => 15,
1888        Value::Macro(_) => 16,
1889        Value::Var(_) => 17,
1890        Value::Atom(_) => 18,
1891        Value::SharedAtom(_) => 46,
1892        Value::ByteBlob(_) => 47,
1893        Value::Namespace(_) => 19,
1894        Value::Protocol(_) => 20,
1895        Value::ProtocolFn(_) => 21,
1896        Value::MultiFn(_) => 22,
1897        Value::Volatile(_) => 23,
1898        Value::Delay(_) => 24,
1899        Value::Promise(_) => 25,
1900        Value::Future(_) => 26,
1901        Value::Agent(_) => 27,
1902        Value::TypeInstance(_) => 28,
1903        Value::BooleanArray(_) => 29,
1904        Value::ByteArray(_) => 30,
1905        Value::ShortArray(_) => 31,
1906        Value::IntArray(_) => 32,
1907        Value::LongArray(_) => 33,
1908        Value::CharArray(_) => 34,
1909        Value::FloatArray(_) => 35,
1910        Value::DoubleArray(_) => 36,
1911        Value::ObjectArray(_) => 37,
1912        Value::Uuid(_) => 38,
1913        Value::NativeObject(_) => 43,
1914        Value::Resource(_) => 39,
1915        Value::TransientMap(_) => 40,
1916        Value::TransientSet(_) => 41,
1917        Value::TransientVector(_) => 42,
1918        Value::Pattern(_) => 43,
1919        Value::Matcher(_) => 44,
1920        Value::Error(_) => 45,
1921    }
1922}