Skip to main content

hara_native/lang/data/
list.rs

1//! Persistent chunked list, ported from `hara.lang.data.List` (Java).
2//!
3//! `Standard` is a singly-linked chain of fixed 32-slot chunks. Each chunk
4//! holds a live window `offset .. offset + count` into its block.
5//!
6//! Chunk block representation: Java uses a shared `Object[32]` with an
7//! offset/count window; `popFirst` creates a new chunk that SHARES the block
8//! (window shifted by one), while `pushFirst` into an open window CLONES the
9//! block before writing. The Rust port mirrors this exactly with
10//! `Rc<[Option<E>; 32]>`: the fixed array is a faithful, safe analogue of the
11//! Java block (same memory shape, `None` where Java has `null`), `Rc` gives
12//! the structural sharing, and clone-on-write preserves persistence. No
13//! unsafe, no new crates, wasm32-compatible.
14//!
15//! `Mutable` is a power-of-2 ring buffer (`elements`, `mask`, `size`,
16//! `offset`) with doubling grow and linearize-on-resize, guarded by an
17//! editable flag: any use after `to_persistent` panics.
18
19use crate::lang::hash::JavaHash;
20use crate::lang::protocol::{
21    HashType, IAssoc, IColl, IConj, ICons, ICount, IDisplay, IEmpty, IEquality, IHash, IMetadata,
22    IMutable, INth, IObjType, IPeekFirst, IPeekLast, IPersistent, IPopFirst, IPopLast, IPushFirst,
23    IPushLast, IToMutable, IToPersistent, ObjType,
24};
25use std::cell::Cell;
26use std::rc::Rc;
27
28const CHUNK_SIZE: usize = 32;
29const DEFAULT_CAPACITY: usize = 4;
30
31/// Fixed 32-slot chunk block; `None` slots are outside the live window.
32type Block<E> = [Option<E>; CHUNK_SIZE];
33
34fn empty_block<E>() -> Block<E> {
35    std::array::from_fn(|_| None)
36}
37
38#[derive(Debug, Clone)]
39struct Chunk<E> {
40    array: Rc<Block<E>>,
41    offset: usize,
42    count: usize,
43    next: Option<Rc<Chunk<E>>>,
44}
45
46#[derive(Debug, Clone)]
47pub struct Standard<E> {
48    metadata: Option<Rc<crate::lang::data::Metadata>>,
49    head: Option<Rc<Chunk<E>>>,
50    size: usize,
51}
52
53impl<E> Default for Standard<E> {
54    fn default() -> Self {
55        Self {
56            metadata: None,
57            head: None,
58            size: 0,
59        }
60    }
61}
62
63impl<E: Clone> Standard<E> {
64    pub fn new() -> Self {
65        Self::default()
66    }
67    pub fn len(&self) -> usize {
68        self.size
69    }
70    pub fn is_empty(&self) -> bool {
71        self.size == 0
72    }
73
74    pub fn push_first(&self, value: E) -> Self {
75        let head = match &self.head {
76            // Open window: clone the block, write at offset - 1.
77            Some(head) if head.offset > 0 => {
78                let mut array = (*head.array).clone();
79                array[head.offset - 1] = Some(value);
80                Rc::new(Chunk {
81                    array: Rc::new(array),
82                    offset: head.offset - 1,
83                    count: head.count + 1,
84                    next: head.next.clone(),
85                })
86            }
87            // No open window: fresh block, value at the last slot.
88            _ => {
89                let mut array = empty_block();
90                array[CHUNK_SIZE - 1] = Some(value);
91                Rc::new(Chunk {
92                    array: Rc::new(array),
93                    offset: CHUNK_SIZE - 1,
94                    count: 1,
95                    next: self.head.clone(),
96                })
97            }
98        };
99        Self {
100            metadata: self.metadata.clone(),
101            head: Some(head),
102            size: self.size + 1,
103        }
104    }
105
106    pub fn push_last(&self, value: E) -> Self {
107        let mut mutable = self.to_mutable();
108        mutable.push_last(value);
109        mutable.to_persistent()
110    }
111
112    pub fn pop_first_value(&self) -> Self {
113        if self.size == 0 {
114            return self.clone();
115        }
116        let head = self.head.as_ref().expect("non-empty list has a head chunk");
117        let head = if head.count > 1 {
118            // Window shift: the new head chunk SHARES the block (no copy).
119            Rc::new(Chunk {
120                array: head.array.clone(),
121                offset: head.offset + 1,
122                count: head.count - 1,
123                next: head.next.clone(),
124            })
125        } else {
126            return Self {
127                metadata: self.metadata.clone(),
128                head: head.next.clone(),
129                size: self.size - 1,
130            };
131        };
132        Self {
133            metadata: self.metadata.clone(),
134            head: Some(head),
135            size: self.size - 1,
136        }
137    }
138
139    pub fn pop_last_value(&self) -> Self {
140        let mut mutable = self.to_mutable();
141        mutable.pop_last();
142        mutable.to_persistent()
143    }
144
145    pub fn get(&self, index: usize) -> Option<&E> {
146        if index >= self.size {
147            return None;
148        }
149        let mut idx = index;
150        let mut chunk = self.head.as_deref();
151        while let Some(current) = chunk {
152            if idx < current.count {
153                return current.array[current.offset + idx].as_ref();
154            }
155            idx -= current.count;
156            chunk = current.next.as_deref();
157        }
158        None
159    }
160
161    pub fn iter(&self) -> Iter<'_, E> {
162        Iter {
163            chunk: self.head.as_deref(),
164            index: 0,
165        }
166    }
167}
168
169pub struct Iter<'a, E> {
170    chunk: Option<&'a Chunk<E>>,
171    index: usize,
172}
173impl<'a, E> Iterator for Iter<'a, E> {
174    type Item = &'a E;
175    fn next(&mut self) -> Option<Self::Item> {
176        loop {
177            let chunk = self.chunk?;
178            if self.index < chunk.count {
179                let value = chunk.array[chunk.offset + self.index].as_ref();
180                self.index += 1;
181                return value;
182            }
183            self.chunk = chunk.next.as_deref();
184            self.index = 0;
185        }
186    }
187}
188
189impl<E: Clone> FromIterator<E> for Standard<E> {
190    fn from_iter<T: IntoIterator<Item = E>>(iter: T) -> Self {
191        // Java `Standard.into`: fill a Mutable with pushLast, then freeze.
192        let mut mutable = iter.into_iter().collect::<Mutable<E>>();
193        mutable.to_persistent()
194    }
195}
196impl<E: Clone> From<Vec<E>> for Standard<E> {
197    fn from(values: Vec<E>) -> Self {
198        values.into_iter().collect()
199    }
200}
201impl<E: Clone> IntoIterator for Standard<E> {
202    type Item = E;
203    type IntoIter = std::vec::IntoIter<E>;
204    fn into_iter(self) -> Self::IntoIter {
205        self.iter().cloned().collect::<Vec<_>>().into_iter()
206    }
207}
208impl<'a, E: Clone> IntoIterator for &'a Standard<E> {
209    type Item = &'a E;
210    type IntoIter = Iter<'a, E>;
211    fn into_iter(self) -> Self::IntoIter {
212        self.iter()
213    }
214}
215impl<E: Clone> std::ops::Index<usize> for Standard<E> {
216    type Output = E;
217    fn index(&self, index: usize) -> &E {
218        self.get(index).expect("list index out of bounds")
219    }
220}
221impl<E: Clone + PartialEq> PartialEq for Standard<E> {
222    fn eq(&self, other: &Self) -> bool {
223        self.size == other.size && self.iter().eq(other.iter())
224    }
225}
226impl<E: Clone> ICount for Standard<E> {
227    fn count(&self) -> usize {
228        self.size
229    }
230}
231impl<E: Clone> INth<E> for Standard<E> {
232    fn nth(&self, index: usize) -> Option<&E> {
233        self.get(index)
234    }
235}
236impl<E: Clone> IPeekFirst<E> for Standard<E> {
237    fn peek_first(&self) -> Option<E> {
238        self.get(0).cloned()
239    }
240}
241impl<E: Clone> IPeekLast<E> for Standard<E> {
242    fn peek_last(&self) -> Option<E> {
243        self.size
244            .checked_sub(1)
245            .and_then(|index| self.get(index))
246            .cloned()
247    }
248}
249impl<E: Clone> IPushFirst<E> for Standard<E> {
250    type Output = Self;
251    fn push_first(&self, value: E) -> Self {
252        Standard::push_first(self, value)
253    }
254}
255impl<E: Clone> IPushLast<E> for Standard<E> {
256    type Output = Self;
257    fn push_last(&self, value: E) -> Self {
258        Standard::push_last(self, value)
259    }
260}
261impl<E: Clone> IPopFirst for Standard<E> {
262    type Output = Self;
263    fn pop_first(&self) -> Self {
264        self.pop_first_value()
265    }
266}
267impl<E: Clone> IPopLast for Standard<E> {
268    type Output = Self;
269    fn pop_last(&self) -> Self {
270        self.pop_last_value()
271    }
272}
273impl<E: Clone> ICons<E> for Standard<E> {
274    type Output = Self;
275    fn cons(&self, value: E) -> Self {
276        self.push_first(value)
277    }
278}
279impl<E: Clone> IConj<E> for Standard<E> {
280    type Output = Self;
281    fn conj(&self, value: E) -> Self {
282        self.push_last(value)
283    }
284}
285impl<E: Clone> IEmpty for Standard<E> {
286    type Output = Self;
287    fn empty(&self) -> Self {
288        Self::new().with_meta(self.metadata.clone())
289    }
290}
291impl<E: Clone> IAssoc<usize, E> for Standard<E> {
292    type Output = Self;
293    fn assoc(&self, index: usize, value: E) -> Self {
294        let mut mutable = self.to_mutable();
295        mutable.assoc(index, value);
296        mutable.to_persistent()
297    }
298}
299impl<E: Clone> IMetadata for Standard<E> {
300    type Metadata = Rc<crate::lang::data::Metadata>;
301    fn meta(&self) -> Option<&Self::Metadata> {
302        self.metadata.as_ref()
303    }
304    fn with_meta(&self, metadata: Option<Self::Metadata>) -> Self {
305        Self {
306            metadata,
307            ..self.clone()
308        }
309    }
310}
311impl<E: Clone> IPersistent for Standard<E> {}
312
313impl<E: Clone + PartialEq> IEquality for Standard<E> {
314    fn equality(&self, other: &Self) -> bool {
315        self == other
316    }
317}
318impl<E: Clone + std::fmt::Debug> IDisplay for Standard<E> {
319    fn display(&self) -> String {
320        format!(
321            "({})",
322            self.iter()
323                .map(|v| format!("{v:?}"))
324                .collect::<Vec<_>>()
325                .join(" ")
326        )
327    }
328}
329impl<E: Clone + std::hash::Hash + JavaHash> IHash for Standard<E> {
330    fn hash_calc(&self, hash_type: HashType) -> u64 {
331        // Java List extends IVectorType → ISequential: ordered
332        // composition, "::SEQUENTIAL" seed (see lang::hash).
333        crate::lang::hash::compose_ordered(
334            "SEQUENTIAL",
335            self.iter().map(|value| value.java_hash(hash_type)),
336        ) as u64
337    }
338}
339impl<E: Clone + std::fmt::Debug> IObjType for Standard<E> {
340    fn obj_type(&self) -> ObjType {
341        ObjType::Sequential
342    }
343}
344impl<E> IColl<E> for Standard<E>
345where
346    E: Clone + PartialEq + std::fmt::Debug + std::hash::Hash + JavaHash,
347{
348    fn start_string(&self) -> &'static str {
349        "("
350    }
351    fn end_string(&self) -> &'static str {
352        ")"
353    }
354}
355
356/// Next power of two, minimum 1 (Java `max(1, 1 << log2Ceil(capacity))`).
357fn ring_capacity(capacity: usize) -> usize {
358    capacity.max(1).next_power_of_two()
359}
360
361#[derive(Clone)]
362pub struct Mutable<E> {
363    editable: Rc<Cell<bool>>,
364    elements: Vec<Option<E>>,
365    mask: usize,
366    size: usize,
367    offset: usize,
368    metadata: Option<Rc<crate::lang::data::Metadata>>,
369}
370
371impl<E> Mutable<E> {
372    pub fn new() -> Self {
373        let capacity = ring_capacity(DEFAULT_CAPACITY);
374        Self {
375            editable: Rc::new(Cell::new(true)),
376            elements: std::iter::repeat_with(|| None).take(capacity).collect(),
377            mask: capacity - 1,
378            size: 0,
379            offset: 0,
380            metadata: None,
381        }
382    }
383
384    fn from_standard(list: &Standard<E>) -> Self
385    where
386        E: Clone,
387    {
388        let capacity = ring_capacity(list.size + 1);
389        let mut elements: Vec<Option<E>> = std::iter::repeat_with(|| None).take(capacity).collect();
390        // Flatten chunks into the ring buffer, in order, at offset 0.
391        let mut i = 0;
392        let mut chunk = list.head.as_deref();
393        while let Some(current) = chunk {
394            for j in 0..current.count {
395                elements[i] = current.array[current.offset + j].clone();
396                i += 1;
397            }
398            chunk = current.next.as_deref();
399        }
400        Self {
401            editable: Rc::new(Cell::new(true)),
402            elements,
403            mask: capacity - 1,
404            size: list.size,
405            offset: 0,
406            metadata: list.metadata.clone(),
407        }
408    }
409
410    fn check_editable(&self) {
411        assert!(self.editable.get(), "mutable list used after to_persistent");
412    }
413
414    // Doubling grow with linearize-on-resize.
415    fn resize(&mut self, new_capacity: usize) {
416        let mut elements: Vec<Option<E>> =
417            std::iter::repeat_with(|| None).take(new_capacity).collect();
418        for i in 0..self.size {
419            elements[i] = self.elements[(self.offset + i) & self.mask].take();
420        }
421        self.elements = elements;
422        self.offset = 0;
423        self.mask = new_capacity - 1;
424    }
425
426    pub fn len(&self) -> usize {
427        self.check_editable();
428        self.size
429    }
430    pub fn is_empty(&self) -> bool {
431        self.len() == 0
432    }
433    pub fn get(&self, index: usize) -> Option<&E> {
434        self.check_editable();
435        if index >= self.size {
436            return None;
437        }
438        self.elements[(self.offset + index) & self.mask].as_ref()
439    }
440    pub fn iter(&self) -> impl Iterator<Item = &E> {
441        self.check_editable();
442        (0..self.size).map(move |i| {
443            self.elements[(self.offset + i) & self.mask]
444                .as_ref()
445                .expect("live ring slot")
446        })
447    }
448    pub fn push_first(&mut self, value: E) -> &mut Self {
449        self.check_editable();
450        if self.size == self.elements.len() {
451            self.resize(self.size << 1);
452        }
453        self.offset = self.offset.wrapping_sub(1) & self.mask;
454        self.elements[self.offset] = Some(value);
455        self.size += 1;
456        self
457    }
458    pub fn push_last(&mut self, value: E) -> &mut Self {
459        self.check_editable();
460        if self.size == self.elements.len() {
461            self.resize(self.size << 1);
462        }
463        self.elements[(self.offset + self.size) & self.mask] = Some(value);
464        self.size += 1;
465        self
466    }
467    pub fn pop_first(&mut self) -> Option<E> {
468        self.check_editable();
469        if self.size == 0 {
470            return None;
471        }
472        let value = self.elements[self.offset].take();
473        self.offset = (self.offset + 1) & self.mask;
474        self.size -= 1;
475        value
476    }
477    pub fn pop_last(&mut self) -> Option<E> {
478        self.check_editable();
479        if self.size == 0 {
480            return None;
481        }
482        let value = self.elements[(self.offset + self.size - 1) & self.mask].take();
483        self.size -= 1;
484        value
485    }
486    pub fn assoc(&mut self, index: usize, value: E) -> Option<E> {
487        self.check_editable();
488        if index == self.size {
489            self.push_last(value);
490            return None;
491        }
492        assert!(index < self.size, "list index out of bounds");
493        self.elements[(self.offset + index) & self.mask].replace(value)
494    }
495    pub fn empty(&mut self) -> &mut Self {
496        self.check_editable();
497        for slot in &mut self.elements {
498            *slot = None;
499        }
500        self.size = 0;
501        self.offset = 0;
502        self
503    }
504}
505
506impl<E> Default for Mutable<E> {
507    fn default() -> Self {
508        Self::new()
509    }
510}
511
512impl<E: PartialEq> PartialEq for Mutable<E> {
513    fn eq(&self, other: &Self) -> bool {
514        self.size == other.size
515            && self.metadata == other.metadata
516            && (0..self.size).all(|i| {
517                self.elements[(self.offset + i) & self.mask]
518                    == other.elements[(other.offset + i) & other.mask]
519            })
520    }
521}
522
523impl<E: std::fmt::Debug> std::fmt::Debug for Mutable<E> {
524    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
525        f.debug_list()
526            .entries(
527                (0..self.size)
528                    .filter_map(|i| self.elements[(self.offset + i) & self.mask].as_ref()),
529            )
530            .finish()
531    }
532}
533
534impl<E> FromIterator<E> for Mutable<E> {
535    fn from_iter<T: IntoIterator<Item = E>>(iter: T) -> Self {
536        let mut mutable = Self::new();
537        for value in iter {
538            mutable.push_last(value);
539        }
540        mutable
541    }
542}
543impl<E> IMutable for Mutable<E> {}
544impl<E: Clone> IToPersistent for Mutable<E> {
545    type Persistent = Standard<E>;
546    fn to_persistent(&mut self) -> Self::Persistent {
547        self.check_editable();
548        self.editable.set(false);
549        // Convert the ring buffer to a chunked list, cutting chunks from the
550        // END: the remainder (size % CHUNK_SIZE) is cut first, so every chunk
551        // is full except possibly the last (tail) one.
552        let mut next: Option<Rc<Chunk<E>>> = None;
553        let mut remaining = self.size;
554        while remaining > 0 {
555            let mut count = remaining % CHUNK_SIZE;
556            if count == 0 {
557                count = CHUNK_SIZE;
558            }
559            let start = remaining - count;
560            let array: Block<E> = std::array::from_fn(|i| {
561                if i < count {
562                    self.elements[(self.offset + start + i) & self.mask].clone()
563                } else {
564                    None
565                }
566            });
567            next = Some(Rc::new(Chunk {
568                array: Rc::new(array),
569                offset: 0,
570                count,
571                next,
572            }));
573            remaining -= count;
574        }
575        Standard {
576            metadata: self.metadata.clone(),
577            head: next,
578            size: self.size,
579        }
580    }
581}
582impl<E: Clone> IToMutable for Standard<E> {
583    type Mutable = Mutable<E>;
584    fn to_mutable(&self) -> Self::Mutable {
585        Mutable::from_standard(self)
586    }
587}
588
589#[cfg(test)]
590mod tests {
591    use super::{Mutable, Standard};
592    use crate::lang::protocol::{IAssoc, IEmpty, IMetadata, INth, IToMutable, IToPersistent};
593    use std::collections::VecDeque;
594    use std::rc::Rc;
595
596    #[test]
597    fn chunk_boundaries_and_persistence() {
598        let list = (0..65).collect::<Standard<_>>();
599        let prefixed = list.push_first(-1);
600        let appended = list.push_last(65);
601        assert_eq!(list.len(), 65);
602        assert_eq!(list[0], 0);
603        assert_eq!(prefixed[0], -1);
604        assert_eq!(appended[65], 65);
605    }
606
607    #[test]
608    fn push_first_pop_first_across_chunk_boundaries() {
609        for &n in &[31usize, 32, 33, 64, 65] {
610            let mut list = Standard::new();
611            for i in 0..n {
612                list = list.push_first(i);
613            }
614            assert_eq!(list.len(), n);
615            // push-first consing order: last pushed comes first
616            let expected = (0..n).rev().collect::<Vec<_>>();
617            assert_eq!(list.iter().copied().collect::<Vec<_>>(), expected);
618            let mut popped = list.clone();
619            let mut model: VecDeque<_> = expected.iter().copied().collect();
620            while let Some(front) = model.pop_front() {
621                assert_eq!(popped.get(0), Some(&front));
622                popped = popped.pop_first_value();
623            }
624            assert!(popped.is_empty());
625            assert_eq!(popped.pop_first_value().len(), 0);
626        }
627    }
628
629    #[test]
630    fn pop_first_shares_chunk_array_with_original() {
631        let list = Standard::from((0..40).collect::<Vec<_>>());
632        let popped = list.pop_first_value();
633        // Aliasing: the popped list's head chunk shares the original's block.
634        assert!(Rc::ptr_eq(
635            &list.head.as_ref().unwrap().array,
636            &popped.head.as_ref().unwrap().array
637        ));
638        // Both iterate correctly through the shared block.
639        assert_eq!(
640            list.iter().copied().collect::<Vec<_>>(),
641            (0..40).collect::<Vec<_>>()
642        );
643        assert_eq!(
644            popped.iter().copied().collect::<Vec<_>>(),
645            (1..40).collect::<Vec<_>>()
646        );
647        // Popping through a whole chunk (count hits 1) drops to the next.
648        let mut rest = list.clone();
649        for _ in 0..32 {
650            rest = rest.pop_first_value();
651        }
652        assert_eq!(
653            rest.iter().copied().collect::<Vec<_>>(),
654            (32..40).collect::<Vec<_>>()
655        );
656    }
657
658    #[test]
659    fn push_first_into_open_window_clones_chunk_array() {
660        // push_first-only head has offset > 0, so the next push clones.
661        let list = Standard::new().push_first(3).push_first(2).push_first(1);
662        let pushed = list.push_first(0);
663        assert!(!Rc::ptr_eq(
664            &list.head.as_ref().unwrap().array,
665            &pushed.head.as_ref().unwrap().array
666        ));
667        assert_eq!(pushed.iter().copied().collect::<Vec<_>>(), vec![0, 1, 2, 3]);
668        assert_eq!(list.iter().copied().collect::<Vec<_>>(), vec![1, 2, 3]);
669    }
670
671    #[test]
672    fn to_persistent_cuts_full_chunks_from_the_end() {
673        let list = Standard::from((0..65).collect::<Vec<_>>());
674        // Java Mutable.toPersistent: remaining % 32 is cut FIRST from the
675        // logical end, so the TAIL chunk is partial; all chunks ahead of it
676        // are full (offset 0). 65 = 32 + 32 + 1.
677        let head = list.head.as_ref().unwrap();
678        assert_eq!((head.offset, head.count), (0, 32));
679        let second = head.next.as_ref().unwrap();
680        assert_eq!((second.offset, second.count), (0, 32));
681        let tail = second.next.as_ref().unwrap();
682        assert_eq!((tail.offset, tail.count), (0, 1));
683        // Exact multiple of the chunk size: tail is full too.
684        let full = Standard::from((0..64).collect::<Vec<_>>());
685        assert_eq!(full.head.as_ref().unwrap().count, 32);
686    }
687
688    #[test]
689    fn iteration_order_is_front_to_back() {
690        let list = Standard::new().push_first(3).push_first(2).push_first(1);
691        assert_eq!(list.iter().copied().collect::<Vec<_>>(), vec![1, 2, 3]);
692        assert_eq!(list.clone().into_iter().collect::<Vec<_>>(), vec![1, 2, 3]);
693        assert!(Standard::<i32>::new().iter().next().is_none());
694    }
695
696    #[test]
697    fn nth_walks_chunks() {
698        let list = Standard::from((0..100).collect::<Vec<_>>());
699        for i in 0..100 {
700            assert_eq!(list.get(i), Some(&i));
701            assert_eq!(list.nth(i), Some(&i));
702        }
703        assert_eq!(list.get(100), None);
704    }
705
706    #[test]
707    fn mutable_ring_buffer_wraparound_matches_vecdeque() {
708        let mut mutable = Mutable::new();
709        let mut model = VecDeque::new();
710        // Interleave pushes and pops to force offset wraparound and resize.
711        for i in 0..200i32 {
712            mutable.push_last(i);
713            model.push_back(i);
714            if i % 3 == 0 {
715                assert_eq!(mutable.pop_first(), model.pop_front());
716            }
717            if i % 5 == 0 {
718                mutable.push_first(-i);
719                model.push_front(-i);
720            }
721            if i % 7 == 0 {
722                assert_eq!(mutable.pop_last(), model.pop_back());
723            }
724            assert_eq!(mutable.len(), model.len());
725            assert_eq!(
726                mutable.iter().copied().collect::<Vec<_>>(),
727                model.iter().copied().collect::<Vec<_>>()
728            );
729        }
730    }
731
732    #[test]
733    fn fuzz_persistent_and_mutable_against_vecdeque() {
734        let mut seed = 0x1234_5678_9abc_def0u64;
735        let mut next = move || {
736            seed = seed
737                .wrapping_mul(6364136223846793005)
738                .wrapping_add(1442695040888963407);
739            (seed >> 33) as usize
740        };
741        let mut list = Standard::new();
742        let mut model: VecDeque<usize> = VecDeque::new();
743        for _ in 0..2000 {
744            match next() % 4 {
745                0 => {
746                    let v = next();
747                    list = list.push_first(v);
748                    model.push_front(v);
749                }
750                1 => {
751                    let v = next();
752                    list = list.push_last(v);
753                    model.push_back(v);
754                }
755                2 => {
756                    list = list.pop_first_value();
757                    model.pop_front();
758                }
759                _ => {
760                    list = list.pop_last_value();
761                    model.pop_back();
762                }
763            }
764            assert_eq!(list.len(), model.len());
765        }
766        assert_eq!(
767            list.iter().copied().collect::<Vec<_>>(),
768            model.iter().copied().collect::<Vec<_>>()
769        );
770        // Mutable phase: continue the same op stream through the transient.
771        let mut mutable = list.to_mutable();
772        for _ in 0..2000 {
773            match next() % 4 {
774                0 => {
775                    let v = next();
776                    mutable.push_first(v);
777                    model.push_front(v);
778                }
779                1 => {
780                    let v = next();
781                    mutable.push_last(v);
782                    model.push_back(v);
783                }
784                2 => assert_eq!(mutable.pop_first(), model.pop_front()),
785                _ => assert_eq!(mutable.pop_last(), model.pop_back()),
786            }
787            assert_eq!(mutable.len(), model.len());
788        }
789        let frozen = mutable.to_persistent();
790        assert_eq!(
791            frozen.iter().copied().collect::<Vec<_>>(),
792            model.iter().copied().collect::<Vec<_>>()
793        );
794    }
795
796    #[test]
797    #[should_panic(expected = "mutable list used after to_persistent")]
798    fn mutable_use_after_to_persistent_panics() {
799        let mut mutable = Standard::from(vec![1, 2, 3]).to_mutable();
800        let _ = mutable.to_persistent();
801        mutable.push_last(4);
802    }
803
804    #[test]
805    fn persistent_operations_preserve_metadata() {
806        let list = Standard::from(vec![1, 2, 3])
807            .with_meta(Some(crate::lang::data::Metadata::document("doc")));
808        let values = [
809            list.push_first(0),
810            list.push_last(4),
811            list.pop_first_value(),
812            list.pop_last_value(),
813            list.assoc(1, 20),
814            list.empty(),
815        ];
816        assert!(values
817            .iter()
818            .all(|value| value.meta().map(|m| m.doc().unwrap()) == Some("doc")));
819    }
820
821    #[test]
822    fn mutable_round_trip_preserves_original_and_updates_edges() {
823        let original = (0..65).collect::<Standard<_>>();
824        let mut mutable = original.to_mutable();
825        assert_eq!(mutable.assoc(32, 320), Some(32));
826        mutable.push_first(-1).push_last(65);
827        assert_eq!(mutable.pop_first(), Some(-1));
828        assert_eq!(mutable.pop_last(), Some(65));
829        let persistent = mutable.to_persistent();
830        assert_eq!(persistent.get(32), Some(&320));
831        assert_eq!(original.get(32), Some(&32));
832    }
833
834    #[test]
835    fn assoc_at_count_appends_and_round_trip_keeps_metadata() {
836        let original = Standard::from(vec![1, 2])
837            .with_meta(Some(crate::lang::data::Metadata::document("doc")));
838        let appended = original.assoc(2, 3);
839        assert_eq!(appended.iter().copied().collect::<Vec<_>>(), vec![1, 2, 3]);
840        assert_eq!(appended.meta().and_then(|value| value.doc()), Some("doc"));
841
842        let mut mutable = original.to_mutable();
843        assert_eq!(mutable.assoc(2, 3), None);
844        let persistent = mutable.to_persistent();
845        assert_eq!(
846            persistent.iter().copied().collect::<Vec<_>>(),
847            vec![1, 2, 3]
848        );
849        assert_eq!(persistent.meta().and_then(|value| value.doc()), Some("doc"));
850    }
851
852    #[test]
853    #[should_panic(expected = "list index out of bounds")]
854    fn persistent_assoc_rejects_index_past_count() {
855        let _ = Standard::from(vec![1, 2]).assoc(3, 4);
856    }
857}