= HashMap::new();
// gap.get(v).copied().unwrap_or(weight[v]) β fallback
// gap.entry(v.clone()).or_insert(weight[v]) β lazy insert
```
**5 files fixed:** `netlist_algo.rs`, `graph_algo.rs`, `graph_cover.rs`, `cover.rs`
**Savings:** One full HashMap clone per algorithm run. For 100K entries: ~several MB per call.
---
### πΈοΈ netlistx-rs β Incremental Cache
```rust
// BEFORE: scans ALL modules + nets on every add_edge() β O(N)
fn invalidate_cache(&mut self) {
self.max_degree = self.modules.iter()
.map(|m| self.get_module_degree(m) as u32).max()...; // O(N)
self.max_net_degree = self.nets.iter()
.map(|n| self.get_net_degree(n) as u32).max()...; // O(N)
}
// AFTER: updates only the affected nodes β O(1)
fn invalidate_cache(&mut self, module: &str, net: &str) {
let mod_deg = self.get_module_degree(module) as u32;
let net_deg = self.get_net_degree(net) as u32;
if mod_deg > self.max_degree { self.max_degree = mod_deg; }
if net_deg > self.max_net_degree { self.max_net_degree = net_deg; }
}
```
**Savings:** O(N) scan per edge addition β O(1).
---
### πΈοΈ netlistx-rs β Results
.mermaid[
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#88C0D0', 'secondaryColor': '#BF616A'}}}%%
graph TD
A["Netlist\noperations"] --> B["Module indices\nIndexMapβVec"]
A --> C["Weight clones\nclone β fallback"]
A --> D["Cache\nO(N) β O(1)"]
A --> E["BFS maps\nper-call β reusable"]
A --> F["Face dedup\nO(FΒ²) β O(F)"]
style B fill:#A3BE8C,color:#fff
style C fill:#A3BE8C,color:#fff
style D fill:#A3BE8C,color:#fff
style E fill:#A3BE8C,color:#fff
style F fill:#A3BE8C,color:#fff
]
---
> **βοΈ Tradeoff:** Replacing `IndexMap` with `Vec` backed by `IndexSet::get_index_of()` changes lookup from a single hash-table operation to **two operations**: `get_index_of()` (hash lookup in the IndexSet) + `Vec::index` (bounds-checked array access). The IndexMap was a single hash lookup. In practice, the Vec index is cheaper than the IndexMap's value-storage overhead, so the two operations still win. However, insertion order metadata is lost β `IndexMap` preserves insertion order for iteration, while `IndexSet::iter()` returns elements in insertion order but the parallel Vec just stores NodeIndices.
> The empty-HashMap-with-fallback pattern in weight-clone elimination adds a `.clone()` on every `entry().or_insert()` call for keys not yet in the map. For algorithms that modify most entries (e.g., dense graph algorithms), this shifts O(N) HashMap clones from upfront to per-access β same total cost, different timing. The savings are largest for **sparse** modification patterns where only a fraction of weight-map entries are touched.
---
### π§ physdes-rs β DME Clock Tree Algorithm
**Domain:** VLSI physical design β zero-skew clock tree construction.
**The Problem:** `build_merging_tree` created a sorted copy of node indices at **every recursion level**.
```rust
// BEFORE: O(n log n) sort + Vec allocation per level
fn build_merging_tree(&mut self, node_ids: &[NodeIdx], ...) -> NodeIdx {
let mut sorted: Vec = node_ids.to_vec(); // alloc
sorted.sort_by(...); // O(n log n)
let mid = sorted.len() / 2;
let left = self.build_merging_tree(&sorted[..mid], ...);
let right = self.build_merging_tree(&sorted[mid..], ...);
// AFTER: O(n) median partitioning β no copy
fn build_merging_tree(&mut self, node_ids: &mut [NodeIdx], ...) -> NodeIdx {
let mid = node_ids.len() / 2;
node_ids.select_nth_unstable_by(mid, ...); // O(n), in-place
let (left, right) = node_ids.split_at_mut(mid);
```
---
### π§ physdes-rs β More Optimizations
**2. Merging segments: HashMap β Vec\