Skip to main content

fresh/model/
marker_tree.rs

1use std::cell::{RefCell, RefMut};
2use std::cmp::{max, Ordering};
3use std::collections::HashMap;
4use std::mem;
5use std::rc::{Rc, Weak};
6
7/// Use a simple u64 for marker IDs
8pub type MarkerId = u64;
9
10// ---
11// 1. Core Data Structures and Pointers
12// ---
13
14#[derive(Debug, Clone, PartialEq)]
15pub struct Interval {
16    pub start: u64,
17    pub end: u64,
18}
19
20/// Type of marker - either a position marker or a line anchor
21#[derive(Debug, Clone, PartialEq)]
22pub enum MarkerType {
23    /// Regular position marker (for overlays, cursors, etc.)
24    Position,
25    /// Line anchor with estimated/exact line number
26    LineAnchor {
27        estimated_line: usize,
28        confidence: AnchorConfidence,
29    },
30}
31
32/// Confidence level for line anchor estimates
33#[derive(Debug, Clone, PartialEq)]
34pub enum AnchorConfidence {
35    /// Exact line number (scanned from known position)
36    Exact,
37    /// Estimated from average line length
38    Estimated,
39    /// Relative to another anchor
40    Relative(MarkerId),
41}
42
43#[derive(Debug, Clone, PartialEq)]
44pub struct Marker {
45    pub id: MarkerId,
46    pub interval: Interval,
47    pub marker_type: MarkerType,
48}
49
50/// A Strong pointer to a tree node (child/sibling/map reference)
51type NodePtr = Option<Rc<RefCell<Node>>>;
52/// A Weak pointer to a tree node (parent reference, doesn't count for ownership)
53type WeakNodePtr = Weak<RefCell<Node>>;
54
55/// The internal tree node
56#[derive(Debug)]
57struct Node {
58    pub marker: Marker,
59
60    /// AVL: Height of this node's subtree
61    pub height: i32,
62    /// Augmentation: The max 'end' value in this node's subtree
63    pub max_end: u64,
64    /// VSCode-style: The delta to be applied to this node and its children
65    pub lazy_delta: i64,
66
67    pub parent: WeakNodePtr,
68    pub left: NodePtr,
69    pub right: NodePtr,
70}
71
72/// The main Interval Tree structure
73#[derive(Debug, Default)]
74pub struct IntervalTree {
75    root: NodePtr,
76    next_id: u64,
77    /// ID-to-Node map for O(1) lookups
78    marker_map: HashMap<MarkerId, Rc<RefCell<Node>>>,
79}
80
81// ---
82// 2. Node Helpers (Pushing Deltas, Stats, Heights)
83// ---
84
85impl Node {
86    fn new(marker: Marker, parent: WeakNodePtr) -> Rc<RefCell<Self>> {
87        // Fix E0382: Calculate max_end before moving ownership of `marker` into the struct.
88        let max_end_val = marker.interval.end;
89
90        Rc::new(RefCell::new(Node {
91            marker,
92            height: 1,
93            max_end: max_end_val,
94            lazy_delta: 0,
95            parent,
96            left: None,
97            right: None,
98        }))
99    }
100
101    /// Gets the height of a node (0 for None).
102    fn height(node: &NodePtr) -> i32 {
103        node.as_ref().map_or(0, |n| n.borrow().height)
104    }
105
106    /// Calculates the balance factor of a node (height(left) - height(right)).
107    fn balance_factor(node: &Rc<RefCell<Self>>) -> i32 {
108        let n = node.borrow();
109        Self::height(&n.left) - Self::height(&n.right)
110    }
111
112    /// Pushes this node's lazy_delta down to its immediate children.
113    fn push_delta(node_rc: &Rc<RefCell<Self>>) {
114        let mut node = node_rc.borrow_mut();
115        if node.lazy_delta == 0 {
116            return;
117        }
118
119        let delta = node.lazy_delta;
120
121        // Apply delta to self (start and end)
122        node.marker.interval.start = (node.marker.interval.start as i64 + delta) as u64;
123        node.marker.interval.end = (node.marker.interval.end as i64 + delta) as u64;
124
125        // Apply delta to children (only update their lazy_delta fields)
126        if let Some(ref left) = node.left {
127            left.borrow_mut().lazy_delta += delta;
128        }
129        if let Some(ref right) = node.right {
130            right.borrow_mut().lazy_delta += delta;
131        }
132
133        node.lazy_delta = 0;
134
135        // The max_end needs to be updated after the push
136        let max_l = node.left.as_ref().map_or(0, |l| l.borrow().max_end);
137        let max_r = node.right.as_ref().map_or(0, |r| r.borrow().max_end);
138        node.max_end = max(node.marker.interval.end, max(max_l, max_r));
139    }
140
141    /// Updates a node's height and max_end based on its children.
142    fn update_stats(node: &Rc<RefCell<Self>>) {
143        let mut n = node.borrow_mut();
144        let height_l = Self::height(&n.left);
145        let height_r = Self::height(&n.right);
146
147        n.height = 1 + max(height_l, height_r);
148
149        let max_l = n.left.as_ref().map_or(0, |l| l.borrow().max_end);
150        let max_r = n.right.as_ref().map_or(0, |r| r.borrow().max_end);
151        n.max_end = max(n.marker.interval.end, max(max_l, max_r));
152    }
153}
154
155// ---
156// 3. Main Public API
157// ---
158
159impl IntervalTree {
160    pub fn new() -> Self {
161        Self::default()
162    }
163
164    /// Inserts a new marker interval. Performance: O(log n)
165    pub fn insert(&mut self, start: u64, end: u64) -> MarkerId {
166        self.insert_with_type(start, end, MarkerType::Position)
167    }
168
169    /// Insert a marker with a specific type
170    pub fn insert_with_type(&mut self, start: u64, end: u64, marker_type: MarkerType) -> MarkerId {
171        let id = self.next_id;
172        self.next_id += 1;
173        let marker = Marker {
174            id,
175            interval: Interval { start, end },
176            marker_type,
177        };
178
179        let new_node = Node::new(marker.clone(), Weak::new());
180        self.root = Self::insert_recursive(self.root.take(), new_node.clone());
181
182        self.marker_map.insert(id, new_node);
183        id
184    }
185
186    /// Insert a line anchor at a specific position
187    pub fn insert_line_anchor(
188        &mut self,
189        start: u64,
190        end: u64,
191        estimated_line: usize,
192        confidence: AnchorConfidence,
193    ) -> MarkerId {
194        self.insert_with_type(
195            start,
196            end,
197            MarkerType::LineAnchor {
198                estimated_line,
199                confidence,
200            },
201        )
202    }
203
204    /// Finds the current true position of a marker by its ID. Performance: O(log n)
205    pub fn get_position(&self, id: MarkerId) -> Option<(u64, u64)> {
206        let node_rc = self.marker_map.get(&id)?;
207        let mut node_opt = Some(Rc::clone(node_rc));
208        let mut current_delta: i64 = 0;
209
210        // Walk up the tree, collecting all deltas that haven't been applied yet.
211        while let Some(current_rc) = node_opt {
212            let current = current_rc.borrow();
213
214            // Add this node's delta (if any)
215            current_delta += current.lazy_delta;
216
217            // Move up to the parent
218            node_opt = current.parent.upgrade();
219        }
220
221        let raw_marker = node_rc.borrow().marker.interval.clone();
222
223        let start = (raw_marker.start as i64 + current_delta) as u64;
224        let end = (raw_marker.end as i64 + current_delta) as u64;
225
226        Some((start, end))
227    }
228
229    /// Deletes a marker by its ID. Performance: O(log n)
230    pub fn delete(&mut self, id: MarkerId) -> bool {
231        let (start, _) = match self.get_position(id) {
232            Some(pos) => pos,
233            None => return false,
234        };
235
236        if !self.marker_map.contains_key(&id) {
237            return false;
238        }
239
240        self.root = Self::delete_recursive(self.root.take(), start, id);
241
242        self.marker_map.remove(&id).is_some()
243    }
244
245    /// Adjusts all markers for a text edit (insertion or deletion).
246    /// Performance: O(log n) due to lazy delta propagation.
247    pub fn adjust_for_edit(&mut self, pos: u64, delta: i64) {
248        Self::adjust_recursive(&mut self.root, pos, delta);
249    }
250
251    /// Finds all markers that overlap a given query range.
252    /// Performance: O(log n + k)
253    pub fn query(&self, query_start: u64, query_end: u64) -> Vec<Marker> {
254        let mut results = Vec::new();
255        Self::query_recursive(&self.root, query_start, query_end, &mut results);
256        results
257    }
258
259    /// Get the marker data for a given marker ID
260    pub fn get_marker(&self, id: MarkerId) -> Option<Marker> {
261        let node_rc = self.marker_map.get(&id)?;
262        Some(node_rc.borrow().marker.clone())
263    }
264
265    /// Update a line anchor's estimated line number and confidence
266    pub fn update_line_anchor(
267        &mut self,
268        id: MarkerId,
269        estimated_line: usize,
270        confidence: AnchorConfidence,
271    ) -> bool {
272        if let Some(node_rc) = self.marker_map.get(&id) {
273            let mut node = node_rc.borrow_mut();
274            node.marker.marker_type = MarkerType::LineAnchor {
275                estimated_line,
276                confidence,
277            };
278            true
279        } else {
280            false
281        }
282    }
283
284    /// Query only line anchors in a range
285    pub fn query_line_anchors(&self, query_start: u64, query_end: u64) -> Vec<Marker> {
286        self.query(query_start, query_end)
287            .into_iter()
288            .filter(|m| matches!(m.marker_type, MarkerType::LineAnchor { .. }))
289            .collect()
290    }
291}
292
293// ---
294// 4. Recursive Implementation Details (Insert, Delete, Adjust)
295// ---
296
297impl IntervalTree {
298    /// Recursive helper for insert
299    fn insert_recursive(root: NodePtr, new_node: Rc<RefCell<Node>>) -> NodePtr {
300        // Remove unnecessary 'mut'
301        let root = match root {
302            Some(r) => r,
303            None => return Some(new_node),
304        };
305
306        Node::push_delta(&root);
307
308        let (start, id) = (
309            new_node.borrow().marker.interval.start,
310            new_node.borrow().marker.id,
311        );
312
313        let mut root_mut = root.borrow_mut();
314        let (root_start, root_id) = (root_mut.marker.interval.start, root_mut.marker.id);
315
316        if start < root_start || (start == root_start && id < root_id) {
317            root_mut.left = Self::insert_recursive(root_mut.left.take(), Rc::clone(&new_node));
318            root_mut.left.as_ref().unwrap().borrow_mut().parent = Rc::downgrade(&root);
319        } else {
320            root_mut.right = Self::insert_recursive(root_mut.right.take(), Rc::clone(&new_node));
321            root_mut.right.as_ref().unwrap().borrow_mut().parent = Rc::downgrade(&root);
322        }
323
324        drop(root_mut);
325        Node::update_stats(&root);
326        Self::balance(root)
327    }
328
329    /// Recursive helper for delete
330    fn delete_recursive(root: NodePtr, start: u64, id: MarkerId) -> NodePtr {
331        // Remove unnecessary 'mut'
332        let root = root?;
333
334        Node::push_delta(&root);
335
336        let mut root_mut = root.borrow_mut();
337        let (root_start, root_id) = (root_mut.marker.interval.start, root_mut.marker.id);
338
339        match start.cmp(&root_start) {
340            Ordering::Less => {
341                root_mut.left = Self::delete_recursive(root_mut.left.take(), start, id);
342            }
343            Ordering::Greater => {
344                root_mut.right = Self::delete_recursive(root_mut.right.take(), start, id);
345            }
346            Ordering::Equal => match id.cmp(&root_id) {
347                Ordering::Less => {
348                    root_mut.left = Self::delete_recursive(root_mut.left.take(), start, id);
349                }
350                Ordering::Greater => {
351                    root_mut.right = Self::delete_recursive(root_mut.right.take(), start, id);
352                }
353                Ordering::Equal => {
354                    return Self::perform_node_deletion(root_mut, Rc::clone(&root));
355                }
356            },
357        }
358
359        drop(root_mut);
360        Node::update_stats(&root);
361        Self::balance(root)
362    }
363
364    /// Handles the actual structural changes for deletion.
365    fn perform_node_deletion(mut node: RefMut<Node>, node_rc: Rc<RefCell<Node>>) -> NodePtr {
366        if node.left.is_none() {
367            let right = node.right.take();
368            if let Some(ref r) = right {
369                r.borrow_mut().parent = node.parent.clone();
370            }
371            right
372        } else if node.right.is_none() {
373            let left = node.left.take();
374            if let Some(ref l) = left {
375                l.borrow_mut().parent = node.parent.clone();
376            }
377            left
378        } else {
379            let successor_rc = Self::min_node(node.right.as_ref().unwrap());
380
381            let (successor_start, successor_id) = {
382                let s = successor_rc.borrow();
383                (s.marker.interval.start, s.marker.id)
384            };
385
386            mem::swap(&mut node.marker, &mut successor_rc.borrow_mut().marker);
387
388            node.right = Self::delete_recursive(node.right.take(), successor_start, successor_id);
389
390            drop(node);
391            Node::update_stats(&node_rc);
392            Self::balance(node_rc)
393        }
394    }
395
396    /// Finds the minimum node in a subtree (for deletion)
397    fn min_node(node_rc: &Rc<RefCell<Node>>) -> Rc<RefCell<Node>> {
398        let mut current = Rc::clone(node_rc);
399        loop {
400            Node::push_delta(&current);
401
402            // Fix E0506: Clone the next node pointer before the borrow (Ref<Node>) on
403            // `current` is dropped and potentially prevents reassignment.
404            let next_left_opt = current.borrow().left.clone();
405
406            if let Some(next) = next_left_opt {
407                current = next;
408            } else {
409                break current;
410            }
411        }
412    }
413
414    /// CORRECTED Recursive helper for `adjust_for_edit` (O(log n) lazy update)
415    fn adjust_recursive(node_opt: &mut NodePtr, pos: u64, delta: i64) {
416        let node_rc = match node_opt {
417            Some(n) => n,
418            None => return,
419        };
420
421        Node::push_delta(node_rc);
422
423        let mut node = node_rc.borrow_mut();
424        let start = node.marker.interval.start;
425
426        if pos <= start {
427            // CASE 1: Edit is at or before this node's start.
428            // This node and everything to its right must be shifted.
429
430            // 1. Shift the current node's start position directly, clamping at `pos` if needed.
431            if delta < 0 {
432                node.marker.interval.start = (start as i64 + delta).max(pos as i64) as u64;
433            } else {
434                node.marker.interval.start = (start as i64 + delta) as u64;
435            }
436
437            // 2. Handle the right subtree.
438            // For insertions (delta > 0): can use lazy propagation since all nodes shift uniformly
439            // For deletions (delta < 0): must recurse to provide position-aware clamping
440            if delta < 0 {
441                // Deletion: recurse immediately so nodes can clamp to `pos`
442                Self::adjust_recursive(&mut node.right, pos, delta);
443            } else {
444                // Insertion: lazy propagation is safe and efficient
445                if let Some(ref right) = node.right {
446                    right.borrow_mut().lazy_delta += delta;
447                }
448            }
449
450            // 3. Recurse left, as it may contain markers spanning the edit pos.
451            Self::adjust_recursive(&mut node.left, pos, delta);
452        } else {
453            // pos > start
454            // CASE 2: This node's start is BEFORE the edit.
455            // Its start is unaffected. We only need to check the right subtree
456            // for nodes that might be affected.
457            Self::adjust_recursive(&mut node.right, pos, delta);
458        }
459
460        // Always handle the interval span case (where end >= pos)
461        if node.marker.interval.end >= pos {
462            node.marker.interval.end = (node.marker.interval.end as i64 + delta)
463                .max(node.marker.interval.start as i64)
464                as u64;
465        }
466
467        drop(node);
468        Node::update_stats(node_rc);
469    }
470
471    /// Recursive helper for query
472    fn query_recursive(
473        node_opt: &NodePtr,
474        query_start: u64,
475        query_end: u64,
476        results: &mut Vec<Marker>,
477    ) {
478        let node_rc = match node_opt {
479            Some(n) => n,
480            None => return,
481        };
482
483        Node::push_delta(node_rc);
484        let node = node_rc.borrow();
485
486        let i = &node.marker.interval;
487        if i.start <= query_end && i.end >= query_start {
488            results.push(node.marker.clone());
489        }
490
491        if node.left.is_some() && node.left.as_ref().unwrap().borrow().max_end >= query_start {
492            Self::query_recursive(&node.left, query_start, query_end, results);
493        }
494
495        if node.right.is_some() && node.marker.interval.start <= query_end {
496            Self::query_recursive(&node.right, query_start, query_end, results);
497        }
498    }
499
500    // --- AVL Balancing ---
501
502    fn balance(node: Rc<RefCell<Node>>) -> NodePtr {
503        let bf = Node::balance_factor(&node);
504
505        if bf > 1 {
506            let left_rc = node.borrow().left.as_ref().unwrap().clone();
507            if Node::balance_factor(&left_rc) < 0 {
508                // Fix RefCell borrow issue: extract left child before rotating
509                let left_child = node.borrow_mut().left.take().unwrap();
510                node.borrow_mut().left = Self::rotate_left(left_child);
511            }
512            Self::rotate_right(node)
513        } else if bf < -1 {
514            let right_rc = node.borrow().right.as_ref().unwrap().clone();
515            if Node::balance_factor(&right_rc) > 0 {
516                // Fix RefCell borrow issue: extract right child before rotating
517                let right_child = node.borrow_mut().right.take().unwrap();
518                node.borrow_mut().right = Self::rotate_right(right_child);
519            }
520            Self::rotate_left(node)
521        } else {
522            Some(node)
523        }
524    }
525
526    fn rotate_left(node_rc: Rc<RefCell<Node>>) -> NodePtr {
527        Node::push_delta(&node_rc);
528        let x_rc = node_rc.borrow_mut().right.take().unwrap();
529        Node::push_delta(&x_rc);
530
531        let mut y = node_rc.borrow_mut();
532        let mut x = x_rc.borrow_mut();
533
534        y.right = x.left.take();
535        if let Some(ref r) = y.right {
536            r.borrow_mut().parent = Rc::downgrade(&node_rc);
537        }
538        x.left = Some(Rc::clone(&node_rc));
539        x.parent = y.parent.clone();
540        y.parent = Rc::downgrade(&x_rc);
541
542        drop(x);
543        drop(y);
544
545        Node::update_stats(&node_rc);
546        Node::update_stats(&x_rc);
547        Some(x_rc)
548    }
549
550    fn rotate_right(node_rc: Rc<RefCell<Node>>) -> NodePtr {
551        Node::push_delta(&node_rc);
552        let x_rc = node_rc.borrow_mut().left.take().unwrap();
553        Node::push_delta(&x_rc);
554
555        let mut y = node_rc.borrow_mut();
556        let mut x = x_rc.borrow_mut();
557
558        y.left = x.right.take();
559        if let Some(ref l) = y.left {
560            l.borrow_mut().parent = Rc::downgrade(&node_rc);
561        }
562        x.right = Some(Rc::clone(&node_rc));
563        x.parent = y.parent.clone();
564        y.parent = Rc::downgrade(&x_rc);
565
566        drop(x);
567        drop(y);
568
569        Node::update_stats(&node_rc);
570        Node::update_stats(&x_rc);
571        Some(x_rc)
572    }
573}
574
575#[cfg(test)]
576mod tests {
577    use super::*;
578
579    /// Helper to insert and return the ID, making test setup cleaner.
580    fn insert_marker(tree: &mut IntervalTree, start: u64, end: u64) -> MarkerId {
581        tree.insert(start, end)
582    }
583
584    /// Helper to get position and unwrap, or panic with a clear message.
585    fn get_pos(tree: &IntervalTree, id: MarkerId) -> (u64, u64) {
586        tree.get_position(id)
587            .unwrap_or_else(|| panic!("Marker ID {} not found.", id))
588    }
589
590    #[test]
591    fn test_initial_insert_and_delete() {
592        let mut tree = IntervalTree::new();
593        let id1 = insert_marker(&mut tree, 10, 20);
594        let id2 = insert_marker(&mut tree, 30, 40);
595
596        assert_eq!(get_pos(&tree, id1), (10, 20));
597        assert_eq!(get_pos(&tree, id2), (30, 40));
598
599        assert!(tree.delete(id1));
600        assert_eq!(tree.get_position(id1), None);
601        assert_eq!(get_pos(&tree, id2), (30, 40));
602    }
603
604    #[test]
605    fn test_basic_edit_adjustment() {
606        let mut tree = IntervalTree::new();
607        let id1 = insert_marker(&mut tree, 10, 20); // Before edit
608        let id2 = insert_marker(&mut tree, 30, 40); // At/After edit
609
610        // Insert 5 characters at position 30
611        tree.adjust_for_edit(30, 5);
612
613        // id1 (10-20) should not move
614        assert_eq!(
615            get_pos(&tree, id1),
616            (10, 20),
617            "Marker before edit should not move."
618        );
619
620        // id2 (30-40) should move to (35-45)
621        assert_eq!(
622            get_pos(&tree, id2),
623            (35, 45),
624            "Marker at/after edit should move."
625        );
626
627        // Delete 10 characters at position 5
628        tree.adjust_for_edit(5, -10); // All markers are after position 5
629
630        // id1 (10-20) is inside the deletion [5, 15) and should be clamped and shrunk.
631        assert_eq!(
632            get_pos(&tree, id1),
633            (5, 10),
634            "Marker moved back by deletion."
635        );
636
637        // id2 (35-45) -> (25-35)
638        assert_eq!(
639            get_pos(&tree, id2),
640            (25, 35),
641            "Marker moved back by deletion."
642        );
643    }
644
645    #[test]
646    fn test_problematic_lazy_delta_scenario() {
647        // This test replicates the tricky tree structure to ensure the O(log n) lazy
648        // delta propagation works correctly and doesn't over-propagate to left children.
649
650        let mut tree = IntervalTree::new();
651
652        // Setup the tree with specific positions to force a parent/child relationship
653        // that caused the previous bug:
654        // L(100) -> P(200) <- R(300)
655        let id_p = insert_marker(&mut tree, 200, 250); // Parent node (P)
656        let id_r = insert_marker(&mut tree, 300, 350); // Right child (R)
657        let id_l = insert_marker(&mut tree, 100, 150); // Left child (L)
658
659        // --- Verify initial state ---
660        assert_eq!(
661            get_pos(&tree, id_l),
662            (100, 150),
663            "L initial position incorrect."
664        );
665        assert_eq!(
666            get_pos(&tree, id_p),
667            (200, 250),
668            "P initial position incorrect."
669        );
670        assert_eq!(
671            get_pos(&tree, id_r),
672            (300, 350),
673            "R initial position incorrect."
674        );
675
676        // --- Apply the problematic edit ---
677        // Edit: Insert 50 characters at position 150 (P=150, delta=+50)
678        // L(100) should NOT move (100 < 150).
679        // P(200) and R(300) should move (+50).
680        tree.adjust_for_edit(150, 50);
681
682        // --- Verify corrected final state ---
683
684        // L(100) should have its end expanded (100 < 150, but 150 >= 150).
685        assert_eq!(
686            get_pos(&tree, id_l),
687            (100, 200),
688            "L(100) should expand to (100, 200)."
689        );
690
691        // P(200) should be shifted (200 >= 150) -> 250
692        assert_eq!(
693            get_pos(&tree, id_p),
694            (250, 300),
695            "P(200) did not shift correctly. Should be 250."
696        );
697
698        // R(300) should be shifted (300 >= 150) -> 350
699        assert_eq!(
700            get_pos(&tree, id_r),
701            (350, 400),
702            "R(300) did not shift correctly. Should be 350."
703        );
704    }
705
706    #[test]
707    fn test_interval_spanning_edit() {
708        let mut tree = IntervalTree::new();
709        // Marker S starts before edit, but spans it.
710        let id_s = insert_marker(&mut tree, 50, 200);
711
712        // Edit: Insert 10 characters at position 100 (P=100, delta=+10)
713        tree.adjust_for_edit(100, 10);
714
715        // S(50, 200) starts before 100, so its start (50) is fixed.
716        // Its end (200) is at/after 100, so its end should move to 210.
717        assert_eq!(
718            get_pos(&tree, id_s),
719            (50, 210),
720            "Spanning marker end did not move correctly."
721        );
722    }
723
724    #[test]
725    fn test_deletion_engulfing_marker_start() {
726        let mut tree = IntervalTree::new();
727        let id1 = insert_marker(&mut tree, 8, 20);
728
729        // Delete 10 chars at pos 5. Deletion is on [5, 15).
730        // Marker is on [8, 20). The part [8, 15) is deleted.
731        // New start should be clamped at the deletion position: 5.
732        // End is adjusted by delta: 20 - 10 = 10.
733        // So new interval should be (5, 10).
734        tree.adjust_for_edit(5, -10);
735
736        assert_eq!(
737            get_pos(&tree, id1),
738            (5, 10),
739            "Marker should be clamped and shrunk."
740        );
741    }
742
743    #[test]
744    fn test_zero_length_marker() {
745        let mut tree = IntervalTree::new();
746        let id1 = insert_marker(&mut tree, 10, 10);
747
748        // Insertion at the marker's position should push it.
749        tree.adjust_for_edit(10, 5);
750        assert_eq!(
751            get_pos(&tree, id1),
752            (15, 15),
753            "Insertion at zero-length marker."
754        );
755
756        // Insertion before should also push it.
757        tree.adjust_for_edit(5, 5);
758        assert_eq!(
759            get_pos(&tree, id1),
760            (20, 20),
761            "Insertion before zero-length marker."
762        );
763
764        // Deletion after should not affect it.
765        tree.adjust_for_edit(25, -5);
766        assert_eq!(
767            get_pos(&tree, id1),
768            (20, 20),
769            "Deletion after zero-length marker."
770        );
771
772        // Deletion that contains the marker.
773        tree.adjust_for_edit(15, -10);
774        // Marker at 20. Deletion on [15, 25).
775        // Start becomes max(15, 20-10) = 15.
776        // End becomes max(new_start, 20-10) = max(15, 10) = 15.
777        assert_eq!(
778            get_pos(&tree, id1),
779            (15, 15),
780            "Deletion containing zero-length marker."
781        );
782    }
783
784    #[test]
785    fn test_edit_at_pos_zero() {
786        let mut tree = IntervalTree::new();
787        let id1 = insert_marker(&mut tree, 10, 20);
788
789        // Insertion at pos 0
790        tree.adjust_for_edit(0, 5);
791        assert_eq!(get_pos(&tree, id1), (15, 25), "Insertion at pos 0.");
792
793        // Deletion at pos 0
794        tree.adjust_for_edit(0, -5);
795        assert_eq!(get_pos(&tree, id1), (10, 20), "Deletion at pos 0.");
796
797        // Deletion at pos 0 that engulfs the start.
798        tree.adjust_for_edit(0, -15);
799        // Marker at (10, 20). Deletion on [0, 15).
800        // New start becomes max(0, 10-15) = 0.
801        // New end becomes max(new_start, 20-15) = max(0, 5) = 5.
802        assert_eq!(get_pos(&tree, id1), (0, 5), "Engulfing deletion at pos 0.");
803    }
804
805    #[test]
806    fn test_deletion_preserves_marker_ordering() {
807        // This test reproduces the bug found in prop_marker_ordering_preserved
808        // where lazy delta propagation causes ordering violations.
809        let mut tree = IntervalTree::new();
810
811        // Create markers in order: [0, 10, 20, 30, 40] (spacing=10)
812        let id0 = insert_marker(&mut tree, 0, 0);
813        let id1 = insert_marker(&mut tree, 10, 10);
814        let id2 = insert_marker(&mut tree, 20, 20);
815        let id3 = insert_marker(&mut tree, 30, 30);
816        let id4 = insert_marker(&mut tree, 40, 40);
817
818        // Verify initial state
819        assert_eq!(get_pos(&tree, id0), (0, 0));
820        assert_eq!(get_pos(&tree, id1), (10, 10));
821        assert_eq!(get_pos(&tree, id2), (20, 20));
822        assert_eq!(get_pos(&tree, id3), (30, 30));
823        assert_eq!(get_pos(&tree, id4), (40, 40));
824
825        // Delete 16 bytes starting at position 5
826        // This deletes range [5, 21)
827        // Expected positions after: [0, 5, 5, 14, 24]
828        tree.adjust_for_edit(5, -16);
829
830        // Get all positions
831        let positions = vec![
832            get_pos(&tree, id0).0,
833            get_pos(&tree, id1).0,
834            get_pos(&tree, id2).0,
835            get_pos(&tree, id3).0,
836            get_pos(&tree, id4).0,
837        ];
838
839        // Verify ordering is preserved (no inversions)
840        for i in 0..positions.len() - 1 {
841            assert!(
842                positions[i] <= positions[i + 1],
843                "Ordering violated at index {}: {:?}[{}]={} > {:?}[{}]={}",
844                i,
845                positions,
846                i,
847                positions[i],
848                positions,
849                i + 1,
850                positions[i + 1]
851            );
852        }
853
854        // Verify specific expected positions
855        assert_eq!(get_pos(&tree, id0), (0, 0), "Marker at 0 should stay at 0");
856        assert_eq!(
857            get_pos(&tree, id1),
858            (5, 5),
859            "Marker at 10 should clamp to 5"
860        );
861        assert_eq!(
862            get_pos(&tree, id2),
863            (5, 5),
864            "Marker at 20 should clamp to 5"
865        );
866        assert_eq!(
867            get_pos(&tree, id3),
868            (14, 14),
869            "Marker at 30 should shift to 14"
870        );
871        assert_eq!(
872            get_pos(&tree, id4),
873            (24, 24),
874            "Marker at 40 should shift to 24"
875        );
876    }
877}