Skip to main content

ravenrs_sheet/calc/recalc/
graph.rs

1use super::RangeRef;
2use crate::base::CellRef;
3use crate::calc::eval::Area;
4use std::collections::BTreeSet;
5use std::hash::{BuildHasherDefault, Hasher};
6
7/// A fast, non-cryptographic hasher for cell keys (FxHash-style multiply-rotate).
8#[derive(Default, Clone, Copy)]
9pub struct FxHasher(u64);
10
11impl Hasher for FxHasher {
12    fn finish(&self) -> u64 {
13        self.0
14    }
15    fn write(&mut self, bytes: &[u8]) {
16        for b in bytes {
17            self.write_u64(*b as u64);
18        }
19    }
20    fn write_u64(&mut self, i: u64) {
21        self.0 = (self.0.rotate_left(5) ^ i).wrapping_mul(0x51_7c_c1_b7_27_22_0a_95);
22    }
23    fn write_u32(&mut self, i: u32) {
24        self.write_u64(i as u64);
25    }
26    fn write_usize(&mut self, i: usize) {
27        self.write_u64(i as u64);
28    }
29}
30
31pub(super) type HashMap<K, V> = std::collections::HashMap<K, V, BuildHasherDefault<FxHasher>>;
32
33pub(super) type HashSet<K> = std::collections::HashSet<K, BuildHasherDefault<FxHasher>>;
34
35pub type Key = (usize, CellRef);
36
37#[derive(Clone, Debug, Default)]
38pub(super) struct Node {
39    pub(super) areas: Vec<Area>,
40    pub(super) dynamic: bool,
41}
42
43/// Ranges wider than this many columns go to the per-sheet wide index instead of per-column ones.
44const WIDE: u32 = 64;
45
46/// Insertions an [`Intervals`] keeps aside (scanned by every query) before merging them in.
47const PENDING: usize = 32;
48
49/// Row intervals `[r0, r1]`, each with a payload, answering "which intervals overlap rows
50/// `a..=b`?" in O(log n + k).
51///
52/// The intervals are sorted by start with an implicit segment tree of their largest end, so a query
53/// only descends into subtrees that hold a match. Removals mark entries dead (re-inserting one
54/// revives it); insertions wait in a small sorted set. [`Intervals::settle`] merges them and drops
55/// dead entries once either grows, so mutations cost O(log n) amortised.
56#[derive(Clone, Debug)]
57pub(super) struct Intervals<T> {
58    /// Sorted, without duplicates.
59    items: Vec<(u32, u32, T)>,
60    dead: Vec<bool>,
61    dead_count: usize,
62    /// Segment tree over `items`: node `i` has children `2i` and `2i + 1`, leaves start at
63    /// `max_end.len() / 2`. Each node holds the largest end below it (dead entries included).
64    max_end: Vec<u32>,
65    /// Inserted since the last merge.
66    pending: BTreeSet<(u32, u32, T)>,
67}
68
69impl<T> Default for Intervals<T> {
70    fn default() -> Self {
71        Intervals { items: Vec::new(), dead: Vec::new(), dead_count: 0, max_end: Vec::new(), pending: BTreeSet::new() }
72    }
73}
74
75impl<T: Copy + Ord> Intervals<T> {
76    pub(super) fn is_empty(&self) -> bool {
77        self.items.len() == self.dead_count && self.pending.is_empty()
78    }
79
80    pub(super) fn insert(&mut self, r0: u32, r1: u32, t: T) {
81        let it = (r0, r1, t);
82        if let Ok(i) = self.items.binary_search(&it) {
83            if let Some(d) = self.dead.get_mut(i)
84                && *d
85            {
86                *d = false;
87                self.dead_count -= 1;
88            }
89            return;
90        }
91        self.pending.insert(it);
92    }
93
94    pub(super) fn remove(&mut self, r0: u32, r1: u32, t: T) {
95        let it = (r0, r1, t);
96        if let Ok(i) = self.items.binary_search(&it) {
97            if let Some(d) = self.dead.get_mut(i)
98                && !*d
99            {
100                *d = true;
101                self.dead_count += 1;
102            }
103            return;
104        }
105        self.pending.remove(&it);
106    }
107
108    /// Merges pending insertions and drops dead entries when there are enough of them.
109    pub(super) fn settle(&mut self) {
110        if self.pending.len() <= PENDING && self.dead_count <= (self.items.len() / 2).max(PENDING) {
111            return;
112        }
113        let mut all: Vec<(u32, u32, T)> = Vec::with_capacity(self.items.len() - self.dead_count + self.pending.len());
114        all.extend(self.items.iter().zip(&self.dead).filter(|(_, d)| !**d).map(|(it, _)| *it));
115        all.extend(std::mem::take(&mut self.pending));
116        // Two sorted runs: the stable sort merges them in linear time.
117        all.sort();
118        all.dedup();
119        let size = all.len().next_power_of_two();
120        let mut max_end = vec![0; 2 * size];
121        if let Some(leaves) = max_end.get_mut(size..) {
122            for (slot, it) in leaves.iter_mut().zip(&all) {
123                *slot = it.1;
124            }
125        }
126        for i in (1..size).rev() {
127            let m = max_end.get(2 * i).copied().unwrap_or(0).max(max_end.get(2 * i + 1).copied().unwrap_or(0));
128            if let Some(slot) = max_end.get_mut(i) {
129                *slot = m;
130            }
131        }
132        self.dead = vec![false; all.len()];
133        self.dead_count = 0;
134        self.items = all;
135        self.max_end = max_end;
136    }
137
138    /// Calls `f` for every interval overlapping rows `a..=b` (`r0 <= b && r1 >= a`). Returns the
139    /// number of tree nodes visited.
140    pub(super) fn query(&self, a: u32, b: u32, f: &mut dyn FnMut(&T)) -> usize {
141        for it in &self.pending {
142            if it.0 <= b && it.1 >= a {
143                f(&it.2);
144            }
145        }
146        // Entries before `p` start at or before row `b`.
147        let p = self.items.partition_point(|it| it.0 <= b);
148        if p == 0 {
149            return 0;
150        }
151        self.walk(1, 0, self.max_end.len() / 2, p, a, f)
152    }
153
154    /// [`Intervals::query`] that skips entries an earlier call with the same `taken` already
155    /// took (and whole subtrees of them), so that a series of queries lists each interval once in
156    /// all. `f` answers whether it took the entry; one it declines is offered again later.
157    /// `taken` belongs to this index as it is now; start it empty. Pending insertions (a few at
158    /// most after [`Intervals::settle`]) are offered every time.
159    pub(super) fn query_untaken(&self, a: u32, b: u32, taken: &mut Vec<bool>, f: &mut dyn FnMut(&T) -> bool) {
160        for it in &self.pending {
161            if it.0 <= b && it.1 >= a {
162                f(&it.2);
163            }
164        }
165        let p = self.items.partition_point(|it| it.0 <= b);
166        if p == 0 {
167            return;
168        }
169        if taken.len() != self.max_end.len() {
170            *taken = vec![false; self.max_end.len()];
171        }
172        self.walk_untaken(1, 0, self.max_end.len() / 2, p, a, taken, f);
173    }
174
175    /// Returns whether everything under `node` has now been reported (or is dead).
176    #[allow(clippy::too_many_arguments)]
177    fn walk_untaken(&self, node: usize, lo: usize, hi: usize, p: usize, a: u32, taken: &mut [bool], f: &mut dyn FnMut(&T) -> bool) -> bool {
178        if taken.get(node).copied().unwrap_or(true) {
179            return true;
180        }
181        if lo >= p || self.max_end.get(node).is_none_or(|m| *m < a) {
182            return false;
183        }
184        let done = if hi - lo <= 1 {
185            match (self.items.get(lo), self.dead.get(lo)) {
186                (Some(it), Some(false)) if it.1 >= a => f(&it.2),
187                (Some(_), Some(true)) => true,
188                _ => false,
189            }
190        } else {
191            let mid = lo + (hi - lo) / 2;
192            let left = self.walk_untaken(2 * node, lo, mid, p, a, taken, f);
193            let right = self.walk_untaken(2 * node + 1, mid, hi, p, a, taken, f);
194            left && right
195        };
196        if done && let Some(slot) = taken.get_mut(node) {
197            *slot = true;
198        }
199        done
200    }
201
202    fn walk(&self, node: usize, lo: usize, hi: usize, p: usize, a: u32, f: &mut dyn FnMut(&T)) -> usize {
203        if lo >= p || self.max_end.get(node).is_none_or(|m| *m < a) {
204            return 1;
205        }
206        if hi - lo <= 1 {
207            if let (Some(it), Some(false)) = (self.items.get(lo), self.dead.get(lo))
208                && it.1 >= a
209            {
210                f(&it.2);
211            }
212            return 1;
213        }
214        let mid = lo + (hi - lo) / 2;
215        1 + self.walk(2 * node, lo, mid, p, a, f) + self.walk(2 * node + 1, mid, hi, p, a, f)
216    }
217}
218
219/// Range-index entries one dependency walk has already listed (see [`Graph::dependents_untaken`]).
220#[derive(Default)]
221pub(super) struct Taken {
222    cols: HashMap<(usize, u32), Vec<bool>>,
223    wide: HashMap<usize, Vec<bool>>,
224}
225
226#[derive(Clone, Debug, Default)]
227pub struct Graph {
228    pub(super) nodes: HashMap<Key, Node>,
229    /// single cell → dependents
230    cell_deps: HashMap<Key, HashSet<Key>>,
231    /// (sheet, col) → row intervals of the ranges (up to `WIDE` columns) that cover the column
232    col_deps: HashMap<(usize, u32), Intervals<Key>>,
233    /// sheet → row intervals of wider ranges, with their columns: (first, last, dependent)
234    wide_deps: HashMap<usize, Intervals<(u32, u32, Key)>>,
235    pub(super) dynamic: HashSet<Key>,
236    /// Range indexes changed since the last [`Graph::settle`].
237    unsettled_cols: HashSet<(usize, u32)>,
238    unsettled_wide: HashSet<usize>,
239}
240
241/// A formula's precedents split by shape: single cells, column intervals and wide ranges.
242type Entries = (Vec<Key>, Vec<(usize, u32, u32, u32)>, Vec<(usize, RangeRef)>);
243
244impl Graph {
245    pub fn len(&self) -> usize {
246        self.nodes.len()
247    }
248    pub fn is_empty(&self) -> bool {
249        self.nodes.is_empty()
250    }
251
252    /// Index entries of a formula's precedents, without duplicates: single cells, column
253    /// intervals and wide ranges.
254    fn entries(areas: &[Area]) -> Entries {
255        let (mut cells, mut cols, mut wide) = (Vec::new(), Vec::new(), Vec::new());
256        for a in areas {
257            if a.range.is_single() {
258                cells.push((a.sheet, a.range.start));
259            } else if a.range.width() > WIDE {
260                wide.push((a.sheet, a.range));
261            } else {
262                for c in a.range.start.col..=a.range.end.col {
263                    cols.push((a.sheet, c, a.range.start.row, a.range.end.row));
264                }
265            }
266        }
267        cells.sort_unstable();
268        cells.dedup();
269        cols.sort_unstable();
270        cols.dedup();
271        wide.sort_unstable_by_key(|(s, r)| (*s, r.start, r.end));
272        wide.dedup();
273        (cells, cols, wide)
274    }
275
276    pub(super) fn insert(&mut self, k: Key, node: Node) {
277        self.remove(k);
278        let (cells, cols, wide) = Self::entries(&node.areas);
279        for c in cells {
280            self.cell_deps.entry(c).or_default().insert(k);
281        }
282        for (s, c, r0, r1) in cols {
283            self.col_deps.entry((s, c)).or_default().insert(r0, r1, k);
284            self.unsettled_cols.insert((s, c));
285        }
286        for (s, r) in wide {
287            self.wide_deps.entry(s).or_default().insert(r.start.row, r.end.row, (r.start.col, r.end.col, k));
288            self.unsettled_wide.insert(s);
289        }
290        if node.dynamic {
291            self.dynamic.insert(k);
292        }
293        self.nodes.insert(k, node);
294    }
295
296    pub(super) fn remove(&mut self, k: Key) {
297        let Some(old) = self.nodes.remove(&k) else { return };
298        let (cells, cols, wide) = Self::entries(&old.areas);
299        for c in cells {
300            if let Some(v) = self.cell_deps.get_mut(&c) {
301                v.remove(&k);
302                if v.is_empty() {
303                    self.cell_deps.remove(&c);
304                }
305            }
306        }
307        for (s, c, r0, r1) in cols {
308            if let Some(v) = self.col_deps.get_mut(&(s, c)) {
309                v.remove(r0, r1, k);
310                self.unsettled_cols.insert((s, c));
311            }
312        }
313        for (s, r) in wide {
314            if let Some(v) = self.wide_deps.get_mut(&s) {
315                v.remove(r.start.row, r.end.row, (r.start.col, r.end.col, k));
316                self.unsettled_wide.insert(s);
317            }
318        }
319        self.dynamic.remove(&k);
320    }
321
322    /// Tidies the range indexes changed by `insert`/`remove` (queries stay correct without it,
323    /// just slower).
324    pub(super) fn settle(&mut self) {
325        for key in std::mem::take(&mut self.unsettled_cols) {
326            if let Some(v) = self.col_deps.get_mut(&key) {
327                v.settle();
328                if v.is_empty() {
329                    self.col_deps.remove(&key);
330                }
331            }
332        }
333        for key in std::mem::take(&mut self.unsettled_wide) {
334            if let Some(v) = self.wide_deps.get_mut(&key) {
335                v.settle();
336                if v.is_empty() {
337                    self.wide_deps.remove(&key);
338                }
339            }
340        }
341    }
342
343    /// Formulas that read cell `c` of `sheet`.
344    pub fn dependents(&self, sheet: usize, c: CellRef, out: &mut Vec<Key>) {
345        if let Some(v) = self.cell_deps.get(&(sheet, c)) {
346            out.extend(v.iter().copied());
347        }
348        if let Some(v) = self.col_deps.get(&(sheet, c.col)) {
349            v.query(c.row, c.row, &mut |k| out.push(*k));
350        }
351        if let Some(v) = self.wide_deps.get(&sheet) {
352            v.query(c.row, c.row, &mut |(c0, c1, k)| {
353                if *c0 <= c.col && c.col <= *c1 {
354                    out.push(*k);
355                }
356            });
357        }
358    }
359
360    /// [`Graph::dependents`] leaving out the formulas whose range reads an earlier call with the
361    /// same `taken` already listed. A walk over many changed cells that a few broad ranges cover
362    /// then lists those formulas once, instead of once per cell (which squared the work). The
363    /// graph must not change while `taken` is in use.
364    pub(super) fn dependents_untaken(&self, sheet: usize, c: CellRef, taken: &mut Taken, out: &mut Vec<Key>) {
365        if let Some(v) = self.cell_deps.get(&(sheet, c)) {
366            out.extend(v.iter().copied());
367        }
368        if let Some(v) = self.col_deps.get(&(sheet, c.col)) {
369            v.query_untaken(c.row, c.row, taken.cols.entry((sheet, c.col)).or_default(), &mut |k| {
370                out.push(*k);
371                true
372            });
373        }
374        if let Some(v) = self.wide_deps.get(&sheet) {
375            // A wide entry is taken once it covers the cell's column too.
376            v.query_untaken(c.row, c.row, taken.wide.entry(sheet).or_default(), &mut |(c0, c1, k)| {
377                let hit = *c0 <= c.col && c.col <= *c1;
378                if hit {
379                    out.push(*k);
380                }
381                hit
382            });
383        }
384    }
385
386    /// Formulas that read any cell of `range` (a formula can be listed more than once).
387    pub fn dependents_of_range(&self, sheet: usize, range: RangeRef, out: &mut Vec<Key>) {
388        if range.count() <= 4096 {
389            for c in range.iter() {
390                if let Some(v) = self.cell_deps.get(&(sheet, c)) {
391                    out.extend(v.iter().copied());
392                }
393            }
394        } else {
395            for ((s, c), v) in &self.cell_deps {
396                if *s == sheet && range.contains(*c) {
397                    out.extend(v.iter().copied());
398                }
399            }
400        }
401        let (r0, r1) = (range.start.row, range.end.row);
402        if (range.width() as usize) <= self.col_deps.len() {
403            for col in range.start.col..=range.end.col {
404                if let Some(v) = self.col_deps.get(&(sheet, col)) {
405                    v.query(r0, r1, &mut |k| out.push(*k));
406                }
407            }
408        } else {
409            for ((s, col), v) in &self.col_deps {
410                if *s == sheet && range.start.col <= *col && *col <= range.end.col {
411                    v.query(r0, r1, &mut |k| out.push(*k));
412                }
413            }
414        }
415        if let Some(v) = self.wide_deps.get(&sheet) {
416            v.query(r0, r1, &mut |(c0, c1, k)| {
417                if *c0 <= range.end.col && range.start.col <= *c1 {
418                    out.push(*k);
419                }
420            });
421        }
422    }
423
424    /// Precedent areas of a formula cell (for Trace Precedents).
425    pub fn precedents_of(&self, k: Key) -> Vec<Area> {
426        self.nodes.get(&k).map(|n| n.areas.clone()).unwrap_or_default()
427    }
428}