Skip to main content

physdes/
dme_algorithm.rs

1//! Deferred Merge Embedding (DME) algorithm for clock tree synthesis.
2//!
3//! Implements the DME algorithm for constructing zero-skew clock trees
4//! with Manhattan geometry. Supports both linear and Elmore delay models.
5//!
6//! Nodes are stored in an arena (`Tree`) and referenced by `usize` index,
7//! avoiding `Rc<RefCell<>>` overhead.
8
9use crate::generic::MinDist;
10use crate::interval::Interval;
11use crate::manhattan_arc::ManhattanArc;
12use crate::point::Point;
13
14/// A clock sink with name, position, and capacitance.
15#[derive(Debug, Clone)]
16pub struct Sink {
17    /// The name of this sink (e.g. "s1", "FF_1")
18    pub name: String,
19    /// The physical position of the sink in the layout
20    pub position: Point<i32, i32>,
21    /// The load capacitance of this sink
22    pub capacitance: f64,
23}
24
25impl Sink {
26    /// Creates a new sink with the given name, position, and capacitance.
27    pub fn new(name: &str, position: Point<i32, i32>, capacitance: f64) -> Self {
28        Sink {
29            name: name.to_string(),
30            position,
31            capacitance,
32        }
33    }
34}
35
36/// Node index used throughout the DME algorithm to reference nodes in the
37/// arena-allocated `Tree`.
38pub type NodeIdx = usize;
39
40/// A node in the clock tree, stored in a `Tree` arena.
41#[derive(Debug, Clone)]
42pub struct TreeNode {
43    /// The name of this node (e.g. "s0", "n1")
44    pub name: String,
45    /// The embedded position of this node in the layout
46    pub position: Point<i32, i32>,
47    /// Index of the left child node, if any
48    pub left: Option<NodeIdx>,
49    /// Index of the right child node, if any
50    pub right: Option<NodeIdx>,
51    /// Index of the parent node, if any
52    pub parent: Option<NodeIdx>,
53    /// Wire segment length from this node to its parent
54    pub wire_length: i32,
55    /// Signal delay at this node
56    pub delay: f64,
57    /// Load capacitance at this node
58    pub capacitance: f64,
59    /// Whether this node's wire needs elongation to satisfy timing
60    pub need_elongation: bool,
61}
62
63impl TreeNode {
64    /// Creates a new tree node with the given name and position.
65    ///
66    /// All other fields are initialized to their default values
67    /// (no children, no parent, zero delay/capacitance/wire length).
68    pub fn new(name: &str, position: Point<i32, i32>) -> Self {
69        TreeNode {
70            name: name.to_string(),
71            position,
72            left: None,
73            right: None,
74            parent: None,
75            wire_length: 0,
76            delay: 0.0,
77            capacitance: 0.0,
78            need_elongation: false,
79        }
80    }
81
82    /// Returns `true` if this node is a leaf (has no children).
83    pub fn is_leaf(&self) -> bool {
84        self.left.is_none() && self.right.is_none()
85    }
86}
87
88/// Arena-allocated tree of `TreeNode`s.
89///
90/// Nodes are stored in a `Vec` and referenced by their index.
91/// This avoids `Rc<RefCell<>>` while still allowing safe mutation
92/// during bottom-up merging and top-down embedding phases.
93#[derive(Debug, Clone, Default)]
94pub struct Tree {
95    nodes: Vec<TreeNode>,
96    /// Index of the root node, if the tree has been built.
97    pub root: Option<NodeIdx>,
98}
99
100impl Tree {
101    /// Creates an empty tree with no nodes.
102    pub fn new() -> Self {
103        Self::default()
104    }
105
106    /// Adds a node to the tree, returning its index.
107    pub fn add(&mut self, node: TreeNode) -> NodeIdx {
108        let idx = self.nodes.len();
109        self.nodes.push(node);
110        idx
111    }
112
113    /// Returns a shared reference to the node at the given index.
114    pub fn get(&self, idx: NodeIdx) -> &TreeNode {
115        &self.nodes[idx]
116    }
117
118    /// Returns a mutable reference to the node at the given index.
119    pub fn get_mut(&mut self, idx: NodeIdx) -> &mut TreeNode {
120        &mut self.nodes[idx]
121    }
122
123    /// Returns the number of nodes in the tree.
124    pub fn len(&self) -> usize {
125        self.nodes.len()
126    }
127
128    /// Returns `true` if the tree contains no nodes.
129    pub fn is_empty(&self) -> bool {
130        self.nodes.is_empty()
131    }
132
133    /// Returns an iterator over all nodes in the tree.
134    pub fn iter(&self) -> impl Iterator<Item = &TreeNode> {
135        self.nodes.iter()
136    }
137
138    /// Simultaneously access two distinct nodes by index (safe, checked).
139    pub fn get_pair_mut(&mut self, a: NodeIdx, b: NodeIdx) -> (&mut TreeNode, &mut TreeNode) {
140        assert_ne!(a, b, "get_pair_mut called with identical indices");
141        if a < b {
142            let (left, right) = self.nodes.split_at_mut(b);
143            (&mut left[a], &mut right[0])
144        } else {
145            let (left, right) = self.nodes.split_at_mut(a);
146            (&mut right[0], &mut left[b])
147        }
148    }
149}
150
151/// Result of a tapping-point calculation.
152///
153/// Carries both the clamped `extend_left` (guaranteed to lie in [0, distance])
154/// and the **raw** (pre-clamp) value so the caller can compute the exact
155/// wire lengths for the two children and the `need_elongation` flags.
156#[derive(Debug, Clone, Copy, PartialEq)]
157pub struct TappingResult {
158    /// Tapping-point offset from the left child, clamped to [0, distance].
159    pub extend_left: i32,
160    /// Raw (pre-clamp) tapping-point offset.
161    pub raw_extend_left: i32,
162    /// Signal delay at the tapping point.
163    pub delay_left: f64,
164}
165
166/// Abstract delay model for wire delay calculation.
167pub trait DelayCalculator {
168    /// Calculates the total wire delay for a given length and load capacitance.
169    ///
170    /// The specific formula depends on the delay model (linear or Elmore).
171    fn calculate_wire_delay(&self, length: i32, load_capacitance: f64) -> f64;
172    /// Calculates the wire delay per unit length for a given load capacitance.
173    fn calculate_wire_delay_per_unit(&self, load_capacitance: f64) -> f64;
174    /// Calculates the total wire capacitance for a given length.
175    fn calculate_wire_capacitance(&self, length: i32) -> f64;
176    /// Computes the tapping point (split location) between two subtrees to
177    /// achieve prescribed skew, given their delays and capacitances.
178    ///
179    /// Returns a `TappingResult` containing both the clamped `extend_left`
180    /// (in [0, distance]) and the raw pre-clamp value, enabling the caller
181    /// to implement the full elongation logic.
182    fn calculate_tapping_point(
183        &self,
184        distance: i32,
185        left_delay: f64,
186        right_delay: f64,
187        left_capacitance: f64,
188        right_capacitance: f64,
189    ) -> TappingResult;
190}
191
192/// Linear delay model where wire delay is proportional to wire length.
193///
194/// `delay = delay_per_unit * length`
195pub struct LinearDelayCalculator {
196    /// Delay per unit length of wire
197    pub delay_per_unit: f64,
198    /// Capacitance per unit length of wire
199    pub capacitance_per_unit: f64,
200}
201
202impl LinearDelayCalculator {
203    /// Creates a linear delay calculator with the given parameters.
204    pub fn new(delay_per_unit: f64, capacitance_per_unit: f64) -> Self {
205        LinearDelayCalculator {
206            delay_per_unit,
207            capacitance_per_unit,
208        }
209    }
210}
211
212impl DelayCalculator for LinearDelayCalculator {
213    /// Linear wire delay: $\text{delay} = \text{delay\_per\_unit} \times \text{length}$
214    fn calculate_wire_delay(&self, length: i32, _load_capacitance: f64) -> f64 {
215        self.delay_per_unit * length as f64
216    }
217    fn calculate_wire_delay_per_unit(&self, _load_capacitance: f64) -> f64 {
218        self.delay_per_unit
219    }
220    fn calculate_wire_capacitance(&self, length: i32) -> f64 {
221        self.capacitance_per_unit * length as f64
222    }
223    /// Tapping point for linear delay model:
224    ///
225    /// $$\text{skew} = \text{right\_delay} - \text{left\_delay}$$
226    ///
227    /// $$\text{raw} = \left\lfloor\frac{\text{skew} / \text{delay\_per\_unit} + \text{distance}}{2}\right\rceil$$
228    fn calculate_tapping_point(
229        &self,
230        distance: i32,
231        left_delay: f64,
232        right_delay: f64,
233        _left_capacitance: f64,
234        _right_capacitance: f64,
235    ) -> TappingResult {
236        if distance == 0 {
237            return TappingResult {
238                extend_left: 0,
239                raw_extend_left: 0,
240                delay_left: left_delay.max(right_delay),
241            };
242        }
243        let skew = right_delay - left_delay;
244        let raw = ((skew / self.delay_per_unit + distance as f64) / 2.0).round() as i32;
245        let delay_left = left_delay + raw as f64 * self.delay_per_unit;
246        let (extend_left, delay_left) = if raw < 0 {
247            (0, left_delay)
248        } else if raw > distance {
249            (distance, right_delay)
250        } else {
251            (raw, delay_left)
252        };
253        TappingResult {
254            extend_left,
255            raw_extend_left: raw,
256            delay_left,
257        }
258    }
259}
260
261/// Elmore delay model: considers distributed wire resistance and capacitance.
262///
263/// `delay = R * (C / 2 + load_capacitance)` where `R` and `C` are
264/// the total resistance and capacitance of the wire segment.
265pub struct ElmoreDelayCalculator {
266    /// Resistance per unit length of wire
267    pub unit_resistance: f64,
268    /// Capacitance per unit length of wire
269    pub unit_capacitance: f64,
270}
271
272impl ElmoreDelayCalculator {
273    /// Creates an Elmore delay calculator with the given resistance and
274    /// capacitance per unit length.
275    pub fn new(unit_resistance: f64, unit_capacitance: f64) -> Self {
276        ElmoreDelayCalculator {
277            unit_resistance,
278            unit_capacitance,
279        }
280    }
281}
282
283impl DelayCalculator for ElmoreDelayCalculator {
284    /// Elmore wire delay: $\text{delay} = R \times \left(\frac{C}{2} + C_{\text{load}}\right)$
285    ///
286    /// where $R = r_{\text{unit}} \times \text{length}$ and $C = c_{\text{unit}} \times \text{length}$.
287    fn calculate_wire_delay(&self, length: i32, load_capacitance: f64) -> f64 {
288        let r = self.unit_resistance * length as f64;
289        let c = self.unit_capacitance * length as f64;
290        r * (c / 2.0 + load_capacitance)
291    }
292    fn calculate_wire_delay_per_unit(&self, load_capacitance: f64) -> f64 {
293        self.unit_resistance * (self.unit_capacitance / 2.0 + load_capacitance)
294    }
295    fn calculate_wire_capacitance(&self, length: i32) -> f64 {
296        self.unit_capacitance * length as f64
297    }
298    /// Tapping point for Elmore delay model. Solves for split fraction $z$:
299    ///
300    /// $$z = \frac{\text{skew} + R(C_W/2 + C_{\text{right}})}{R(C_W + C_{\text{right}} + C_{\text{left}})}$$
301    ///
302    /// where $R = r \cdot \text{distance}$ and $C_W = c \cdot \text{distance}$.
303    fn calculate_tapping_point(
304        &self,
305        distance: i32,
306        left_delay: f64,
307        right_delay: f64,
308        left_capacitance: f64,
309        right_capacitance: f64,
310    ) -> TappingResult {
311        if distance == 0 {
312            return TappingResult {
313                extend_left: 0,
314                raw_extend_left: 0,
315                delay_left: left_delay.max(right_delay),
316            };
317        }
318        let skew = right_delay - left_delay;
319        let r = distance as f64 * self.unit_resistance;
320        let c_w = distance as f64 * self.unit_capacitance;
321        let z = (skew + r * (right_capacitance + c_w / 2.0))
322            / (r * (c_w + right_capacitance + left_capacitance));
323        let raw = (z * distance as f64).round() as i32;
324        let r_left = raw as f64 * self.unit_resistance;
325        let c_left = raw as f64 * self.unit_capacitance;
326        let delay_left = left_delay + r_left * (c_left / 2.0 + left_capacitance);
327        let (extend_left, delay_left) = if raw < 0 {
328            (0, left_delay)
329        } else if raw > distance {
330            (distance, right_delay)
331        } else {
332            (raw, delay_left)
333        };
334        TappingResult {
335            extend_left,
336            raw_extend_left: raw,
337            delay_left,
338        }
339    }
340}
341
342/// Results of clock skew analysis.
343#[derive(Debug, Clone)]
344pub struct SkewAnalysis {
345    /// Maximum signal delay among all sinks
346    pub max_delay: f64,
347    /// Minimum signal delay among all sinks
348    pub min_delay: f64,
349    /// Clock skew = max_delay - min_delay
350    pub skew: f64,
351    /// Individual delays for each sink
352    pub sink_delays: Vec<f64>,
353    /// Total wire length of the clock tree
354    pub total_wirelength: i32,
355    /// Name of the delay model used (e.g. "LinearDelayCalculator")
356    pub delay_model: String,
357}
358
359/// Detailed tree statistics collected from a clock tree.
360#[derive(Debug, Clone)]
361pub struct TreeStatistics {
362    /// List of all nodes with their info
363    pub nodes: Vec<NodeInfo>,
364    /// List of all wires with their info
365    pub wires: Vec<WireInfo>,
366    /// Names of all sink nodes
367    pub sinks: Vec<String>,
368    /// Total number of nodes in the tree
369    pub total_nodes: i32,
370    /// Total number of sink (leaf) nodes
371    pub total_sinks: i32,
372    /// Total number of wire segments
373    pub total_wires: i32,
374}
375
376/// Information about a single node in the clock tree.
377#[derive(Debug, Clone)]
378pub struct NodeInfo {
379    /// Node name
380    pub name: String,
381    /// Node position as `(x, y)` coordinates
382    pub position: (i32, i32),
383    /// Node type: "sink" or "internal"
384    pub node_type: String,
385    /// Signal delay at this node
386    pub delay: f64,
387    /// Load capacitance at this node
388    pub capacitance: f64,
389}
390
391/// Information about a wire segment in the clock tree.
392#[derive(Debug, Clone)]
393pub struct WireInfo {
394    /// Name of the source (parent) node
395    pub from_node: String,
396    /// Name of the destination (child) node
397    pub to_node: String,
398    /// Wire length
399    pub length: i32,
400    /// Source node position as `(x, y)` coordinates
401    pub from_pos: (i32, i32),
402    /// Destination node position as `(x, y)` coordinates
403    pub to_pos: (i32, i32),
404}
405
406// ---------------------------------------------------------------------------
407// DME algorithm
408// ---------------------------------------------------------------------------
409
410/// The DME (Deferred Merge Embedding) algorithm for clock tree synthesis.
411///
412/// Builds a prescribed-skew clock tree using a bottom-up merging phase and
413/// a top-down embedding phase. Uses an arena-allocated node representation
414/// (`Tree`) for cache efficiency. The constructed tree can be queried
415/// via `get_tree()`.
416///
417/// Supports both linear and Elmore delay models via the `DelayCalculator`
418/// trait.
419pub struct DMEAlgorithm {
420    sinks: Vec<Sink>,
421    delay_calculator: Box<dyn DelayCalculator>,
422    node_id: i32,
423    source: Option<Point<i32, i32>>,
424    tree: Tree,
425}
426
427impl DMEAlgorithm {
428    /// Creates a new DME algorithm instance with the given sinks and delay model.
429    ///
430    /// # Panics
431    ///
432    /// Panics if `sinks` is empty.
433    pub fn new(sinks: Vec<Sink>, calculator: Box<dyn DelayCalculator>) -> Self {
434        assert!(!sinks.is_empty(), "No sinks provided");
435        DMEAlgorithm {
436            sinks,
437            delay_calculator: calculator,
438            node_id: 0,
439            source: None,
440            tree: Tree::new(),
441        }
442    }
443
444    /// Creates a new DME algorithm with a specified clock source position.
445    ///
446    /// # Panics
447    ///
448    /// Panics if `sinks` is empty.
449    pub fn with_source(
450        sinks: Vec<Sink>,
451        calculator: Box<dyn DelayCalculator>,
452        source: Point<i32, i32>,
453    ) -> Self {
454        assert!(!sinks.is_empty(), "No sinks provided");
455        DMEAlgorithm {
456            sinks,
457            delay_calculator: calculator,
458            node_id: 0,
459            source: Some(source),
460            tree: Tree::new(),
461        }
462    }
463
464    /// Returns a reference to the constructed tree.
465    pub fn get_tree(&self) -> &Tree {
466        &self.tree
467    }
468
469    /// Returns a mutable reference to the constructed tree.
470    pub fn get_tree_mut(&mut self) -> &mut Tree {
471        &mut self.tree
472    }
473
474    /// Builds the clock tree and returns the root index.
475    pub fn build_clock_tree(&mut self) -> NodeIdx {
476        self.node_id = 0;
477        self.tree = Tree::new();
478
479        for s in &self.sinks {
480            let mut node = TreeNode::new(&s.name, s.position);
481            node.capacitance = s.capacitance;
482            self.tree.add(node);
483        }
484
485        // Free sink memory — no longer needed after building leaf nodes.
486        self.sinks = Vec::new();
487
488        let mut leaf_indices: Vec<NodeIdx> = (0..self.tree.len()).collect();
489        let root = self.build_merging_tree(&mut leaf_indices, false);
490
491        let n = self.tree.len();
492        let mut merging_segments: Vec<Option<ManhattanArc<Interval<i32>>>> = vec![None; n];
493        self.compute_merging_segment(root, &mut merging_segments);
494        self.embed_node(root, None, &merging_segments);
495        self.compute_delays(root, 0.0);
496
497        self.tree.root = Some(root);
498        root
499    }
500
501    /// Build a balanced merging tree by recursive bipartition.
502    ///
503    /// Uses `select_nth_unstable_by` for O(n) median partitioning per level
504    /// instead of O(n log n) full sort.  C++ and Python use the equivalent
505    /// `std::nth_element` / `statistics.median_low` partition so that all
506    /// three produce identical tree topologies.
507    fn build_merging_tree(&mut self, node_ids: &mut [NodeIdx], vertical: bool) -> NodeIdx {
508        if node_ids.len() == 1 {
509            return node_ids[0];
510        }
511
512        let mid = node_ids.len() / 2;
513        if vertical {
514            node_ids.select_nth_unstable_by(mid, |&a, &b| {
515                self.tree
516                    .get(a)
517                    .position
518                    .xcoord
519                    .cmp(&self.tree.get(b).position.xcoord)
520            });
521        } else {
522            node_ids.select_nth_unstable_by(mid, |&a, &b| {
523                self.tree
524                    .get(a)
525                    .position
526                    .ycoord
527                    .cmp(&self.tree.get(b).position.ycoord)
528            });
529        }
530
531        let (left, right) = node_ids.split_at_mut(mid);
532        let left_child = self.build_merging_tree(left, !vertical);
533        let right_child = self.build_merging_tree(right, !vertical);
534
535        let id = format!("n{}", self.node_id);
536        self.node_id += 1;
537        let pos = self.tree.get(left_child).position;
538        let parent_idx = self.tree.add(TreeNode::new(&id, pos));
539
540        self.tree.get_mut(parent_idx).left = Some(left_child);
541        self.tree.get_mut(parent_idx).right = Some(right_child);
542        self.tree.get_mut(left_child).parent = Some(parent_idx);
543        self.tree.get_mut(right_child).parent = Some(parent_idx);
544
545        parent_idx
546    }
547
548    fn compute_merging_segment(
549        &mut self,
550        node: NodeIdx,
551        segments: &mut Vec<Option<ManhattanArc<Interval<i32>>>>,
552    ) -> ManhattanArc<Interval<i32>> {
553        if self.tree.get(node).is_leaf() {
554            let pos = self.tree.get(node).position;
555            let ms1 = ManhattanArc::from_point(pos);
556            let ms = ManhattanArc::new(
557                Interval::new(ms1.xcoord(), ms1.xcoord()),
558                Interval::new(ms1.ycoord(), ms1.ycoord()),
559            );
560            segments[node] = Some(ms);
561            return ms;
562        }
563
564        let left = self
565            .tree
566            .get(node)
567            .left
568            .expect("Internal node missing left child");
569        let right = self
570            .tree
571            .get(node)
572            .right
573            .expect("Internal node missing right child");
574
575        let left_ms = self.compute_merging_segment(left, segments);
576        let right_ms = self.compute_merging_segment(right, segments);
577
578        let distance = left_ms.min_dist_with(&right_ms) as i32;
579
580        let (left_delay, right_delay) = {
581            let ln = self.tree.get(left);
582            let rn = self.tree.get(right);
583            (ln.delay, rn.delay)
584        };
585        let (left_cap, right_cap) = {
586            let ln = self.tree.get(left);
587            let rn = self.tree.get(right);
588            (ln.capacitance, rn.capacitance)
589        };
590
591        let tp = self.delay_calculator.calculate_tapping_point(
592            distance,
593            left_delay,
594            right_delay,
595            left_cap,
596            right_cap,
597        );
598
599        // Apply node side-effects: wire_length and need_elongation.
600        // When the raw extend_left falls outside [0, distance], the elongated
601        // branch gets the raw (pre-clamp) value so its wire exceeds the
602        // segment distance.
603        {
604            let (l_node, r_node) = self.tree.get_pair_mut(left, right);
605            l_node.wire_length = tp.extend_left;
606            r_node.wire_length = distance - tp.raw_extend_left;
607
608            if tp.raw_extend_left < 0 {
609                l_node.wire_length = 0;
610                r_node.wire_length = distance - tp.raw_extend_left;
611                r_node.need_elongation = true;
612                #[cfg(feature = "std")]
613                log::warn!(
614                    "Warning: Right node needs elongation: extend_left < 0  => extend_left \
615                     set to 0"
616                );
617            } else if tp.raw_extend_left > distance {
618                r_node.wire_length = 0;
619                l_node.wire_length = tp.raw_extend_left;
620                l_node.need_elongation = true;
621                #[cfg(feature = "std")]
622                log::warn!(
623                    "Warning: Left node needs elongation: extend_left > distance => \
624                     extend_left set to distance"
625                );
626            }
627        }
628
629        self.tree.get_mut(node).delay = tp.delay_left;
630
631        let merged_segment = left_ms.merge_with(&right_ms, tp.extend_left);
632        segments[node] = Some(merged_segment);
633
634        let wire_cap = self.delay_calculator.calculate_wire_capacitance(distance);
635        self.tree.get_mut(node).capacitance = {
636            let lc = self.tree.get(left).capacitance;
637            let rc = self.tree.get(right).capacitance;
638            lc + rc + wire_cap
639        };
640
641        merged_segment
642    }
643
644    fn embed_node(
645        &mut self,
646        node: NodeIdx,
647        parent_segment: Option<&ManhattanArc<Interval<i32>>>,
648        segments: &Vec<Option<ManhattanArc<Interval<i32>>>>,
649    ) {
650        let node_segment = segments[node]
651            .as_ref()
652            .expect("Merging segment not found for node");
653
654        if parent_segment.is_none() {
655            if let Some(src) = self.source {
656                let nearest = node_segment.nearest_point_to(&src);
657                self.tree.get_mut(node).position = nearest;
658            } else {
659                let upper = node_segment.get_upper_corner();
660                self.tree.get_mut(node).position = upper;
661            }
662        } else {
663            let parent_pos = self
664                .tree
665                .get(node)
666                .parent
667                .map(|p| self.tree.get(p).position);
668            if let Some(pp) = parent_pos {
669                let nearest = node_segment.nearest_point_to(&pp);
670                self.tree.get_mut(node).position = nearest;
671                let dist = self.tree.get(node).position.min_dist_with(&pp) as i32;
672                self.tree.get_mut(node).wire_length = dist;
673            }
674        }
675
676        let left = self.tree.get(node).left;
677        let right = self.tree.get(node).right;
678
679        if let Some(l) = left {
680            self.embed_node(l, Some(node_segment), segments);
681        }
682        if let Some(r) = right {
683            self.embed_node(r, Some(node_segment), segments);
684        }
685    }
686
687    fn compute_delays(&mut self, node: NodeIdx, parent_delay: f64) {
688        let has_parent = self.tree.get(node).parent.is_some();
689        if has_parent {
690            let wl = self.tree.get(node).wire_length;
691            let cap = self.tree.get(node).capacitance;
692            let wire_delay = self.delay_calculator.calculate_wire_delay(wl, cap);
693            self.tree.get_mut(node).delay = parent_delay + wire_delay;
694        } else {
695            self.tree.get_mut(node).delay = 0.0;
696        }
697
698        let current_delay = self.tree.get(node).delay;
699        let left = self.tree.get(node).left;
700        let right = self.tree.get(node).right;
701
702        if let Some(l) = left {
703            self.compute_delays(l, current_delay);
704        }
705        if let Some(r) = right {
706            self.compute_delays(r, current_delay);
707        }
708    }
709
710    /// Analyze clock skew from the constructed tree.
711    pub fn analyze_skew(&self, root: NodeIdx) -> SkewAnalysis {
712        let mut sink_delays = Vec::new();
713        collect_sink_delays(&self.tree, root, &mut sink_delays);
714
715        if sink_delays.is_empty() {
716            panic!("No sink delays collected");
717        }
718
719        let max_delay = sink_delays
720            .iter()
721            .cloned()
722            .fold(f64::NEG_INFINITY, f64::max);
723        let min_delay = sink_delays.iter().cloned().fold(f64::INFINITY, f64::min);
724        let skew = max_delay - min_delay;
725        let total_wl = total_wirelength(&self.tree, root);
726        #[allow(clippy::incompatible_msrv)]
727        let delay_model = std::any::type_name_of_val(&*self.delay_calculator).to_string();
728
729        SkewAnalysis {
730            max_delay,
731            min_delay,
732            skew,
733            sink_delays,
734            total_wirelength: total_wl,
735            delay_model,
736        }
737    }
738}
739
740// ---------------------------------------------------------------------------
741// Free helper functions (work with &Tree + NodeIdx)
742// ---------------------------------------------------------------------------
743
744fn collect_sink_delays(tree: &Tree, node: NodeIdx, sink_delays: &mut Vec<f64>) {
745    if tree.get(node).is_leaf() {
746        sink_delays.push(tree.get(node).delay);
747    }
748    if let Some(l) = tree.get(node).left {
749        collect_sink_delays(tree, l, sink_delays);
750    }
751    if let Some(r) = tree.get(node).right {
752        collect_sink_delays(tree, r, sink_delays);
753    }
754}
755
756fn total_wirelength(tree: &Tree, node: NodeIdx) -> i32 {
757    let mut total = tree.get(node).wire_length;
758    if let Some(l) = tree.get(node).left {
759        total += total_wirelength(tree, l);
760    }
761    if let Some(r) = tree.get(node).right {
762        total += total_wirelength(tree, r);
763    }
764    total
765}
766
767/// Extracts detailed statistics from a clock tree.
768pub fn get_tree_statistics(tree: &Tree, root: NodeIdx) -> TreeStatistics {
769    let mut stats = TreeStatistics {
770        nodes: Vec::new(),
771        wires: Vec::new(),
772        sinks: Vec::new(),
773        total_nodes: 0,
774        total_sinks: 0,
775        total_wires: 0,
776    };
777    traverse_tree(tree, root, None, &mut stats);
778    stats.total_nodes = stats.nodes.len() as i32;
779    stats.total_sinks = stats.sinks.len() as i32;
780    stats.total_wires = stats.wires.len() as i32;
781    stats
782}
783
784fn traverse_tree(tree: &Tree, node: NodeIdx, parent: Option<NodeIdx>, stats: &mut TreeStatistics) {
785    let n = tree.get(node);
786    stats.nodes.push(NodeInfo {
787        name: n.name.clone(),
788        position: (n.position.xcoord, n.position.ycoord),
789        node_type: if n.is_leaf() {
790            "sink".to_string()
791        } else {
792            "internal".to_string()
793        },
794        delay: n.delay,
795        capacitance: n.capacitance,
796    });
797
798    if n.is_leaf() {
799        stats.sinks.push(n.name.clone());
800    }
801
802    if let Some(p) = parent {
803        let pb = tree.get(p);
804        stats.wires.push(WireInfo {
805            from_node: pb.name.clone(),
806            to_node: n.name.clone(),
807            length: n.wire_length,
808            from_pos: (pb.position.xcoord, pb.position.ycoord),
809            to_pos: (n.position.xcoord, n.position.ycoord),
810        });
811    }
812
813    let left = n.left;
814    let right = n.right;
815    let _ = n;
816
817    if let Some(l) = left {
818        traverse_tree(tree, l, Some(node), stats);
819    }
820    if let Some(r) = right {
821        traverse_tree(tree, r, Some(node), stats);
822    }
823}
824
825// ---------------------------------------------------------------------------
826// Tests
827// ---------------------------------------------------------------------------
828
829#[cfg(test)]
830mod tests {
831    use super::*;
832
833    fn make_sinks(count: i32) -> Vec<Sink> {
834        (0..count)
835            .map(|i| {
836                let x = (i * 37) % 100;
837                let y = (i * 53) % 100;
838                Sink::new(
839                    &format!("s{}", i),
840                    Point::new(x, y),
841                    1.0 + (i % 5) as f64 * 0.2,
842                )
843            })
844            .collect()
845    }
846
847    fn run_tree(sinks: Vec<Sink>, calc: Box<dyn DelayCalculator>) -> (DMEAlgorithm, SkewAnalysis) {
848        let mut dme = DMEAlgorithm::new(sinks, calc);
849        let root = dme.build_clock_tree();
850        let analysis = dme.analyze_skew(root);
851        (dme, analysis)
852    }
853
854    #[test]
855    fn test_dme_skew_within_two_percent_linear() {
856        let sinks = make_sinks(8);
857        let calc = Box::new(LinearDelayCalculator::new(0.5, 0.1));
858        let (_, analysis) = run_tree(sinks, calc);
859        assert!(analysis.max_delay > 0.0);
860        let pct = analysis.skew / analysis.max_delay * 100.0;
861        assert!(
862            pct < 2.0,
863            "Skew {:.4} ({:.2}%) exceeds 2%",
864            analysis.skew,
865            pct
866        );
867    }
868
869    #[test]
870    fn test_dme_skew_within_two_percent_elmore() {
871        let sinks = make_sinks(8);
872        let calc = Box::new(ElmoreDelayCalculator::new(0.1, 0.1));
873        let (_, analysis) = run_tree(sinks, calc);
874        assert!(analysis.max_delay > 0.0);
875        let pct = analysis.skew / analysis.max_delay * 100.0;
876        assert!(
877            pct < 2.0,
878            "Skew {:.4} ({:.2}%) exceeds 2%",
879            analysis.skew,
880            pct
881        );
882    }
883
884    #[test]
885    fn test_dme_two_sinks_zero_skew() {
886        let sinks = vec![
887            Sink::new("s1", Point::new(0, 0), 1.0),
888            Sink::new("s2", Point::new(10, 0), 1.0),
889        ];
890        let (_, analysis) = run_tree(sinks, Box::new(LinearDelayCalculator::new(1.0, 0.1)));
891        assert_eq!(
892            analysis.skew, 0.0,
893            "Two symmetric sinks should have zero skew"
894        );
895        assert_eq!(analysis.total_wirelength, 10);
896    }
897
898    #[test]
899    fn test_dme_single_sink() {
900        let sinks = vec![Sink::new("s1", Point::new(5, 5), 1.0)];
901        let (dme, analysis) = run_tree(sinks, Box::new(LinearDelayCalculator::new(0.5, 0.1)));
902        assert!(dme.get_tree().get(dme.get_tree().root.unwrap()).is_leaf());
903        assert_eq!(analysis.skew, 0.0);
904        assert_eq!(analysis.sink_delays.len(), 1);
905    }
906
907    #[test]
908    fn test_dme_with_source() {
909        let sinks = vec![
910            Sink::new("s1", Point::new(-10, -10), 1.0),
911            Sink::new("s2", Point::new(10, -10), 1.0),
912            Sink::new("s3", Point::new(-10, 10), 1.0),
913            Sink::new("s4", Point::new(10, 10), 1.0),
914        ];
915        let calc = Box::new(LinearDelayCalculator::new(0.5, 0.1));
916        let mut dme = DMEAlgorithm::with_source(sinks, calc, Point::new(0, 0));
917        let root = dme.build_clock_tree();
918        let analysis = dme.analyze_skew(root);
919        assert!(analysis.max_delay > 0.0);
920        let pct = analysis.skew / analysis.max_delay * 100.0;
921        assert!(
922            pct < 2.0,
923            "Skew {:.4} ({:.2}%) exceeds 2%",
924            analysis.skew,
925            pct
926        );
927    }
928
929    #[test]
930    fn test_get_tree_statistics() {
931        let sinks = vec![
932            Sink::new("s1", Point::new(0, 0), 1.0),
933            Sink::new("s2", Point::new(10, 0), 1.0),
934        ];
935        let mut dme = DMEAlgorithm::new(sinks, Box::new(LinearDelayCalculator::new(1.0, 0.1)));
936        let root = dme.build_clock_tree();
937        let stats = get_tree_statistics(dme.get_tree(), root);
938        assert_eq!(stats.total_nodes, 3);
939        assert_eq!(stats.total_sinks, 2);
940        assert_eq!(stats.total_wires, 2);
941    }
942
943    // -----------------------------------------------------------------------
944    // Elongation unit tests (caller-side logic matching C++ TappingResult)
945    // -----------------------------------------------------------------------
946
947    /// Simulate the caller-side elongation logic from compute_merging_segment.
948    fn apply_elongation(distance: i32, tp: &TappingResult) -> (i32, i32, bool, bool) {
949        let mut left_wl = tp.extend_left;
950        let mut right_wl = distance - tp.raw_extend_left;
951        let mut left_el = false;
952        let mut right_el = false;
953        if tp.raw_extend_left < 0 {
954            left_wl = 0;
955            right_wl = distance - tp.raw_extend_left;
956            right_el = true;
957        } else if tp.raw_extend_left > distance {
958            right_wl = 0;
959            left_wl = tp.raw_extend_left;
960            left_el = true;
961        }
962        (left_wl, right_wl, left_el, right_el)
963    }
964
965    #[test]
966    fn test_linear_tapping_point_balanced() {
967        let calc = LinearDelayCalculator::new(0.5, 0.2);
968        let tp = calc.calculate_tapping_point(10, 1.0, 1.0, 0.0, 0.0);
969        assert_eq!(tp.extend_left, 5);
970        assert_eq!(tp.raw_extend_left, 5);
971        approx_eq(tp.delay_left, 1.0 + 5.0 * 0.5);
972    }
973
974    #[test]
975    fn test_linear_tapping_point_right_slower() {
976        let calc = LinearDelayCalculator::new(0.5, 0.2);
977        let tp = calc.calculate_tapping_point(10, 1.0, 3.0, 0.0, 0.0);
978        assert_eq!(tp.extend_left, 7);
979        approx_eq(tp.delay_left, 1.0 + 7.0 * 0.5);
980    }
981
982    #[test]
983    fn test_linear_tapping_point_left_slower() {
984        let calc = LinearDelayCalculator::new(0.5, 0.2);
985        let tp = calc.calculate_tapping_point(10, 3.0, 1.0, 0.0, 0.0);
986        assert_eq!(tp.extend_left, 3);
987        approx_eq(tp.delay_left, 3.0 + 3.0 * 0.5);
988    }
989
990    #[test]
991    fn test_linear_elongation_right_branch() {
992        // left.delay=10, right.delay=1, distance=10
993        // raw = -4, clamped to 0
994        let calc = LinearDelayCalculator::new(0.5, 0.2);
995        let tp = calc.calculate_tapping_point(10, 10.0, 1.0, 0.0, 0.0);
996        assert_eq!(tp.extend_left, 0);
997        assert_eq!(tp.raw_extend_left, -4);
998        approx_eq(tp.delay_left, 10.0);
999
1000        let (l_wl, r_wl, l_el, r_el) = apply_elongation(10, &tp);
1001        assert_eq!(l_wl, 0);
1002        assert_eq!(r_wl, 14); // distance - raw = 10 - (-4) = 14
1003        assert!(!l_el);
1004        assert!(r_el);
1005    }
1006
1007    #[test]
1008    fn test_linear_elongation_left_branch() {
1009        // left.delay=1, right.delay=10, distance=10
1010        // raw = 14, clamped to 10
1011        let calc = LinearDelayCalculator::new(0.5, 0.2);
1012        let tp = calc.calculate_tapping_point(10, 1.0, 10.0, 0.0, 0.0);
1013        assert_eq!(tp.extend_left, 10);
1014        assert_eq!(tp.raw_extend_left, 14);
1015        approx_eq(tp.delay_left, 10.0);
1016
1017        let (l_wl, r_wl, l_el, r_el) = apply_elongation(10, &tp);
1018        assert_eq!(l_wl, 14); // raw = 14
1019        assert_eq!(r_wl, 0);
1020        assert!(l_el);
1021        assert!(!r_el);
1022    }
1023
1024    #[test]
1025    fn test_elmore_elongation_right_branch() {
1026        let calc = ElmoreDelayCalculator::new(0.1, 0.2);
1027        let tp = calc.calculate_tapping_point(10, 10.0, 1.0, 1.0, 1.0);
1028        assert_eq!(tp.extend_left, 0);
1029        assert_eq!(tp.raw_extend_left, -18);
1030        approx_eq(tp.delay_left, 10.0);
1031
1032        let (l_wl, r_wl, l_el, r_el) = apply_elongation(10, &tp);
1033        assert_eq!(l_wl, 0);
1034        assert_eq!(r_wl, 28); // distance - raw = 10 - (-18) = 28
1035        assert!(!l_el);
1036        assert!(r_el);
1037    }
1038
1039    #[test]
1040    fn test_elmore_elongation_left_branch() {
1041        let calc = ElmoreDelayCalculator::new(0.1, 0.2);
1042        let tp = calc.calculate_tapping_point(10, 1.0, 10.0, 1.0, 1.0);
1043        assert_eq!(tp.extend_left, 10);
1044        assert_eq!(tp.raw_extend_left, 28);
1045        approx_eq(tp.delay_left, 10.0);
1046
1047        let (l_wl, r_wl, l_el, r_el) = apply_elongation(10, &tp);
1048        assert_eq!(l_wl, 28); // raw = 28
1049        assert_eq!(r_wl, 0);
1050        assert!(l_el);
1051        assert!(!r_el);
1052    }
1053
1054    /// Helper: approximate float equality within 1e-9.
1055    fn approx_eq(a: f64, b: f64) {
1056        assert!((a - b).abs() < 1e-9, "left={}, right={}", a, b);
1057    }
1058}