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::{ByteBlob, 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<SharedSlot>>`.
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<ByteBlob>),
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 = crate::realization::for_trait(ls.get());
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 = crate::realization::for_trait(ls.get());
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 = crate::realization::for_trait(ls.get());
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 = crate::realization::for_trait(ls.get());
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 values = a.get().0.lock().unwrap().clone();
747                crate::collections::hash_values(values)
748            }
749
750            Value::SharedAtom(a) => Arc::as_ptr(a) as u32,
751            Value::ByteBlob(b) => {
752                let mut h: u32 = 0;
753                for byte in b.iter() {
754                    h = hash_combine_ordered(h, hash_i64(*byte as i64));
755                }
756                h
757            }
758
759            // For non-data types, use pointer identity.
760            Value::Fn(f) => f.get() as *const _ as u32,
761            Value::BoundFn(f) => f.get() as *const _ as u32,
762            Value::NativeFunction(f) => f.get() as *const _ as u32,
763            Value::Var(v) => v.get() as *const _ as u32,
764            Value::Atom(a) => a.get() as *const _ as u32,
765            Value::Namespace(n) => n.get() as *const _ as u32,
766            Value::Queue(q) => {
767                let mut h: u32 = 1;
768                for v in q.get().iter() {
769                    h = hash_combine_ordered(h, v.clojure_hash());
770                }
771                h
772            }
773            Value::Macro(f) => f.get() as *const _ as u32,
774            Value::BigDecimal(d) => hash_string(&d.get().to_string()),
775            Value::Ratio(r) => hash_string(&r.get().to_string()),
776            Value::LazySeq(ls) => crate::realization::for_trait(ls.get()).clojure_hash(),
777            Value::Protocol(p) => p.get() as *const _ as u32,
778            Value::ProtocolFn(pf) => pf.get() as *const _ as u32,
779            Value::MultiFn(mf) => mf.get() as *const _ as u32,
780            Value::Cons(_) => {
781                // Hash like an ordered sequence.
782                let mut h: u32 = 1;
783                for v in value_to_seq_vec(self) {
784                    h = hash_combine_ordered(h, v.clojure_hash());
785                }
786                h
787            }
788            // Pointer identity for concurrency primitives.
789            Value::Volatile(v) => v.get() as *const _ as u32,
790            Value::Delay(d) => d.get() as *const _ as u32,
791            Value::Promise(p) => p.get() as *const _ as u32,
792            Value::Future(fu) => fu.get() as *const _ as u32,
793            Value::Agent(a) => a.get() as *const _ as u32,
794            // Record hash: combine type tag hash with fields hash.
795            Value::TypeInstance(ti) => {
796                let tag_hash = hash_string(&ti.get().type_tag);
797                let mut fields_hash: u32 = 0;
798                ti.get().fields.for_each(|k, v| {
799                    fields_hash = hash_combine_unordered(
800                        fields_hash,
801                        hash_combine_ordered(k.clojure_hash(), v.clojure_hash()),
802                    );
803                });
804                hash_combine_ordered(tag_hash, fields_hash)
805            }
806            Value::Error(e) => e.get().clojure_hash(),
807        }
808    }
809}
810
811// Implement std::hash::Hash by delegating to ClojureHash.
812impl std::hash::Hash for Value {
813    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
814        self.clojure_hash().hash(state);
815    }
816}
817
818// ── Display / pr-str ──────────────────────────────────────────────────────────
819
820impl fmt::Display for Value {
821    /// Prints in `pr-str` style (readable): strings are quoted, chars use `\` notation.
822    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
823        pr_str(self, f, true)
824    }
825}
826
827/// A wrapper for printing a Value non-readably (for `str`, `println`).
828pub struct PrintValue<'a>(pub &'a Value);
829
830impl fmt::Display for PrintValue<'_> {
831    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
832        pr_str(self.0, f, false)
833    }
834}
835
836/// Print a value.  `readably = true` quotes strings and escapes chars.
837pub fn pr_str(v: &Value, f: &mut fmt::Formatter<'_>, readably: bool) -> fmt::Result {
838    match v {
839        Value::WithMeta(inner, _) => pr_str(inner, f, readably),
840        Value::Reduced(inner) => {
841            write!(f, "#reduced ")?;
842            pr_str(inner, f, readably)
843        }
844        Value::Nil => write!(f, "nil"),
845        Value::Bool(b) => write!(f, "{b}"),
846        Value::Long(n) => write!(f, "{n}"),
847        Value::Double(d) => {
848            if d.is_infinite() {
849                if readably {
850                    if *d > 0.0 {
851                        write!(f, "##Inf")
852                    } else {
853                        write!(f, "##-Inf")
854                    }
855                } else if *d > 0.0 {
856                    write!(f, "Infinity")
857                } else {
858                    write!(f, "-Infinity")
859                }
860            } else if d.is_nan() {
861                if readably {
862                    write!(f, "##NaN")
863                } else {
864                    write!(f, "NaN")
865                }
866            } else if d.fract() == 0.0 && d.abs() < 1e15 {
867                write!(f, "{d:.1}")
868            } else {
869                write!(f, "{d}")
870            }
871        }
872        Value::BigInt(n) => {
873            if readably {
874                write!(f, "{}N", n.get())
875            } else {
876                write!(f, "{}", n.get())
877            }
878        }
879        Value::BigDecimal(d) => {
880            let dec = d.get();
881            let s = format!("{}", dec);
882            // bigdecimal Display omits trailing zeros for zero values (e.g. 0.0 → "0").
883            // Preserve scale: if the value has fractional digits but displays without a dot, add them.
884            if !s.contains('.') && dec.fractional_digit_count() > 0 {
885                let zeros = "0".repeat(dec.fractional_digit_count() as usize);
886                if readably {
887                    write!(f, "{s}.{zeros}M")
888                } else {
889                    write!(f, "{s}.{zeros}")
890                }
891            } else if readably {
892                write!(f, "{s}M")
893            } else {
894                write!(f, "{s}")
895            }
896        }
897        Value::Ratio(r) => write!(f, "{}", r.get()),
898        Value::Uuid(u) => {
899            let uuid = uuid::Uuid::from_u128(u.get().0);
900            if readably {
901                write!(f, "#uuid \"{}\"", uuid)
902            } else {
903                write!(f, "{}", uuid)
904            }
905        }
906        Value::Char(c) => {
907            if readably {
908                match c {
909                    '\n' => write!(f, "\\newline"),
910                    '\t' => write!(f, "\\tab"),
911                    ' ' => write!(f, "\\space"),
912                    '\r' => write!(f, "\\return"),
913                    c => write!(f, "\\{c}"),
914                }
915            } else {
916                write!(f, "{c}")
917            }
918        }
919        Value::Str(s) => {
920            if readably {
921                write!(f, "\"")?;
922                for c in s.get().chars() {
923                    match c {
924                        '"' => write!(f, "\\\"")?,
925                        '\\' => write!(f, "\\\\")?,
926                        '\n' => write!(f, "\\n")?,
927                        '\t' => write!(f, "\\t")?,
928                        '\r' => write!(f, "\\r")?,
929                        c => write!(f, "{c}")?,
930                    }
931                }
932                write!(f, "\"")
933            } else {
934                write!(f, "{}", s.get())
935            }
936        }
937        Value::Pattern(r) => {
938            if readably {
939                write!(f, "#\"")?;
940                write!(f, "{}", r.get().as_str())?;
941                write!(f, "\"")
942            } else {
943                write!(f, "#<{}>", r.get())
944            }
945        }
946        Value::Matcher(_) => write!(f, "#<Matcher>"),
947        Value::Symbol(s) => write!(f, "{}", s.get()),
948        Value::Keyword(k) => write!(f, "{}", k.get()),
949        Value::List(l) => {
950            write!(f, "(")?;
951            let mut first = true;
952            for item in l.get().iter() {
953                if !first {
954                    write!(f, " ")?;
955                }
956                pr_str(item, f, readably)?;
957                first = false;
958            }
959            write!(f, ")")
960        }
961        Value::Vector(v) => {
962            write!(f, "[")?;
963            let mut first = true;
964            for item in v.get().iter() {
965                if !first {
966                    write!(f, " ")?;
967                }
968                pr_str(item, f, readably)?;
969                first = false;
970            }
971            write!(f, "]")
972        }
973        Value::Map(m) => {
974            write!(f, "{{")?;
975            let mut first = true;
976            m.for_each(|k, v| {
977                // Ignore fmt errors inside the closure — limitations of fmt.
978                if !first {
979                    let _ = write!(f, ", ");
980                }
981                let _ = pr_str(k, f, readably);
982                let _ = write!(f, " ");
983                let _ = pr_str(v, f, readably);
984                first = false;
985            });
986            write!(f, "}}")
987        }
988        Value::Set(s) => {
989            write!(f, "#{{")?;
990            let mut first = true;
991            for item in s.iter() {
992                if !first {
993                    write!(f, " ")?;
994                }
995                pr_str(item, f, readably)?;
996                first = false;
997            }
998            write!(f, "}}")
999        }
1000        Value::BooleanArray(_)
1001        | Value::ByteArray(_)
1002        | Value::ShortArray(_)
1003        | Value::IntArray(_)
1004        | Value::LongArray(_)
1005        | Value::CharArray(_)
1006        | Value::FloatArray(_)
1007        | Value::DoubleArray(_)
1008        | Value::ObjectArray(_) => write!(f, "#[array]"),
1009        Value::Queue(q) => {
1010            // Printed as a list with a type tag.
1011            write!(f, "#queue (")?;
1012            let mut first = true;
1013            for item in q.get().iter() {
1014                if !first {
1015                    write!(f, " ")?;
1016                }
1017                pr_str(item, f, readably)?;
1018                first = false;
1019            }
1020            write!(f, ")")
1021        }
1022        Value::LazySeq(ls) => pr_str(&crate::realization::for_trait(ls.get()), f, readably),
1023        Value::Cons(c) => {
1024            write!(f, "(")?;
1025            pr_str(&c.get().head, f, readably)?;
1026            let mut tail = c.get().tail.clone();
1027            loop {
1028                match tail {
1029                    Value::Nil => break,
1030                    Value::List(l) => {
1031                        for item in l.get().iter() {
1032                            write!(f, " ")?;
1033                            pr_str(item, f, readably)?;
1034                        }
1035                        break;
1036                    }
1037                    Value::Cons(next_c) => {
1038                        write!(f, " ")?;
1039                        pr_str(&next_c.get().head, f, readably)?;
1040                        tail = next_c.get().tail.clone();
1041                    }
1042                    Value::LazySeq(ls) => {
1043                        tail = crate::realization::for_trait(ls.get());
1044                    }
1045                    other => {
1046                        write!(f, " . ")?;
1047                        pr_str(&other, f, readably)?;
1048                        break;
1049                    }
1050                }
1051            }
1052            write!(f, ")")
1053        }
1054        Value::NativeFunction(nf) => write!(f, "#<NativeFn {}>", nf.get().name),
1055        Value::BoundFn(_) => write!(f, "#<BoundFn>"),
1056        Value::Fn(fun) => match &fun.get().name {
1057            Some(n) => write!(f, "#<Fn {n}>"),
1058            None => write!(f, "#<Fn>"),
1059        },
1060        Value::Macro(m) => match &m.get().name {
1061            Some(n) => write!(f, "#<Macro {n}>"),
1062            None => write!(f, "#<Macro>"),
1063        },
1064        Value::Var(v) => write!(f, "#'{}/{}", v.get().namespace, v.get().name),
1065        Value::Atom(a) => write!(f, "#<Atom {}>", a.get().deref()),
1066        Value::SharedAtom(a) => {
1067            write!(f, "#<SharedAtom {}>", a.deref_val().type_name())
1068        }
1069        Value::ByteBlob(b) => write!(f, "#<ByteBlob {} bytes>", b.len()),
1070        Value::Namespace(n) => write!(f, "#<Namespace {}>", n.get().name),
1071        Value::Protocol(p) => write!(f, "#<Protocol {}>", p.get().name),
1072        Value::ProtocolFn(pf) => {
1073            write!(
1074                f,
1075                "#<fn {}/{}>",
1076                pf.get().protocol.get().name,
1077                pf.get().method_name
1078            )
1079        }
1080        Value::MultiFn(mf) => write!(f, "#<MultiFn {}>", mf.get().name),
1081        Value::Volatile(_) => write!(f, "#<Volatile>"),
1082        Value::Delay(_) => write!(f, "#<Delay>"),
1083        Value::Promise(_) => write!(f, "#<Promise>"),
1084        Value::Future(_) => write!(f, "#<Future>"),
1085        Value::Agent(_) => write!(f, "#<Agent>"),
1086        Value::TypeInstance(ti) => {
1087            let ti = ti.get();
1088            write!(f, "#{}{{", ti.type_tag)?;
1089            let mut first = true;
1090            ti.fields.for_each(|k, v| {
1091                if !first {
1092                    let _ = write!(f, ", ");
1093                }
1094                let _ = pr_str(k, f, readably);
1095                let _ = write!(f, " ");
1096                let _ = pr_str(v, f, readably);
1097                first = false;
1098            });
1099            write!(f, "}}")
1100        }
1101        Value::NativeObject(obj) => {
1102            write!(f, "#<{} {:?}>", obj.get().type_tag(), obj.get().inner())
1103        }
1104        Value::Resource(r) => {
1105            if r.is_closed() {
1106                write!(f, "#<{} (closed)>", r.resource_type())
1107            } else {
1108                write!(f, "#<{}>", r.resource_type())
1109            }
1110        }
1111        Value::TransientMap(_) => write!(f, "#<TransientMap>"),
1112        Value::TransientSet(_) => write!(f, "#<TransientSet>"),
1113        Value::TransientVector(_) => write!(f, "#<TransientVector>"),
1114        Value::Error(e) => {
1115            write!(f, "#error ")?;
1116            let map = e.get().to_map().map_err(|_| fmt::Error {})?;
1117            pr_str(&map, f, readably)
1118        }
1119    }
1120}
1121
1122// ── Keyword-argument rest normalization ──────────────────────────────────────
1123
1124impl Value {
1125    /// Normalize a variadic rest sequence into the value a *map-shaped* rest
1126    /// pattern (`[& {:keys [a b]}]`) destructures against.
1127    ///
1128    /// This is Clojure's `seq-to-map-for-destructuring`, and every execution
1129    /// tier must agree on it — the tree-walker calls it from `bind_fn_params`,
1130    /// the IR interpreter and the compiled tiers reach it through
1131    /// `KnownFn::KwargsMap` / `rt_kwargs_map`.  A divergence here is issue
1132    /// #368.
1133    ///
1134    /// - Zero or one argument: the lone value *is* the map, returned verbatim.
1135    ///   `(f {:a 1})` therefore keeps the caller's map identity — a sorted map
1136    ///   stays sorted, metadata stays attached — and `(f)` / `(f nil)` bind
1137    ///   `nil`, which reads like an empty map for every `:keys` lookup.
1138    /// - Otherwise the arguments are alternating key/value pairs, optionally
1139    ///   followed by a trailing map whose entries win over the pairs before it
1140    ///   (Clojure 1.11's keyword-argument calling convention).
1141    ///
1142    /// An odd argument count with no trailing map is a caller error, reported
1143    /// the way Clojure reports it rather than dropping the dangling key.
1144    pub fn from_kwargs_rest(mut entries: Vec<Value>) -> Result<Value, ValueError> {
1145        if entries.len() < 2 {
1146            return Ok(entries.pop().unwrap_or(Value::Nil));
1147        }
1148
1149        // A trailing map is only a trailing map when what precedes it pairs up
1150        // evenly; `(f :a {:x 1})` passes a map as `:a`'s *value*.  Match through
1151        // any `WithMeta` wrapper — a map carrying metadata is still a map.
1152        let trailing = match entries.last().map(Value::unwrap_meta) {
1153            Some(Value::Map(_)) if !entries.len().is_multiple_of(2) => entries.pop(),
1154            _ => None,
1155        };
1156
1157        if !entries.len().is_multiple_of(2) {
1158            let key = entries.last().expect("odd length implies a last entry");
1159            return Err(ValueError::Other(format!(
1160                "No value supplied for key: {}",
1161                PrintValue(key)
1162            )));
1163        }
1164
1165        let mut result = MapValue::from_flat_entries(entries);
1166        if let Some(Value::Map(map)) = trailing.as_ref().map(Value::unwrap_meta) {
1167            for (key, value) in map.iter() {
1168                result = result.assoc(key.clone(), value.clone());
1169            }
1170        }
1171        Ok(Value::Map(result))
1172    }
1173}
1174
1175// ── Metadata helpers ─────────────────────────────────────────────────────────
1176
1177impl Value {
1178    /// Strip any `WithMeta` wrapper, returning the underlying value.
1179    pub fn unwrap_meta(&self) -> &Value {
1180        match self {
1181            Value::WithMeta(inner, _) => inner.unwrap_meta(),
1182            other => other,
1183        }
1184    }
1185
1186    /// Return metadata if present, or `None`.
1187    pub fn get_meta(&self) -> Option<&Value> {
1188        match self {
1189            Value::WithMeta(_, meta) => Some(meta),
1190            _ => None,
1191        }
1192    }
1193
1194    /// Return a new value with metadata attached.
1195    pub fn with_meta(self, meta: Value) -> Value {
1196        match self {
1197            Value::WithMeta(inner, _) => Value::WithMeta(inner, Box::new(meta)),
1198            other => Value::WithMeta(Box::new(other), Box::new(meta)),
1199        }
1200    }
1201}
1202
1203// ── type_name helper ──────────────────────────────────────────────────────────
1204
1205impl Value {
1206    /// A human-readable type name for error messages.
1207    pub fn type_name(&self) -> &'static str {
1208        match self {
1209            Value::WithMeta(inner, _) => inner.type_name(),
1210            Value::Reduced(_) => "reduced",
1211            Value::Nil => "nil",
1212            Value::Bool(_) => "boolean",
1213            Value::Long(_) => "long",
1214            Value::Double(_) => "double",
1215            Value::BigInt(_) => "bigint",
1216            Value::BigDecimal(_) => "bigdecimal",
1217            Value::Ratio(_) => "ratio",
1218            Value::Char(_) => "char",
1219            Value::Str(_) => "string",
1220            Value::Pattern(_) => "pattern",
1221            Value::Matcher(_) => "matcher",
1222            Value::Symbol(_) => "symbol",
1223            Value::Keyword(_) => "keyword",
1224            Value::Uuid(_) => "uuid",
1225            Value::List(_) => "list",
1226            Value::Vector(_) => "vector",
1227            Value::Map(_) => "map",
1228            Value::Set(_) => "set",
1229            Value::Queue(_) => "queue",
1230            Value::NativeFunction(_)
1231            | Value::Fn(_)
1232            | Value::BoundFn(_)
1233            | Value::Macro(_)
1234            | Value::ProtocolFn(_)
1235            | Value::MultiFn(_) => "fn",
1236            Value::Var(_) => "var",
1237            Value::Atom(_) => "atom",
1238            Value::SharedAtom(_) => "shared-atom",
1239            Value::ByteBlob(_) => "byte-blob",
1240            Value::Namespace(_) => "namespace",
1241            Value::LazySeq(_) => "lazyseq",
1242            Value::Cons(_) => "cons",
1243            Value::Protocol(_) => "protocol",
1244            Value::Volatile(_) => "volatile",
1245            Value::Delay(_) => "delay",
1246            Value::Promise(_) => "promise",
1247            Value::Future(_) => "future",
1248            Value::Agent(_) => "agent",
1249            Value::TypeInstance(_) => "record",
1250            Value::NativeObject(_) => "native-object",
1251            Value::BooleanArray(_) => "boolean-array",
1252            Value::ByteArray(_) => "byte-array",
1253            Value::ShortArray(_) => "short-array",
1254            Value::IntArray(_) => "int-array",
1255            Value::LongArray(_) => "long-array",
1256            Value::FloatArray(_) => "float-array",
1257            Value::DoubleArray(_) => "double-array",
1258            Value::CharArray(_) => "char-array",
1259            Value::ObjectArray(_) => "object-array",
1260            Value::Resource(r) => r.resource_type(),
1261            Value::TransientMap(_) => "transient-map",
1262            Value::TransientSet(_) => "transient-set",
1263            Value::TransientVector(_) => "transient-vector",
1264            Value::Error(_) => "error",
1265        }
1266    }
1267
1268    /// Convenience: wrap a `&str` in `Value::Str`.
1269    pub fn string(s: impl Into<String>) -> Self {
1270        Value::Str(GcPtr::new(s.into()))
1271    }
1272
1273    /// Construct a UUID value with distinct object identity.
1274    pub fn uuid(value: u128) -> Self {
1275        Value::Uuid(GcPtr::new(UuidValue(value)))
1276    }
1277
1278    /// Convenience: build a `[key val]` map entry (a tagged 2-element vector).
1279    pub fn map_entry(key: Value, val: Value) -> Self {
1280        Value::Vector(GcPtr::new(PersistentVector::map_entry(key, val)))
1281    }
1282
1283    /// True only for map entries (`[k v]` pairs from seq'ing a map, `find`,
1284    /// or the `map-entry` constructor) — not for plain 2-element vectors.
1285    pub fn is_map_entry(&self) -> bool {
1286        matches!(self.unwrap_meta(), Value::Vector(v) if v.get().is_map_entry())
1287    }
1288
1289    /// Convenience: wrap a `Symbol`.
1290    pub fn symbol(s: Symbol) -> Self {
1291        Value::Symbol(GcPtr::new(s))
1292    }
1293
1294    /// Convenience: wrap a `Keyword`.
1295    pub fn keyword(k: Keyword) -> Self {
1296        Value::Keyword(GcPtr::new(k))
1297    }
1298
1299    /// True for sequential collections (list, vector, lazy seq, cons).
1300    pub fn is_sequential(&self) -> bool {
1301        matches!(
1302            self,
1303            Value::List(_) | Value::Vector(_) | Value::LazySeq(_) | Value::Cons(_)
1304        )
1305    }
1306
1307    /// True for any collection.
1308    pub fn is_coll(&self) -> bool {
1309        self.unwrap_meta().is_coll_inner()
1310    }
1311
1312    fn is_coll_inner(&self) -> bool {
1313        matches!(
1314            self,
1315            Value::List(_)
1316                | Value::Vector(_)
1317                | Value::Map(_)
1318                | Value::Set(_)
1319                | Value::Queue(_)
1320                | Value::LazySeq(_)
1321                | Value::Cons(_)
1322        )
1323    }
1324}
1325
1326impl cljrs_gc::Trace for Value {
1327    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1328        use cljrs_gc::GcVisitor as _;
1329        match self {
1330            Value::Reduced(inner) => inner.trace(visitor),
1331            Value::WithMeta(inner, meta) => {
1332                inner.trace(visitor);
1333                meta.trace(visitor);
1334            }
1335            Value::Nil | Value::Bool(_) | Value::Long(_) | Value::Double(_) | Value::Char(_) => {}
1336            Value::Uuid(p) => visitor.visit(p),
1337            Value::BigInt(p) => visitor.visit(p),
1338            Value::BigDecimal(p) => visitor.visit(p),
1339            Value::Ratio(p) => visitor.visit(p),
1340            Value::Str(p) => visitor.visit(p),
1341            Value::Pattern(p) => visitor.visit(p),
1342            Value::Matcher(m) => visitor.visit(m),
1343            Value::Symbol(p) => visitor.visit(p),
1344            Value::Keyword(p) => visitor.visit(p),
1345            Value::List(p) => visitor.visit(p),
1346            Value::Vector(p) => visitor.visit(p),
1347            Value::Map(m) => m.trace(visitor),
1348            Value::Set(s) => s.trace(visitor),
1349            Value::Queue(p) => visitor.visit(p),
1350            Value::NativeFunction(p) => visitor.visit(p),
1351            Value::BoundFn(p) => visitor.visit(p),
1352            Value::Fn(p) | Value::Macro(p) => visitor.visit(p),
1353            Value::Var(p) => visitor.visit(p),
1354            Value::Atom(p) => visitor.visit(p),
1355            Value::Namespace(p) => visitor.visit(p),
1356            Value::LazySeq(p) => visitor.visit(p),
1357            Value::Cons(p) => visitor.visit(p),
1358            Value::Protocol(p) => visitor.visit(p),
1359            Value::ProtocolFn(p) => visitor.visit(p),
1360            Value::MultiFn(p) => visitor.visit(p),
1361            Value::Volatile(p) => visitor.visit(p),
1362            Value::Delay(p) => visitor.visit(p),
1363            Value::Promise(p) => visitor.visit(p),
1364            Value::Future(p) => visitor.visit(p),
1365            Value::Agent(p) => visitor.visit(p),
1366            Value::TypeInstance(p) => visitor.visit(p),
1367            Value::ObjectArray(p) => visitor.visit(p),
1368            // Primitive arrays have no child Values, but their boxes live on
1369            // the GC heap and must themselves be marked.
1370            Value::BooleanArray(p) => visitor.visit(p),
1371            Value::ByteArray(p) => visitor.visit(p),
1372            Value::ShortArray(p) => visitor.visit(p),
1373            Value::IntArray(p) => visitor.visit(p),
1374            Value::LongArray(p) => visitor.visit(p),
1375            Value::FloatArray(p) => visitor.visit(p),
1376            Value::DoubleArray(p) => visitor.visit(p),
1377            Value::CharArray(p) => visitor.visit(p),
1378            Value::NativeObject(p) => visitor.visit(p),
1379            // Resource, SharedAtom, and ByteBlob are Arc-ref-counted outside the
1380            // GC heap — nothing to trace through the GC visitor.
1381            Value::Resource(_) | Value::SharedAtom(_) | Value::ByteBlob(_) => {}
1382            Value::TransientMap(m) => visitor.visit(m),
1383            Value::TransientVector(p) => visitor.visit(p),
1384            Value::TransientSet(m) => visitor.visit(m),
1385            Value::Error(e) => visitor.visit(e),
1386        }
1387    }
1388}
1389
1390impl cljrs_gc::Trace for MapValue {
1391    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1392        use cljrs_gc::GcVisitor as _;
1393        match self {
1394            MapValue::Array(p) => visitor.visit(p),
1395            MapValue::Hash(p) => visitor.visit(p),
1396            MapValue::Sorted(p) => visitor.visit(p),
1397        }
1398    }
1399}
1400
1401// ── TypeInstance ──────────────────────────────────────────────────────────────
1402
1403/// A record or reify instance.  `type_tag` identifies the concrete type;
1404/// `fields` holds the key/value pairs (keyword → value).
1405#[derive(Clone, Debug)]
1406pub struct TypeInstance {
1407    pub type_tag: Arc<str>,
1408    pub fields: MapValue,
1409    /// Mutable `deftype` fields (`^:unsynchronized-mutable` /
1410    /// `^:volatile-mutable`), held in an interior-mutable cell — an `Atom` over
1411    /// a keyword→value map — so `set!` can update them in place. `None` for
1412    /// `defrecord`, `reify`, and immutable `deftype`s. Instances are `!Send`,
1413    /// so one shared cell needs no stronger volatility than an `Atom`.
1414    pub mutable: Option<GcPtr<crate::types::Atom>>,
1415}
1416
1417impl cljrs_gc::Trace for TypeInstance {
1418    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1419        use cljrs_gc::GcVisitor as _;
1420        self.fields.trace(visitor);
1421        if let Some(m) = &self.mutable {
1422            visitor.visit(m);
1423        }
1424    }
1425}
1426
1427#[allow(clippy::items_after_test_module)]
1428#[cfg(test)]
1429mod tests {
1430    use super::*;
1431
1432    fn kw(s: &str) -> Value {
1433        Value::keyword(Keyword::simple(s))
1434    }
1435    #[allow(dead_code)]
1436    fn sym(s: &str) -> Value {
1437        Value::symbol(Symbol::simple(s))
1438    }
1439    fn int(n: i64) -> Value {
1440        Value::Long(n)
1441    }
1442    fn s(v: &str) -> Value {
1443        Value::string(v)
1444    }
1445
1446    // ── Equality ──────────────────────────────────────────────────────────────
1447
1448    #[test]
1449    fn test_nil_eq() {
1450        assert_eq!(Value::Nil, Value::Nil);
1451        assert_ne!(Value::Nil, int(0));
1452    }
1453
1454    #[test]
1455    fn test_numeric_cross_type() {
1456        let big1 = Value::BigInt(GcPtr::new(BigInt::from(1i64)));
1457        assert_eq!(int(1), big1.clone());
1458        assert_eq!(big1, int(1));
1459        // 1.0 == 1
1460        assert_eq!(Value::Double(1.0), int(1));
1461        assert_eq!(int(1), Value::Double(1.0));
1462        // 1.5 != 1
1463        assert_ne!(Value::Double(1.5), int(1));
1464    }
1465
1466    #[test]
1467    fn test_nan_not_equal_to_itself() {
1468        let nan = Value::Double(f64::NAN);
1469        assert_ne!(nan, nan.clone());
1470    }
1471
1472    #[test]
1473    fn test_string_equality() {
1474        assert_eq!(s("hello"), s("hello"));
1475        assert_ne!(s("hello"), s("world"));
1476    }
1477
1478    #[test]
1479    fn test_list_vector_seq_equality() {
1480        let list = Value::List(GcPtr::new(PersistentList::from_iter([int(1), int(2)])));
1481        let vec = Value::Vector(GcPtr::new(PersistentVector::from_iter([int(1), int(2)])));
1482        // Clojure: (= '(1 2) [1 2]) => true
1483        assert_eq!(list, vec);
1484    }
1485
1486    #[test]
1487    fn test_map_equality_order_independent() {
1488        let mut a = MapValue::empty();
1489        a = a.assoc(kw("a"), int(1));
1490        a = a.assoc(kw("b"), int(2));
1491
1492        let mut b = MapValue::empty();
1493        b = b.assoc(kw("b"), int(2));
1494        b = b.assoc(kw("a"), int(1));
1495
1496        assert_eq!(Value::Map(a), Value::Map(b));
1497    }
1498
1499    // ── Hashing ───────────────────────────────────────────────────────────────
1500
1501    #[test]
1502    fn test_hash_consistency() {
1503        let big1 = Value::BigInt(GcPtr::new(BigInt::from(1i64)));
1504        assert_eq!(int(1).clojure_hash(), big1.clojure_hash());
1505    }
1506
1507    #[test]
1508    fn test_hash_whole_double() {
1509        // (= 1 1.0) → true, so (hash 1) == (hash 1.0)
1510        assert_eq!(int(1).clojure_hash(), Value::Double(1.0).clojure_hash());
1511    }
1512
1513    // ── Display ───────────────────────────────────────────────────────────────
1514
1515    #[test]
1516    fn test_pr_str_nil() {
1517        assert_eq!(Value::Nil.to_string(), "nil");
1518    }
1519
1520    #[test]
1521    fn test_pr_str_string() {
1522        assert_eq!(s("hello").to_string(), "\"hello\"");
1523        assert_eq!(s("a\"b").to_string(), "\"a\\\"b\"");
1524    }
1525
1526    #[test]
1527    fn test_pr_str_char() {
1528        assert_eq!(Value::Char('a').to_string(), "\\a");
1529        assert_eq!(Value::Char('\n').to_string(), "\\newline");
1530    }
1531
1532    #[test]
1533    fn test_pr_str_list() {
1534        let l = Value::List(GcPtr::new(PersistentList::from_iter([int(1), int(2)])));
1535        assert_eq!(l.to_string(), "(1 2)");
1536    }
1537
1538    #[test]
1539    fn test_pr_str_vector() {
1540        let v = Value::Vector(GcPtr::new(PersistentVector::from_iter([int(1), int(2)])));
1541        assert_eq!(v.to_string(), "[1 2]");
1542    }
1543
1544    #[test]
1545    #[allow(clippy::approx_constant)]
1546    fn test_pr_str_double() {
1547        assert_eq!(Value::Double(1.0).to_string(), "1.0");
1548        assert_eq!(Value::Double(3.14).to_string(), "3.14");
1549        assert_eq!(Value::Double(f64::INFINITY).to_string(), "##Inf");
1550        assert_eq!(Value::Double(f64::NEG_INFINITY).to_string(), "##-Inf");
1551        assert_eq!(Value::Double(f64::NAN).to_string(), "##NaN");
1552    }
1553
1554    // ── Keyword-argument rest normalization ──────────────────────────────────
1555
1556    fn kwargs(entries: Vec<Value>) -> Value {
1557        Value::from_kwargs_rest(entries).expect("well-formed kwargs")
1558    }
1559
1560    fn map_of(entries: Vec<Value>) -> Value {
1561        Value::Map(MapValue::from_flat_entries(entries))
1562    }
1563
1564    #[test]
1565    fn kwargs_rest_empty_and_lone_values_pass_through() {
1566        // `(f)` and `(f nil)` both bind nil, as in Clojure — `:as` sees the
1567        // rest value itself, so an empty map here would be observable.
1568        assert_eq!(kwargs(vec![]), Value::Nil);
1569        assert_eq!(kwargs(vec![Value::Nil]), Value::Nil);
1570        // A lone argument is the map verbatim, whatever it is: no rebuild, so
1571        // map identity (and with it sortedness and metadata) survives, and a
1572        // non-map is handed on for `get` to return nil against.
1573        assert_eq!(kwargs(vec![kw("a")]), kw("a"));
1574        let sorted = Value::Map(MapValue::Sorted(GcPtr::new(SortedMap::from_pairs(vec![
1575            (kw("b"), int(1)),
1576            (kw("a"), int(2)),
1577        ]))));
1578        assert!(matches!(
1579            kwargs(vec![sorted.clone()]),
1580            Value::Map(MapValue::Sorted(_))
1581        ));
1582        let with_meta = map_of(vec![kw("a"), int(1)]).with_meta(map_of(vec![kw("t"), int(1)]));
1583        assert_eq!(kwargs(vec![with_meta.clone()]), with_meta);
1584    }
1585
1586    #[test]
1587    fn kwargs_rest_pairs_build_a_map() {
1588        assert_eq!(
1589            kwargs(vec![kw("a"), int(1), kw("b"), int(2)]),
1590            map_of(vec![kw("a"), int(1), kw("b"), int(2)])
1591        );
1592        // Duplicate keys: last wins, as for a map literal built by assoc.
1593        assert_eq!(
1594            kwargs(vec![kw("a"), int(1), kw("a"), int(2)]),
1595            map_of(vec![kw("a"), int(2)])
1596        );
1597    }
1598
1599    #[test]
1600    fn kwargs_rest_trailing_map_merges_and_wins() {
1601        // `(f :a 1 {:b 2})` — the trailing map's entries join the pairs...
1602        assert_eq!(
1603            kwargs(vec![kw("a"), int(1), map_of(vec![kw("b"), int(2)])]),
1604            map_of(vec![kw("a"), int(1), kw("b"), int(2)])
1605        );
1606        // ...and beat them on a clash.
1607        assert_eq!(
1608            kwargs(vec![kw("a"), int(1), map_of(vec![kw("a"), int(9)])]),
1609            map_of(vec![kw("a"), int(9)])
1610        );
1611        // A map carrying metadata is still a trailing map: `with_meta` wraps
1612        // rather than annotates, so matching the raw `Value` would miss it.
1613        let meta_map = map_of(vec![kw("b"), int(2)]).with_meta(map_of(vec![kw("t"), int(1)]));
1614        assert_eq!(
1615            kwargs(vec![kw("a"), int(1), meta_map]),
1616            map_of(vec![kw("a"), int(1), kw("b"), int(2)])
1617        );
1618    }
1619
1620    #[test]
1621    fn kwargs_rest_map_in_value_position_is_not_a_trailing_map() {
1622        // Even arity: the map is `:a`'s value, not a trailing map to merge.
1623        let inner = map_of(vec![kw("x"), int(1)]);
1624        assert_eq!(
1625            kwargs(vec![kw("a"), inner.clone()]),
1626            map_of(vec![kw("a"), inner])
1627        );
1628    }
1629
1630    #[test]
1631    fn kwargs_rest_dangling_key_is_an_error() {
1632        // Clojure's message, and an error rather than the silently dropped
1633        // argument a bare `chunks(2)` would produce.
1634        let err = Value::from_kwargs_rest(vec![kw("a"), int(1), kw("b")])
1635            .expect_err("odd arity with no trailing map");
1636        assert_eq!(err.to_string(), "No value supplied for key: :b");
1637    }
1638}
1639
1640// Ord impl for Value
1641
1642impl PartialOrd for Value {
1643    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1644        Some(self.cmp(other))
1645    }
1646}
1647
1648impl Ord for Value {
1649    fn cmp(&self, other: &Self) -> Ordering {
1650        // Strip metadata for comparison.
1651        let a = self.unwrap_meta();
1652        let b = other.unwrap_meta();
1653        if !std::ptr::eq(a, self) || !std::ptr::eq(b, other) {
1654            return a.cmp(b);
1655        }
1656        // Same-type fast paths first, then cross-type numeric, then type-discriminant fallback.
1657        match (self, other) {
1658            // ── Nil ──
1659            (Value::Nil, Value::Nil) => Ordering::Equal,
1660
1661            // ── Booleans ──
1662            (Value::Bool(a), Value::Bool(b)) => a.cmp(b),
1663
1664            // ── Numerics (same type) ──
1665            (Value::Long(a), Value::Long(b)) => a.cmp(b),
1666            (Value::Double(a), Value::Double(b)) => total_cmp_f64(*a, *b),
1667            (Value::BigInt(a), Value::BigInt(b)) => a.get().cmp(b.get()),
1668            (Value::BigDecimal(a), Value::BigDecimal(b)) => {
1669                // BigDecimal doesn't impl Ord; compare via PartialOrd then string fallback.
1670                a.get()
1671                    .partial_cmp(b.get())
1672                    .unwrap_or_else(|| a.get().to_string().cmp(&b.get().to_string()))
1673            }
1674            (Value::Ratio(a), Value::Ratio(b)) => a.get().cmp(b.get()),
1675
1676            // ── Cross-type numerics (promote to common type) ──
1677            (Value::Long(a), Value::Double(b)) => total_cmp_f64(*a as f64, *b),
1678            (Value::Double(a), Value::Long(b)) => total_cmp_f64(*a, *b as f64),
1679            (Value::Long(a), Value::BigInt(b)) => BigInt::from(*a).cmp(b.get()),
1680            (Value::BigInt(a), Value::Long(b)) => a.get().cmp(&BigInt::from(*b)),
1681            (Value::Long(a), Value::Ratio(b)) => {
1682                num_rational::Ratio::from(BigInt::from(*a)).cmp(b.get())
1683            }
1684            (Value::Ratio(a), Value::Long(b)) => {
1685                a.get().cmp(&num_rational::Ratio::from(BigInt::from(*b)))
1686            }
1687            (Value::BigInt(a), Value::Ratio(b)) => {
1688                num_rational::Ratio::from(a.get().clone()).cmp(b.get())
1689            }
1690            (Value::Ratio(a), Value::BigInt(b)) => {
1691                a.get().cmp(&num_rational::Ratio::from(b.get().clone()))
1692            }
1693            (Value::Double(a), Value::BigInt(b)) => {
1694                total_cmp_f64(*a, b.get().to_f64().unwrap_or(f64::MAX))
1695            }
1696            (Value::BigInt(a), Value::Double(b)) => {
1697                total_cmp_f64(a.get().to_f64().unwrap_or(f64::MAX), *b)
1698            }
1699            (Value::Double(a), Value::Ratio(b)) => {
1700                total_cmp_f64(*a, b.get().to_f64().unwrap_or(f64::MAX))
1701            }
1702            (Value::Ratio(a), Value::Double(b)) => {
1703                total_cmp_f64(a.get().to_f64().unwrap_or(f64::MAX), *b)
1704            }
1705            (Value::Long(a), Value::BigDecimal(b)) => {
1706                let ad = bigdecimal::BigDecimal::from(*a);
1707                ad.partial_cmp(b.get())
1708                    .unwrap_or_else(|| ad.to_string().cmp(&b.get().to_string()))
1709            }
1710            (Value::BigDecimal(a), Value::Long(b)) => {
1711                let bd = bigdecimal::BigDecimal::from(*b);
1712                a.get()
1713                    .partial_cmp(&bd)
1714                    .unwrap_or_else(|| a.get().to_string().cmp(&bd.to_string()))
1715            }
1716            (Value::Double(a), Value::BigDecimal(b)) => {
1717                match bigdecimal::BigDecimal::try_from(*a) {
1718                    Ok(ad) => ad
1719                        .partial_cmp(b.get())
1720                        .unwrap_or_else(|| ad.to_string().cmp(&b.get().to_string())),
1721                    Err(_) => {
1722                        // NaN or infinity
1723                        if a.is_nan() {
1724                            Ordering::Greater
1725                        } else if *a < 0.0 {
1726                            Ordering::Less
1727                        } else {
1728                            Ordering::Greater
1729                        }
1730                    }
1731                }
1732            }
1733            (Value::BigDecimal(a), Value::Double(b)) => {
1734                match bigdecimal::BigDecimal::try_from(*b) {
1735                    Ok(bd) => a
1736                        .get()
1737                        .partial_cmp(&bd)
1738                        .unwrap_or_else(|| a.get().to_string().cmp(&bd.to_string())),
1739                    Err(_) => {
1740                        if b.is_nan() {
1741                            Ordering::Less
1742                        } else if *b < 0.0 {
1743                            Ordering::Greater
1744                        } else {
1745                            Ordering::Less
1746                        }
1747                    }
1748                }
1749            }
1750            (Value::BigInt(a), Value::BigDecimal(b)) => {
1751                let ad = bigdecimal::BigDecimal::from(a.get().clone());
1752                ad.partial_cmp(b.get())
1753                    .unwrap_or_else(|| ad.to_string().cmp(&b.get().to_string()))
1754            }
1755            (Value::BigDecimal(a), Value::BigInt(b)) => {
1756                let bd = bigdecimal::BigDecimal::from(b.get().clone());
1757                a.get()
1758                    .partial_cmp(&bd)
1759                    .unwrap_or_else(|| a.get().to_string().cmp(&bd.to_string()))
1760            }
1761            (Value::Ratio(a), Value::BigDecimal(b)) => {
1762                let af = a.get().to_f64().unwrap_or(f64::MAX);
1763                match bigdecimal::BigDecimal::try_from(af) {
1764                    Ok(ad) => ad
1765                        .partial_cmp(b.get())
1766                        .unwrap_or_else(|| ad.to_string().cmp(&b.get().to_string())),
1767                    Err(_) => Ordering::Greater,
1768                }
1769            }
1770            (Value::BigDecimal(a), Value::Ratio(b)) => {
1771                let bf = b.get().to_f64().unwrap_or(f64::MAX);
1772                match bigdecimal::BigDecimal::try_from(bf) {
1773                    Ok(bd) => a
1774                        .get()
1775                        .partial_cmp(&bd)
1776                        .unwrap_or_else(|| a.get().to_string().cmp(&bd.to_string())),
1777                    Err(_) => Ordering::Less,
1778                }
1779            }
1780
1781            // ── Characters ──
1782            (Value::Char(a), Value::Char(b)) => a.cmp(b),
1783
1784            // ── Strings ──
1785            (Value::Str(a), Value::Str(b)) => a.get().cmp(b.get()),
1786
1787            // ── Symbols ──
1788            (Value::Symbol(a), Value::Symbol(b)) => cmp_ns_name(
1789                &a.get().namespace,
1790                &a.get().name,
1791                &b.get().namespace,
1792                &b.get().name,
1793            ),
1794
1795            // ── Keywords ──
1796            (Value::Keyword(a), Value::Keyword(b)) => cmp_ns_name(
1797                &a.get().namespace,
1798                &a.get().name,
1799                &b.get().namespace,
1800                &b.get().name,
1801            ),
1802
1803            // ── Sequential collections: element-by-element ──
1804            (Value::Vector(a), Value::Vector(b)) => iter_cmp(a.get().iter(), b.get().iter()),
1805            (Value::List(a), Value::List(b)) => iter_cmp(a.get().iter(), b.get().iter()),
1806
1807            // ── Sets: compare by size, then elements ──
1808            (Value::Set(a), Value::Set(b)) => a.count().cmp(&b.count()),
1809
1810            // ── Maps: compare by size ──
1811            (Value::Map(a), Value::Map(b)) => a.count().cmp(&b.count()),
1812
1813            // ── Different types: order by type discriminant for a consistent total order ──
1814            _ => type_discriminant(self).cmp(&type_discriminant(other)),
1815        }
1816    }
1817}
1818
1819/// Compare two iterators of Values element-by-element.
1820fn iter_cmp<'a>(
1821    mut a: impl Iterator<Item = &'a Value>,
1822    mut b: impl Iterator<Item = &'a Value>,
1823) -> Ordering {
1824    loop {
1825        match (a.next(), b.next()) {
1826            (None, None) => return Ordering::Equal,
1827            (None, Some(_)) => return Ordering::Less,
1828            (Some(_), None) => return Ordering::Greater,
1829            (Some(x), Some(y)) => {
1830                let c = x.cmp(y);
1831                if c != Ordering::Equal {
1832                    return c;
1833                }
1834            }
1835        }
1836    }
1837}
1838
1839/// Total ordering for f64: NaN sorts after everything else, otherwise use IEEE total_order.
1840fn total_cmp_f64(a: f64, b: f64) -> Ordering {
1841    a.total_cmp(&b)
1842}
1843
1844/// Compare namespace-qualified names: namespace first (None < Some), then name.
1845fn cmp_ns_name(
1846    ns_a: &Option<Arc<str>>,
1847    name_a: &Arc<str>,
1848    ns_b: &Option<Arc<str>>,
1849    name_b: &Arc<str>,
1850) -> Ordering {
1851    match (ns_a, ns_b) {
1852        (None, None) => name_a.cmp(name_b),
1853        (None, Some(_)) => Ordering::Less,
1854        (Some(_), None) => Ordering::Greater,
1855        (Some(a), Some(b)) => a.cmp(b).then_with(|| name_a.cmp(name_b)),
1856    }
1857}
1858
1859/// Assign a stable integer to each Value variant for cross-type ordering.
1860fn type_discriminant(v: &Value) -> u8 {
1861    match v {
1862        Value::WithMeta(inner, _) => type_discriminant(inner),
1863        Value::Reduced(inner) => type_discriminant(inner),
1864        Value::Nil => 0,
1865        Value::Bool(_) => 1,
1866        Value::Long(_)
1867        | Value::Double(_)
1868        | Value::BigInt(_)
1869        | Value::BigDecimal(_)
1870        | Value::Ratio(_) => 2,
1871        Value::Char(_) => 3,
1872        Value::Str(_) => 4,
1873        Value::Symbol(_) => 5,
1874        Value::Keyword(_) => 6,
1875        Value::List(_) => 7,
1876        Value::Vector(_) => 8,
1877        Value::Map(_) => 9,
1878        Value::Set(_) => 10,
1879        Value::Queue(_) => 11,
1880        Value::LazySeq(_) => 12,
1881        Value::Cons(_) => 13,
1882        Value::NativeFunction(_) => 14,
1883        Value::BoundFn(_) => 14,
1884        Value::Fn(_) => 15,
1885        Value::Macro(_) => 16,
1886        Value::Var(_) => 17,
1887        Value::Atom(_) => 18,
1888        Value::SharedAtom(_) => 46,
1889        Value::ByteBlob(_) => 47,
1890        Value::Namespace(_) => 19,
1891        Value::Protocol(_) => 20,
1892        Value::ProtocolFn(_) => 21,
1893        Value::MultiFn(_) => 22,
1894        Value::Volatile(_) => 23,
1895        Value::Delay(_) => 24,
1896        Value::Promise(_) => 25,
1897        Value::Future(_) => 26,
1898        Value::Agent(_) => 27,
1899        Value::TypeInstance(_) => 28,
1900        Value::BooleanArray(_) => 29,
1901        Value::ByteArray(_) => 30,
1902        Value::ShortArray(_) => 31,
1903        Value::IntArray(_) => 32,
1904        Value::LongArray(_) => 33,
1905        Value::CharArray(_) => 34,
1906        Value::FloatArray(_) => 35,
1907        Value::DoubleArray(_) => 36,
1908        Value::ObjectArray(_) => 37,
1909        Value::Uuid(_) => 38,
1910        Value::NativeObject(_) => 43,
1911        Value::Resource(_) => 39,
1912        Value::TransientMap(_) => 40,
1913        Value::TransientSet(_) => 41,
1914        Value::TransientVector(_) => 42,
1915        Value::Pattern(_) => 43,
1916        Value::Matcher(_) => 44,
1917        Value::Error(_) => 45,
1918    }
1919}