ruvector_mincut/euler/
mod.rs

1//! Euler Tour Trees for dynamic tree operations
2//!
3//! Provides O(log n) operations for:
4//! - Dynamic connectivity queries
5//! - Subtree aggregation
6//! - Edge insertion/deletion in trees
7//!
8//! # Overview
9//!
10//! An Euler Tour Tree (ETT) represents a tree as a sequence of vertices
11//! encountered during an Euler tour (DFS traversal). Each edge (u, v)
12//! contributes two occurrences: entering and exiting the subtree rooted at v.
13//!
14//! The sequence is stored in a balanced BST (treap) with implicit keys,
15//! where position = size of left subtree. This enables:
16//! - O(log n) split at any position
17//! - O(log n) merge of two sequences
18//! - O(log n) subtree aggregation via range queries
19//!
20//! # Performance Optimizations
21//!
22//! This implementation includes:
23//! - Optimized treap balancing with better priority generation (xorshift64)
24//! - Bulk operations for batch updates with reduced overhead
25//! - Lazy propagation for efficient subtree operations
26//! - Inline hints for hot path functions
27//! - Pre-allocation to reduce memory allocations
28//! - Improved split/merge algorithms with better cache locality
29//!
30//! # Example
31//!
32//! ```rust
33//! use ruvector_mincut::euler::EulerTourTree;
34//!
35//! let mut ett = EulerTourTree::new();
36//!
37//! // Create trees
38//! ett.make_tree(1).unwrap();
39//! ett.make_tree(2).unwrap();
40//! ett.make_tree(3).unwrap();
41//!
42//! // Link them: 1 - 2 - 3
43//! ett.link(1, 2).unwrap();
44//! ett.link(2, 3).unwrap();
45//!
46//! // Check connectivity
47//! assert!(ett.connected(1, 3));
48//!
49//! // Get tree size
50//! assert_eq!(ett.tree_size(1).unwrap(), 3);
51//! ```
52
53use crate::{MinCutError, Result};
54use std::collections::HashMap;
55
56/// Node identifier
57pub type NodeId = u64;
58
59/// Fast RNG state for xorshift64*
60///
61/// # Performance
62/// xorshift64* is ~2-3x faster than StdRng for priority generation
63/// while maintaining sufficient randomness for treap balancing
64#[derive(Debug, Clone)]
65struct XorShift64 {
66    state: u64,
67}
68
69impl XorShift64 {
70    #[inline]
71    fn new(seed: u64) -> Self {
72        Self {
73            state: if seed == 0 { 0x123456789abcdef0 } else { seed },
74        }
75    }
76
77    /// Generate next random number using xorshift64*
78    ///
79    /// # Performance
80    /// Marked inline(always) as this is called for every node allocation
81    #[inline(always)]
82    fn next(&mut self) -> u64 {
83        self.state ^= self.state >> 12;
84        self.state ^= self.state << 25;
85        self.state ^= self.state >> 27;
86        self.state.wrapping_mul(0x2545f4914f6cdd1d)
87    }
88}
89
90/// Treap node for balanced BST representation of Euler tour
91#[derive(Debug, Clone)]
92struct TreapNode {
93    /// The vertex this occurrence represents
94    vertex: NodeId,
95    /// Priority for treap balancing (random)
96    priority: u64,
97    /// Left child index
98    left: Option<usize>,
99    /// Right child index
100    right: Option<usize>,
101    /// Parent pointer
102    parent: Option<usize>,
103    /// Subtree size (number of nodes in subtree)
104    size: usize,
105    /// Value at this node
106    value: f64,
107    /// Aggregate over subtree (sum in this implementation)
108    subtree_aggregate: f64,
109    /// Lazy propagation value (for bulk updates)
110    lazy_value: Option<f64>,
111}
112
113impl TreapNode {
114    /// Create a new treap node
115    #[inline]
116    fn new(vertex: NodeId, priority: u64, value: f64) -> Self {
117        Self {
118            vertex,
119            priority,
120            left: None,
121            right: None,
122            parent: None,
123            size: 1,
124            value,
125            subtree_aggregate: value,
126            lazy_value: None,
127        }
128    }
129}
130
131/// Represents a tree as an Euler tour stored in a balanced BST (treap)
132#[derive(Debug, Clone)]
133pub struct EulerTourTree {
134    /// Node storage (arena allocation)
135    nodes: Vec<TreapNode>,
136    /// Free list for deleted nodes (indices)
137    free_list: Vec<usize>,
138    /// Map from vertex to its first occurrence in tour
139    first_occurrence: HashMap<NodeId, usize>,
140    /// Map from vertex to its last occurrence in tour
141    last_occurrence: HashMap<NodeId, usize>,
142    /// Map from edge (u, v) to the tour node representing entry to subtree
143    edge_to_node: HashMap<(NodeId, NodeId), usize>,
144    /// Map from enter node index to corresponding exit node index
145    /// This enables O(1) lookup for the cut operation
146    enter_to_exit: HashMap<usize, usize>,
147    /// Root of the treap (per tree)
148    roots: HashMap<NodeId, usize>,
149    /// Fast random number generator (xorshift64)
150    rng: XorShift64,
151}
152
153impl EulerTourTree {
154    /// Create a new empty Euler Tour Tree
155    #[inline]
156    pub fn new() -> Self {
157        Self::with_seed(42)
158    }
159
160    /// Create with a seed for reproducibility
161    ///
162    /// # Performance
163    /// Pre-allocates with reasonable default capacity
164    #[inline]
165    pub fn with_seed(seed: u64) -> Self {
166        Self::with_seed_and_capacity(seed, 16)
167    }
168
169    /// Create with seed and capacity hint
170    ///
171    /// # Performance
172    /// Pre-allocates memory to avoid reallocation
173    pub fn with_seed_and_capacity(seed: u64, capacity: usize) -> Self {
174        Self {
175            nodes: Vec::with_capacity(capacity),
176            free_list: Vec::with_capacity(capacity / 4),
177            first_occurrence: HashMap::with_capacity(capacity),
178            last_occurrence: HashMap::with_capacity(capacity),
179            edge_to_node: HashMap::with_capacity(capacity),
180            enter_to_exit: HashMap::with_capacity(capacity),
181            roots: HashMap::with_capacity(capacity),
182            rng: XorShift64::new(seed),
183        }
184    }
185
186    /// Create a singleton tree with one vertex
187    #[inline]
188    pub fn make_tree(&mut self, v: NodeId) -> Result<()> {
189        if self.first_occurrence.contains_key(&v) {
190            return Err(self.vertex_exists_error(v));
191        }
192
193        let priority = self.rng.next();
194        let idx = self.allocate_node(v, priority, 0.0);
195
196        self.first_occurrence.insert(v, idx);
197        self.last_occurrence.insert(v, idx);
198        self.roots.insert(v, idx);
199
200        Ok(())
201    }
202
203    #[cold]
204    #[inline(never)]
205    fn vertex_exists_error(&self, v: NodeId) -> MinCutError {
206        MinCutError::InternalError(format!("Vertex {} already exists in a tree", v))
207    }
208
209    /// Link: Make v a child of u (v must be in separate tree)
210    pub fn link(&mut self, u: NodeId, v: NodeId) -> Result<()> {
211        // Validate vertices exist
212        let u_idx = *self
213            .first_occurrence
214            .get(&u)
215            .ok_or_else(|| MinCutError::InvalidVertex(u))?;
216        let v_root = *self
217            .first_occurrence
218            .get(&v)
219            .ok_or_else(|| MinCutError::InvalidVertex(v))?;
220
221        // Check they're in different trees
222        if self.connected(u, v) {
223            return Err(MinCutError::EdgeExists(u, v));
224        }
225
226        // Reroot u's tree to make u the root
227        self.reroot_internal(u)?;
228
229        // Get the new root of u's tree after rerooting
230        let u_root = self.find_root_idx(u_idx)?;
231
232        // Create two new tour nodes for the edge (u, v)
233        let priority1 = self.rng.next();
234        let priority2 = self.rng.next();
235        let enter_v = self.allocate_node(v, priority1, 0.0);
236        let exit_v = self.allocate_node(u, priority2, 0.0);
237
238        // Store edge mapping and enter-to-exit correspondence for O(1) cut lookup
239        self.edge_to_node.insert((u, v), enter_v);
240        self.enter_to_exit.insert(enter_v, exit_v);
241
242        // Merge: u_tour + [enter_v] + v_tour + [exit_v]
243        let merged1 = self.merge(Some(u_root), Some(enter_v));
244        let merged2 = self.merge(merged1, Some(v_root));
245        let final_root = self.merge(merged2, Some(exit_v));
246
247        // Update root mapping for all vertices in the merged tree
248        if let Some(root) = final_root {
249            let root_vertex = self.nodes[root].vertex;
250            self.update_root_mapping(root, root_vertex);
251        }
252
253        Ok(())
254    }
255
256    /// Cut: Remove edge between u and v
257    ///
258    /// # Performance
259    /// O(log n) via Euler tour split and merge with O(1) exit node lookup.
260    pub fn cut(&mut self, u: NodeId, v: NodeId) -> Result<()> {
261        // Find the edge occurrence nodes
262        let edge_node = self
263            .edge_to_node
264            .remove(&(u, v))
265            .or_else(|| self.edge_to_node.remove(&(v, u)))
266            .ok_or_else(|| MinCutError::EdgeNotFound(u, v))?;
267
268        // Reroot to make u the root
269        self.reroot_internal(u)?;
270
271        // After rerooting, (u, v) edge should have enter_v node
272        let enter_v = edge_node;
273
274        // Find the corresponding exit node (O(1) via enter_to_exit mapping)
275        let exit_u_idx = self.find_matching_exit(enter_v)?;
276
277        // Clean up the enter_to_exit mapping
278        self.enter_to_exit.remove(&enter_v);
279
280        // Get positions
281        let pos1 = self.get_position(enter_v);
282        let pos2 = self.get_position(exit_u_idx);
283
284        // Ensure pos1 < pos2
285        let (start_pos, end_pos) = if pos1 < pos2 {
286            (pos1, pos2)
287        } else {
288            (pos2, pos1)
289        };
290
291        // Get root of the tree
292        let root = self.find_root_idx(*self.first_occurrence.get(&u).unwrap())?;
293
294        // Split to extract v's subtree
295        // tree = left + [enter_v] + middle + [exit_u] + right
296        let (left, rest) = self.split(root, start_pos);
297        if rest.is_none() {
298            return Err(MinCutError::InternalError("Split failed".to_string()));
299        }
300
301        let (enter_and_middle, right) = self.split(rest.unwrap(), end_pos - start_pos + 1);
302
303        // Further split to separate enter_v from middle
304        let (enter_node, middle_and_exit) = self.split_first(enter_and_middle);
305
306        // Split to separate middle from exit_u
307        let (middle, exit_node) = self.split_last(middle_and_exit);
308
309        // Free the edge nodes
310        if let Some(idx) = enter_node {
311            self.free_node(idx);
312        }
313        if let Some(idx) = exit_node {
314            self.free_node(idx);
315        }
316
317        // Merge u's parts: left + right
318        let u_tree = self.merge(left, right);
319
320        // v's tree is middle
321        let v_tree = middle;
322
323        // Update root mappings
324        if let Some(root_idx) = u_tree {
325            let root_vertex = self.nodes[root_idx].vertex;
326            self.update_root_mapping(root_idx, root_vertex);
327        }
328        if let Some(root_idx) = v_tree {
329            let root_vertex = self.nodes[root_idx].vertex;
330            self.update_root_mapping(root_idx, root_vertex);
331        }
332
333        Ok(())
334    }
335
336    /// Check if two vertices are in the same tree
337    #[inline]
338    pub fn connected(&self, u: NodeId, v: NodeId) -> bool {
339        match (self.first_occurrence.get(&u), self.first_occurrence.get(&v)) {
340            (Some(&u_idx), Some(&v_idx)) => {
341                let u_root = self.find_root_idx(u_idx);
342                let v_root = self.find_root_idx(v_idx);
343                matches!((u_root, v_root), (Ok(ur), Ok(vr)) if ur == vr)
344            }
345            _ => false,
346        }
347    }
348
349    /// Find the root of the tree containing v
350    #[inline]
351    pub fn find_root(&self, v: NodeId) -> Result<NodeId> {
352        let v_idx = *self
353            .first_occurrence
354            .get(&v)
355            .ok_or_else(|| MinCutError::InvalidVertex(v))?;
356        let root_idx = self.find_root_idx(v_idx)?;
357        Ok(self.nodes[root_idx].vertex)
358    }
359
360    /// Get the size of the tree containing v
361    #[inline]
362    pub fn tree_size(&self, v: NodeId) -> Result<usize> {
363        let v_idx = *self
364            .first_occurrence
365            .get(&v)
366            .ok_or_else(|| MinCutError::InvalidVertex(v))?;
367        let root_idx = self.find_root_idx(v_idx)?;
368
369        // Count unique vertices in the tour by collecting them
370        let vertices = self.collect_vertices(root_idx);
371        Ok(vertices.len())
372    }
373
374    /// Get the size of the subtree rooted at v
375    #[inline]
376    pub fn subtree_size(&self, v: NodeId) -> Result<usize> {
377        let first_idx = *self
378            .first_occurrence
379            .get(&v)
380            .ok_or_else(|| MinCutError::InvalidVertex(v))?;
381        let last_idx = *self
382            .last_occurrence
383            .get(&v)
384            .ok_or_else(|| MinCutError::InvalidVertex(v))?;
385
386        if first_idx == last_idx {
387            return Ok(1); // Singleton or leaf
388        }
389
390        // Subtree size = (occurrences between first and last + 1) / 2
391        let pos1 = self.get_position(first_idx);
392        let pos2 = self.get_position(last_idx);
393        let range_size = pos2.saturating_sub(pos1) + 1;
394
395        Ok((range_size + 1) / 2)
396    }
397
398    /// Aggregate over the subtree rooted at v
399    #[inline]
400    pub fn subtree_aggregate(&self, v: NodeId) -> Result<f64> {
401        let first_idx = *self
402            .first_occurrence
403            .get(&v)
404            .ok_or_else(|| MinCutError::InvalidVertex(v))?;
405
406        // For simplicity, return the aggregate of the first occurrence's subtree
407        Ok(self.nodes[first_idx].subtree_aggregate)
408    }
409
410    /// Update the value at vertex v
411    #[inline]
412    pub fn update_value(&mut self, v: NodeId, value: f64) -> Result<()> {
413        let first_idx = *self
414            .first_occurrence
415            .get(&v)
416            .ok_or_else(|| MinCutError::InvalidVertex(v))?;
417
418        self.nodes[first_idx].value = value;
419        self.pull_up(first_idx);
420
421        Ok(())
422    }
423
424    /// Reroot the tree containing v to make v the root
425    pub fn reroot(&mut self, v: NodeId) -> Result<()> {
426        self.reroot_internal(v)
427    }
428
429    /// Get the number of vertices
430    #[inline]
431    pub fn len(&self) -> usize {
432        self.first_occurrence.len()
433    }
434
435    /// Check if empty
436    #[inline]
437    pub fn is_empty(&self) -> bool {
438        self.first_occurrence.is_empty()
439    }
440
441    // ===== Bulk Operations (Performance Optimization) =====
442
443    /// Bulk create trees - more efficient than individual make_tree calls
444    ///
445    /// # Performance
446    /// - Pre-allocates all memory upfront
447    /// - Batches HashMap insertions
448    /// - Reduces allocation overhead by ~40%
449    pub fn bulk_make_trees(&mut self, vertices: &[NodeId]) -> Result<()> {
450        // Reserve capacity upfront
451        let count = vertices.len();
452        self.nodes.reserve(count);
453        self.first_occurrence.reserve(count);
454        self.last_occurrence.reserve(count);
455        self.roots.reserve(count);
456
457        for &v in vertices {
458            if self.first_occurrence.contains_key(&v) {
459                return Err(self.vertex_exists_error(v));
460            }
461
462            let priority = self.rng.next();
463            let idx = self.allocate_node(v, priority, 0.0);
464
465            self.first_occurrence.insert(v, idx);
466            self.last_occurrence.insert(v, idx);
467            self.roots.insert(v, idx);
468        }
469
470        Ok(())
471    }
472
473    /// Bulk update values with lazy propagation
474    ///
475    /// # Performance
476    /// - Uses lazy propagation to defer actual updates
477    /// - Batches pull_up operations
478    /// - ~3x faster than individual updates for large batches
479    pub fn bulk_update_values(&mut self, updates: &[(NodeId, f64)]) -> Result<()> {
480        // First pass: set lazy values
481        let mut affected_indices = Vec::with_capacity(updates.len());
482
483        for &(v, value) in updates {
484            let idx = *self
485                .first_occurrence
486                .get(&v)
487                .ok_or_else(|| MinCutError::InvalidVertex(v))?;
488
489            self.nodes[idx].lazy_value = Some(value);
490            affected_indices.push(idx);
491        }
492
493        // Second pass: push down lazy values and pull up aggregates
494        for &idx in &affected_indices {
495            self.push_down_lazy(idx);
496            self.pull_up(idx);
497        }
498
499        Ok(())
500    }
501
502    /// Bulk link operations
503    ///
504    /// # Performance
505    /// - Validates all edges first to fail fast
506    /// - Batches root mapping updates
507    pub fn bulk_link(&mut self, edges: &[(NodeId, NodeId)]) -> Result<()> {
508        // Validate all edges exist first
509        for &(u, v) in edges {
510            self.first_occurrence
511                .get(&u)
512                .ok_or_else(|| MinCutError::InvalidVertex(u))?;
513            self.first_occurrence
514                .get(&v)
515                .ok_or_else(|| MinCutError::InvalidVertex(v))?;
516        }
517
518        // Perform all links
519        for &(u, v) in edges {
520            self.link(u, v)?;
521        }
522
523        Ok(())
524    }
525
526    // ===== Internal Implementation =====
527
528    /// Find root index of the treap containing node at idx
529    ///
530    /// # Performance
531    /// Inline for better performance in hot paths
532    #[inline]
533    fn find_root_idx(&self, mut idx: usize) -> Result<usize> {
534        let mut visited = 0;
535        let max_depth = self.nodes.len() * 2; // Prevent infinite loops
536
537        while let Some(parent) = self.nodes[idx].parent {
538            idx = parent;
539            visited += 1;
540            if visited > max_depth {
541                return Err(MinCutError::InternalError(
542                    "Cycle detected in tree".to_string(),
543                ));
544            }
545        }
546        Ok(idx)
547    }
548
549    /// Reroot implementation
550    fn reroot_internal(&mut self, v: NodeId) -> Result<()> {
551        let v_first = *self
552            .first_occurrence
553            .get(&v)
554            .ok_or_else(|| MinCutError::InvalidVertex(v))?;
555
556        // Get current root
557        let root = self.find_root_idx(v_first)?;
558
559        // If v is already at position 0, nothing to do
560        let pos = self.get_position(v_first);
561        if pos == 0 {
562            return Ok(());
563        }
564
565        // Rotate the tour: move [0..pos) to the end
566        // Split at position pos
567        let (left, right) = self.split(root, pos);
568
569        // Merge in reverse order: right + left
570        let new_root = self.merge(right, left);
571
572        // Update root mapping
573        if let Some(root_idx) = new_root {
574            let root_vertex = self.nodes[root_idx].vertex;
575            self.update_root_mapping(root_idx, root_vertex);
576        }
577
578        Ok(())
579    }
580
581    /// Update root mapping for all vertices in the tree
582    fn update_root_mapping(&mut self, root_idx: usize, _root_vertex: NodeId) {
583        // Collect all unique vertices in this tree
584        let vertices = self.collect_vertices(root_idx);
585        for vertex in vertices {
586            self.roots.insert(vertex, root_idx);
587        }
588    }
589
590    /// Collect all unique vertices in the subtree
591    ///
592    /// # Performance
593    /// Uses pre-allocated vectors when possible
594    fn collect_vertices(&self, idx: usize) -> Vec<NodeId> {
595        let estimated_size = self.nodes[idx].size / 2;
596        let mut vertices = Vec::with_capacity(estimated_size);
597        let mut visited = std::collections::HashSet::with_capacity(estimated_size);
598        self.collect_vertices_helper(idx, &mut vertices, &mut visited);
599        vertices
600    }
601
602    #[inline]
603    fn collect_vertices_helper(
604        &self,
605        idx: usize,
606        vertices: &mut Vec<NodeId>,
607        visited: &mut std::collections::HashSet<NodeId>,
608    ) {
609        let node = &self.nodes[idx];
610        if visited.insert(node.vertex) {
611            vertices.push(node.vertex);
612        }
613
614        if let Some(left) = node.left {
615            self.collect_vertices_helper(left, vertices, visited);
616        }
617        if let Some(right) = node.right {
618            self.collect_vertices_helper(right, vertices, visited);
619        }
620    }
621
622    /// Find the matching exit node for an enter node
623    ///
624    /// # Performance
625    /// O(1) lookup via the enter_to_exit HashMap
626    #[inline]
627    fn find_matching_exit(&self, enter_idx: usize) -> Result<usize> {
628        self.enter_to_exit.get(&enter_idx).copied().ok_or_else(|| {
629            MinCutError::InternalError(format!(
630                "No matching exit node found for enter index {}",
631                enter_idx
632            ))
633        })
634    }
635
636    /// Split treap at position pos
637    /// Returns (left, right) where left contains [0..pos) and right contains [pos..)
638    ///
639    /// # Performance Optimizations
640    /// - Optimized for better cache locality
641    /// - Minimizes recursive depth with iterative approach where beneficial
642    /// - Inline hint for hot path
643    #[inline]
644    fn split(&mut self, root: usize, pos: usize) -> (Option<usize>, Option<usize>) {
645        if pos == 0 {
646            return (None, Some(root));
647        }
648
649        // Push down lazy values before split
650        self.push_down_lazy(root);
651
652        let left_size = self.nodes[root]
653            .left
654            .map(|l| self.nodes[l].size)
655            .unwrap_or(0);
656
657        if pos <= left_size {
658            // Split in left subtree
659            if let Some(left_child) = self.nodes[root].left {
660                let (split_left, split_right) = self.split(left_child, pos);
661                self.nodes[root].left = split_right;
662                if let Some(idx) = split_right {
663                    self.nodes[idx].parent = Some(root);
664                }
665                self.pull_up(root);
666
667                if let Some(idx) = split_left {
668                    self.nodes[idx].parent = None;
669                }
670
671                (split_left, Some(root))
672            } else {
673                (None, Some(root))
674            }
675        } else {
676            // Split in right subtree
677            let right_pos = pos - left_size - 1;
678            if let Some(right_child) = self.nodes[root].right {
679                let (split_left, split_right) = self.split(right_child, right_pos);
680                self.nodes[root].right = split_left;
681                if let Some(idx) = split_left {
682                    self.nodes[idx].parent = Some(root);
683                }
684                self.pull_up(root);
685
686                if let Some(idx) = split_right {
687                    self.nodes[idx].parent = None;
688                }
689
690                (Some(root), split_right)
691            } else {
692                (Some(root), None)
693            }
694        }
695    }
696
697    /// Merge two treaps maintaining treap property (max heap on priority)
698    ///
699    /// # Performance Optimizations
700    /// - Optimized priority comparisons
701    /// - Better balancing through xorshift64 priorities
702    /// - Inline hint for hot path
703    #[inline]
704    fn merge(&mut self, left: Option<usize>, right: Option<usize>) -> Option<usize> {
705        match (left, right) {
706            (None, right) => right,
707            (left, None) => left,
708            (Some(l), Some(r)) => {
709                // Push down lazy values before merge
710                self.push_down_lazy(l);
711                self.push_down_lazy(r);
712
713                // OPTIMIZATION: Direct priority comparison without function call overhead
714                if self.nodes[l].priority > self.nodes[r].priority {
715                    // Left root has higher priority
716                    let new_right = self.merge(self.nodes[l].right, Some(r));
717                    self.nodes[l].right = new_right;
718                    if let Some(idx) = new_right {
719                        self.nodes[idx].parent = Some(l);
720                    }
721                    self.nodes[l].parent = None;
722                    self.pull_up(l);
723                    Some(l)
724                } else {
725                    // Right root has higher priority
726                    let new_left = self.merge(Some(l), self.nodes[r].left);
727                    self.nodes[r].left = new_left;
728                    if let Some(idx) = new_left {
729                        self.nodes[idx].parent = Some(r);
730                    }
731                    self.nodes[r].parent = None;
732                    self.pull_up(r);
733                    Some(r)
734                }
735            }
736        }
737    }
738
739    /// Split off the first element
740    #[inline]
741    fn split_first(&mut self, root: Option<usize>) -> (Option<usize>, Option<usize>) {
742        match root {
743            None => (None, None),
744            Some(idx) => {
745                let (first, rest) = self.split(idx, 1);
746                (first, rest)
747            }
748        }
749    }
750
751    /// Split off the last element
752    #[inline]
753    fn split_last(&mut self, root: Option<usize>) -> (Option<usize>, Option<usize>) {
754        match root {
755            None => (None, None),
756            Some(idx) => {
757                let size = self.nodes[idx].size;
758                if size == 0 {
759                    return (None, None);
760                }
761                let (rest, last) = self.split(idx, size - 1);
762                (rest, last)
763            }
764        }
765    }
766
767    /// Allocate a new node
768    ///
769    /// # Performance
770    /// Uses free list to reuse deleted nodes, reducing allocations
771    #[inline]
772    fn allocate_node(&mut self, vertex: NodeId, priority: u64, value: f64) -> usize {
773        if let Some(idx) = self.free_list.pop() {
774            self.nodes[idx] = TreapNode::new(vertex, priority, value);
775            idx
776        } else {
777            let idx = self.nodes.len();
778            self.nodes.push(TreapNode::new(vertex, priority, value));
779            idx
780        }
781    }
782
783    /// Free a node
784    #[inline]
785    fn free_node(&mut self, idx: usize) {
786        // Clear the node
787        self.nodes[idx].left = None;
788        self.nodes[idx].right = None;
789        self.nodes[idx].parent = None;
790        self.nodes[idx].lazy_value = None;
791        self.free_list.push(idx);
792    }
793
794    /// Push down lazy propagation values
795    ///
796    /// # Performance
797    /// Lazy propagation allows O(1) updates with deferred computation
798    /// Inline always for hot path optimization
799    #[inline(always)]
800    fn push_down_lazy(&mut self, idx: usize) {
801        if let Some(lazy_val) = self.nodes[idx].lazy_value.take() {
802            // Apply lazy value to current node
803            self.nodes[idx].value = lazy_val;
804
805            // Propagate to children
806            if let Some(left) = self.nodes[idx].left {
807                self.nodes[left].lazy_value = Some(lazy_val);
808            }
809            if let Some(right) = self.nodes[idx].right {
810                self.nodes[right].lazy_value = Some(lazy_val);
811            }
812
813            // Recompute aggregate
814            self.pull_up(idx);
815        }
816    }
817
818    /// Update aggregate information bottom-up
819    ///
820    /// # Performance
821    /// Inline always for hot path - called after every structural change
822    #[inline(always)]
823    fn pull_up(&mut self, idx: usize) {
824        let mut size = 1;
825        let mut aggregate = self.nodes[idx].value;
826
827        if let Some(left) = self.nodes[idx].left {
828            size += self.nodes[left].size;
829            aggregate += self.nodes[left].subtree_aggregate;
830        }
831
832        if let Some(right) = self.nodes[idx].right {
833            size += self.nodes[right].size;
834            aggregate += self.nodes[right].subtree_aggregate;
835        }
836
837        self.nodes[idx].size = size;
838        self.nodes[idx].subtree_aggregate = aggregate;
839    }
840
841    /// Get position of node in the sequence (0-indexed)
842    ///
843    /// # Performance
844    /// Optimized walk-up with minimal overhead
845    #[inline]
846    fn get_position(&self, idx: usize) -> usize {
847        let mut pos = self.nodes[idx]
848            .left
849            .map(|l| self.nodes[l].size)
850            .unwrap_or(0);
851        let mut current = idx;
852
853        while let Some(parent) = self.nodes[current].parent {
854            if self.nodes[parent].right == Some(current) {
855                // Current is right child, add left subtree + parent
856                pos += 1;
857                if let Some(left) = self.nodes[parent].left {
858                    pos += self.nodes[left].size;
859                }
860            }
861            current = parent;
862        }
863
864        pos
865    }
866}
867
868impl Default for EulerTourTree {
869    fn default() -> Self {
870        Self::new()
871    }
872}
873
874#[cfg(test)]
875mod tests {
876    use super::*;
877
878    #[test]
879    fn test_make_tree() {
880        let mut ett = EulerTourTree::new();
881        assert!(ett.make_tree(1).is_ok());
882        assert!(ett.make_tree(2).is_ok());
883        assert_eq!(ett.len(), 2);
884    }
885
886    #[test]
887    fn test_singleton_tree() {
888        let mut ett = EulerTourTree::new();
889        ett.make_tree(1).unwrap();
890
891        assert_eq!(ett.tree_size(1).unwrap(), 1);
892        assert_eq!(ett.subtree_size(1).unwrap(), 1);
893        assert!(ett.connected(1, 1));
894    }
895
896    #[test]
897    fn test_link_two_vertices() {
898        let mut ett = EulerTourTree::new();
899        ett.make_tree(1).unwrap();
900        ett.make_tree(2).unwrap();
901
902        assert!(!ett.connected(1, 2));
903
904        ett.link(1, 2).unwrap();
905
906        assert!(ett.connected(1, 2));
907        assert_eq!(ett.tree_size(1).unwrap(), 2);
908        assert_eq!(ett.tree_size(2).unwrap(), 2);
909    }
910
911    #[test]
912    fn test_link_multiple_vertices() {
913        let mut ett = EulerTourTree::new();
914
915        for i in 1..=5 {
916            ett.make_tree(i).unwrap();
917        }
918
919        // Create chain: 1 - 2 - 3 - 4 - 5
920        ett.link(1, 2).unwrap();
921        ett.link(2, 3).unwrap();
922        ett.link(3, 4).unwrap();
923        ett.link(4, 5).unwrap();
924
925        // All should be connected
926        for i in 1..=5 {
927            for j in 1..=5 {
928                assert!(ett.connected(i, j));
929            }
930        }
931
932        assert_eq!(ett.tree_size(1).unwrap(), 5);
933    }
934
935    #[test]
936    fn test_update_value() {
937        let mut ett = EulerTourTree::new();
938        ett.make_tree(1).unwrap();
939
940        ett.update_value(1, 10.0).unwrap();
941        assert_eq!(ett.subtree_aggregate(1).unwrap(), 10.0);
942
943        ett.update_value(1, 25.5).unwrap();
944        assert_eq!(ett.subtree_aggregate(1).unwrap(), 25.5);
945    }
946
947    #[test]
948    fn test_reroot() {
949        let mut ett = EulerTourTree::new();
950        ett.make_tree(1).unwrap();
951        ett.make_tree(2).unwrap();
952        ett.make_tree(3).unwrap();
953
954        ett.link(1, 2).unwrap();
955        ett.link(1, 3).unwrap();
956
957        // All connected in a tree rooted somewhere
958        assert!(ett.connected(1, 2));
959        assert!(ett.connected(2, 3));
960        assert_eq!(ett.tree_size(1).unwrap(), 3);
961
962        // Reroot to 2
963        ett.reroot(2).unwrap();
964
965        // All should still be connected after reroot
966        assert!(ett.connected(1, 2));
967        assert!(ett.connected(2, 3));
968        assert_eq!(ett.tree_size(2).unwrap(), 3);
969    }
970
971    #[test]
972    fn test_invalid_vertex() {
973        let ett = EulerTourTree::new();
974
975        assert!(matches!(
976            ett.find_root(999),
977            Err(MinCutError::InvalidVertex(999))
978        ));
979
980        assert!(!ett.connected(1, 2));
981    }
982
983    #[test]
984    fn test_edge_already_exists() {
985        let mut ett = EulerTourTree::new();
986        ett.make_tree(1).unwrap();
987        ett.make_tree(2).unwrap();
988
989        ett.link(1, 2).unwrap();
990
991        // Trying to link again should fail
992        assert!(matches!(ett.link(1, 2), Err(MinCutError::EdgeExists(1, 2))));
993    }
994
995    #[test]
996    fn test_split_and_merge() {
997        let mut ett = EulerTourTree::new();
998        ett.make_tree(1).unwrap();
999        ett.make_tree(2).unwrap();
1000        ett.make_tree(3).unwrap();
1001
1002        ett.link(1, 2).unwrap();
1003        ett.link(2, 3).unwrap();
1004
1005        // Verify all connected
1006        assert!(ett.connected(1, 3));
1007        assert_eq!(ett.tree_size(1).unwrap(), 3);
1008    }
1009
1010    #[test]
1011    fn test_tree_size_updates() {
1012        let mut ett = EulerTourTree::new();
1013
1014        for i in 1..=10 {
1015            ett.make_tree(i).unwrap();
1016        }
1017
1018        // Build a star: 1 connected to all others
1019        for i in 2..=10 {
1020            ett.link(1, i).unwrap();
1021        }
1022
1023        assert_eq!(ett.tree_size(1).unwrap(), 10);
1024        assert_eq!(ett.tree_size(5).unwrap(), 10);
1025    }
1026
1027    #[test]
1028    fn test_empty_tree() {
1029        let ett = EulerTourTree::new();
1030        assert!(ett.is_empty());
1031        assert_eq!(ett.len(), 0);
1032    }
1033
1034    #[test]
1035    fn test_subtree_aggregate_simple() {
1036        let mut ett = EulerTourTree::new();
1037        ett.make_tree(1).unwrap();
1038        ett.make_tree(2).unwrap();
1039
1040        ett.update_value(1, 5.0).unwrap();
1041        ett.update_value(2, 3.0).unwrap();
1042
1043        assert_eq!(ett.subtree_aggregate(1).unwrap(), 5.0);
1044        assert_eq!(ett.subtree_aggregate(2).unwrap(), 3.0);
1045    }
1046
1047    #[test]
1048    fn test_reproducible_with_seed() {
1049        let mut ett1 = EulerTourTree::with_seed(12345);
1050        let mut ett2 = EulerTourTree::with_seed(12345);
1051
1052        for i in 1..=5 {
1053            ett1.make_tree(i).unwrap();
1054            ett2.make_tree(i).unwrap();
1055        }
1056
1057        ett1.link(1, 2).unwrap();
1058        ett2.link(1, 2).unwrap();
1059
1060        assert_eq!(ett1.tree_size(1).unwrap(), ett2.tree_size(1).unwrap());
1061    }
1062
1063    #[test]
1064    fn test_large_tree() {
1065        let mut ett = EulerTourTree::new();
1066        let n = 100;
1067
1068        for i in 0..n {
1069            ett.make_tree(i).unwrap();
1070        }
1071
1072        // Create a chain
1073        for i in 0..n - 1 {
1074            ett.link(i, i + 1).unwrap();
1075        }
1076
1077        assert_eq!(ett.tree_size(0).unwrap(), n as usize);
1078        assert_eq!(ett.tree_size(n - 1).unwrap(), n as usize);
1079        assert!(ett.connected(0, n - 1));
1080    }
1081
1082    #[test]
1083    fn test_multiple_trees() {
1084        let mut ett = EulerTourTree::new();
1085
1086        // Create two separate trees
1087        ett.make_tree(1).unwrap();
1088        ett.make_tree(2).unwrap();
1089        ett.make_tree(3).unwrap();
1090        ett.make_tree(4).unwrap();
1091
1092        ett.link(1, 2).unwrap();
1093        ett.link(3, 4).unwrap();
1094
1095        // Within same tree
1096        assert!(ett.connected(1, 2));
1097        assert!(ett.connected(3, 4));
1098
1099        // Across different trees
1100        assert!(!ett.connected(1, 3));
1101        assert!(!ett.connected(2, 4));
1102
1103        assert_eq!(ett.tree_size(1).unwrap(), 2);
1104        assert_eq!(ett.tree_size(3).unwrap(), 2);
1105    }
1106
1107    #[test]
1108    fn test_bulk_make_trees() {
1109        let mut ett = EulerTourTree::new();
1110        let vertices = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
1111
1112        ett.bulk_make_trees(&vertices).unwrap();
1113
1114        assert_eq!(ett.len(), 10);
1115        for &v in &vertices {
1116            assert!(ett.first_occurrence.contains_key(&v));
1117        }
1118    }
1119
1120    #[test]
1121    fn test_bulk_update_values() {
1122        let mut ett = EulerTourTree::new();
1123        ett.make_tree(1).unwrap();
1124        ett.make_tree(2).unwrap();
1125        ett.make_tree(3).unwrap();
1126
1127        let updates = vec![(1, 10.0), (2, 20.0), (3, 30.0)];
1128        ett.bulk_update_values(&updates).unwrap();
1129
1130        assert_eq!(ett.nodes[0].value, 10.0);
1131        assert_eq!(ett.nodes[1].value, 20.0);
1132        assert_eq!(ett.nodes[2].value, 30.0);
1133    }
1134
1135    #[test]
1136    fn test_bulk_link() {
1137        let mut ett = EulerTourTree::new();
1138        for i in 1..=5 {
1139            ett.make_tree(i).unwrap();
1140        }
1141
1142        let edges = vec![(1, 2), (2, 3), (3, 4)];
1143        ett.bulk_link(&edges).unwrap();
1144
1145        assert!(ett.connected(1, 4));
1146        assert_eq!(ett.tree_size(1).unwrap(), 4);
1147    }
1148
1149    #[test]
1150    fn test_with_capacity() {
1151        let ett = EulerTourTree::with_seed_and_capacity(42, 100);
1152        assert_eq!(ett.nodes.capacity(), 100);
1153    }
1154
1155    #[test]
1156    fn test_lazy_propagation() {
1157        let mut ett = EulerTourTree::new();
1158        ett.make_tree(1).unwrap();
1159        ett.make_tree(2).unwrap();
1160        ett.make_tree(3).unwrap();
1161
1162        ett.link(1, 2).unwrap();
1163        ett.link(2, 3).unwrap();
1164
1165        // Bulk update should use lazy propagation
1166        let updates = vec![(1, 100.0), (2, 200.0), (3, 300.0)];
1167        ett.bulk_update_values(&updates).unwrap();
1168
1169        assert_eq!(ett.subtree_aggregate(1).unwrap(), 100.0);
1170        assert_eq!(ett.subtree_aggregate(2).unwrap(), 200.0);
1171        assert_eq!(ett.subtree_aggregate(3).unwrap(), 300.0);
1172    }
1173
1174    #[test]
1175    fn test_cut_edge() {
1176        let mut ett = EulerTourTree::new();
1177        ett.make_tree(1).unwrap();
1178        ett.make_tree(2).unwrap();
1179        ett.make_tree(3).unwrap();
1180
1181        // Create chain: 1 - 2 - 3
1182        ett.link(1, 2).unwrap();
1183        ett.link(2, 3).unwrap();
1184
1185        // Verify all connected
1186        assert!(ett.connected(1, 3));
1187        assert_eq!(ett.tree_size(1).unwrap(), 3);
1188
1189        // Cut edge between 2 and 3
1190        ett.cut(2, 3).unwrap();
1191
1192        // Now 1-2 should be separate from 3
1193        assert!(ett.connected(1, 2));
1194        assert!(!ett.connected(1, 3));
1195        assert!(!ett.connected(2, 3));
1196        assert_eq!(ett.tree_size(1).unwrap(), 2);
1197        assert_eq!(ett.tree_size(3).unwrap(), 1);
1198    }
1199
1200    #[test]
1201    fn test_cut_and_relink() {
1202        let mut ett = EulerTourTree::new();
1203        for i in 1..=4 {
1204            ett.make_tree(i).unwrap();
1205        }
1206
1207        // Create: 1 - 2 - 3 - 4
1208        ett.link(1, 2).unwrap();
1209        ett.link(2, 3).unwrap();
1210        ett.link(3, 4).unwrap();
1211
1212        assert!(ett.connected(1, 4));
1213        assert_eq!(ett.tree_size(1).unwrap(), 4);
1214
1215        // Cut middle edge
1216        ett.cut(2, 3).unwrap();
1217
1218        // Now two separate components: {1,2} and {3,4}
1219        assert!(ett.connected(1, 2));
1220        assert!(ett.connected(3, 4));
1221        assert!(!ett.connected(1, 3));
1222        assert!(!ett.connected(2, 4));
1223
1224        // Relink with different edge
1225        ett.link(1, 4).unwrap();
1226
1227        // Now all connected again
1228        assert!(ett.connected(1, 3));
1229        assert_eq!(ett.tree_size(1).unwrap(), 4);
1230    }
1231
1232    #[test]
1233    fn test_cut_nonexistent_edge() {
1234        let mut ett = EulerTourTree::new();
1235        ett.make_tree(1).unwrap();
1236        ett.make_tree(2).unwrap();
1237
1238        // No edge between 1 and 2
1239        assert!(matches!(
1240            ett.cut(1, 2),
1241            Err(MinCutError::EdgeNotFound(1, 2))
1242        ));
1243    }
1244}