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