1use std::collections::VecDeque;
14
15use crate::base::{Array, CellError, CellRef, RangeRef, Value};
16use crate::formula::Expr;
17use crate::model::{CalcMode, Workbook};
18
19use crate::calc::eval::{Evaluator, Host, precedents, stack_position};
20
21mod graph;
22mod pass_host;
23
24#[cfg(test)]
25use graph::Intervals;
26pub use graph::{FxHasher, Graph, Key};
27use graph::{HashMap, HashSet, Node, Taken};
28use pass_host::{PassHost, spills_as_before};
29
30#[derive(Clone, Debug)]
32pub struct Calc {
33 pub graph: Graph,
34 rng: u64,
35 pub fixed_now: Option<f64>,
37 pub circular: Vec<Key>,
39 pub last_recalc_cells: usize,
40}
41
42impl Default for Calc {
43 fn default() -> Self {
44 Calc { graph: Graph::default(), rng: 0x9E37_79B9_7F4A_7C15, fixed_now: None, circular: vec![], last_recalc_cells: 0 }
45 }
46}
47
48fn pop_component(stack: &mut Vec<usize>, on_stack: &mut [bool], comp: &mut [usize], comp_index: usize, root: usize) {
51 while let Some(w) = stack.pop() {
52 on_stack[w] = false;
53 comp[w] = comp_index;
54 if w == root {
55 break;
56 }
57 }
58}
59
60fn components(edges: &[Vec<usize>]) -> (Vec<usize>, Vec<usize>) {
63 let n = edges.len();
64 let (mut low, mut num) = (vec![0usize; n], vec![usize::MAX; n]);
65 let (mut on_stack, mut stack, mut comp) = (vec![false; n], Vec::new(), vec![usize::MAX; n]);
66 let (mut next, mut comps) = (0usize, 0usize);
67 for root in 0..n {
68 if num[root] != usize::MAX {
69 continue;
70 }
71 let mut call: Vec<(usize, usize)> = vec![(root, 0)];
72 num[root] = next;
73 low[root] = next;
74 next += 1;
75 stack.push(root);
76 on_stack[root] = true;
77 while let Some(top) = call.last_mut() {
78 let v = top.0;
79 if let Some(&w) = edges.get(v).and_then(|e| e.get(top.1)) {
80 top.1 += 1;
81 if num[w] == usize::MAX {
82 num[w] = next;
83 low[w] = next;
84 next += 1;
85 stack.push(w);
86 on_stack[w] = true;
87 call.push((w, 0));
88 } else if on_stack[w] {
89 low[v] = low[v].min(num[w]);
90 }
91 continue;
92 }
93 call.pop();
94 if let Some(&(u, _)) = call.last() {
95 low[u] = low[u].min(low[v]);
96 }
97 if low[v] == num[v] {
98 pop_component(&mut stack, &mut on_stack, &mut comp, comps, v);
99 comps += 1;
100 }
101 }
102 }
103 let mut size = vec![0usize; comps];
104 for &c in &comp {
105 if let Some(s) = size.get_mut(c) {
106 *s += 1;
107 }
108 }
109 (comp, size)
110}
111
112impl Calc {
113 pub fn new() -> Self {
114 Self::default()
115 }
116
117 fn now(&self) -> f64 {
118 if let Some(n) = self.fixed_now {
119 return n;
120 }
121 now_serial()
122 }
123
124 pub fn rebuild(&mut self, wb: &Workbook) {
126 self.graph = Graph::default();
127 let mut circ = Vec::new();
128 for (si, sheet) in wb.sheets.iter().enumerate() {
129 for (c, cell) in sheet.cells.iter() {
130 if let Some(f) = &cell.formula
131 && let Some((areas, dynamic)) = f.with_expr(|e| precedents(wb, si, e))
132 {
133 self.graph.insert((si, c), Node { areas, dynamic });
134 if cell.value == Value::Error(CellError::Circ) {
135 circ.push((si, c));
136 }
137 } else if cell.py.is_some() {
138 self.graph.insert((si, c), Node { areas: Vec::new(), dynamic: false });
141 }
142 }
143 }
144 self.graph.settle();
145 self.circular = if wb.calc.iterative { Vec::new() } else { self.loops_among(&circ) };
146 }
147
148 fn loops_among(&self, circ: &[Key]) -> Vec<Key> {
152 if circ.is_empty() {
153 return Vec::new();
154 }
155 let n = circ.len();
156 let index: HashMap<Key, usize> = circ.iter().enumerate().map(|(i, k)| (*k, i)).collect();
157 let mut deps = Vec::new();
159 let edges: Vec<Vec<usize>> = circ
160 .iter()
161 .map(|k| {
162 deps.clear();
163 self.graph.dependents(k.0, k.1, &mut deps);
164 let mut e: Vec<usize> = deps.iter().filter_map(|d| index.get(d).copied()).collect();
165 e.sort_unstable();
166 e.dedup();
167 e
168 })
169 .collect();
170 let (comp, size) = components(&edges);
172 let on_loop = |i: usize| comp.get(i).and_then(|c| size.get(*c)).is_some_and(|s| *s > 1) || edges.get(i).is_some_and(|e| e.contains(&i));
173 let mut out: Vec<Key> =
175 self.circular.iter().copied().filter(|k| index.get(k).is_some_and(|&i| on_loop(i) || self.graph.dynamic.contains(k))).collect();
176 let mut covered: HashSet<usize> = out.iter().filter_map(|k| index.get(k)).filter_map(|&i| comp.get(i).copied()).collect();
177 let mut order: Vec<usize> = (0..n).collect();
178 order.sort_by_key(|&i| circ.get(i).map(|(s, c)| (*s, c.row, c.col)));
179 for i in order {
180 if let (Some(&c), Some(&k)) = (comp.get(i), circ.get(i))
181 && on_loop(i)
182 && covered.insert(c)
183 {
184 out.push(k);
185 }
186 }
187 out
188 }
189
190 pub fn recalc_all(&mut self, wb: &mut Workbook) {
192 self.rebuild(wb);
193 for i in 0..wb.sheets.len() {
196 if wb.sheet(i).is_some_and(|s| !s.spill_ranges.is_empty() || !s.spill.is_empty())
197 && let Some(sh) = wb.sheet_mut(i)
198 {
199 sh.spill.clear();
200 sh.spill_ranges.clear();
201 }
202 }
203 let all: Vec<Key> = self.graph.nodes.keys().copied().collect();
204 self.run(wb, all, true);
205 }
206
207 pub fn cells_changed(&mut self, wb: &mut Workbook, changed: &[Key]) {
209 let mut cleared: Vec<(usize, RangeRef)> = Vec::new();
210 for &k in changed {
211 let precedent =
212 wb.sheet(k.0).and_then(|s| s.cell(k.1)).and_then(|c| c.formula.as_ref()).and_then(|f| f.with_expr(|e| precedents(wb, k.0, e)));
213 let py = wb.sheet(k.0).and_then(|s| s.cell(k.1)).and_then(|c| c.py.as_ref());
214 let python = py.is_some();
215 let spills = py.and_then(|p| p.result_value()).is_some_and(|v| matches!(v, Value::Array(_)));
217 match precedent {
218 Some((areas, dynamic)) => {
219 self.graph.insert(k, Node { areas, dynamic });
220 }
221 None if python => {
223 self.graph.insert(k, Node { areas: Vec::new(), dynamic: false });
224 if !spills
228 && let Some(sh) = wb.sheet_mut(k.0)
229 && let Some(r) = sh.spill_ranges.remove(&k.1)
230 {
231 sh.clear_spill(r);
232 cleared.push((k.0, r));
233 }
234 }
235 None => {
236 self.graph.remove(k);
237 if let Some(sh) = wb.sheet_mut(k.0)
239 && let Some(r) = sh.spill_ranges.remove(&k.1)
240 {
241 for c in r.iter() {
242 sh.spill.remove(&c);
243 }
244 cleared.push((k.0, r));
245 }
246 if let Some(cell) = wb.sheet_mut(k.0).and_then(|s| s.cells.get_mut(k.1))
248 && cell.formula.is_some()
249 {
250 cell.value = Value::Error(CellError::Name);
251 }
252 }
253 }
254 }
255 self.graph.settle();
256 if wb.calc.mode == CalcMode::Manual {
257 let own: Vec<Key> = changed.iter().copied().filter(|k| self.graph.nodes.contains_key(k)).collect();
259 self.run(wb, own, false);
260 return;
261 }
262 let mut seeds: Vec<Key> = changed.to_vec();
263 let mut emptied: Vec<Key> = Vec::new();
264 for &k in changed {
267 if let Some(sh) = wb.sheet(k.0) {
268 for (anchor, r) in &sh.spill_ranges {
269 if *anchor != k.1 && r.contains(k.1) {
270 seeds.push((k.0, *anchor));
271 }
272 }
273 for (c, cell) in sh.cells.iter_range(RangeRef::new(CellRef::new(k.1.row.saturating_sub(64), k.1.col.saturating_sub(64)), k.1)) {
274 if c != k.1 && cell.value == Value::Error(CellError::Spill) {
275 seeds.push((k.0, c));
276 }
277 }
278 if !sh.cells.has(k.1) {
279 match emptied.iter_mut().find(|x| x.0 == k.0) {
280 Some(x) => x.1 = CellRef::new(x.1.row.max(k.1.row), x.1.col.max(k.1.col)),
281 None => emptied.push(k),
282 }
283 }
284 }
285 }
286 for (si, end) in emptied {
288 for &(s, c) in self.graph.nodes.keys() {
289 if s == si && c.row <= end.row && c.col <= end.col && wb.sheet(s).is_some_and(|sh| sh.value(c) == Value::Error(CellError::Spill)) {
290 seeds.push((s, c));
291 }
292 }
293 }
294 let mut areas: Vec<(usize, RangeRef)> = cleared;
297 for &k in changed {
298 if let Some(r) = wb.sheet(k.0).and_then(|s| s.spill_ranges.get(&k.1)) {
299 areas.push((k.0, *r));
300 }
301 }
302 let t0 = prof_now();
303 let dirty = self.dirty_closure(wb, &seeds, &areas);
304 let t1 = prof_now();
305 let n = dirty.len();
306 self.run(wb, dirty, false);
307 if std::env::var_os("GRIDCRAFT_PROFILE").is_some() {
308 eprintln!("recalc: {} seeds, closure {} cells {:.1} ms, run {:.1} ms", seeds.len(), n, t1 - t0, prof_now() - t1);
309 }
310 }
311
312 fn dirty_closure(&self, wb: &Workbook, seeds: &[Key], areas: &[(usize, RangeRef)]) -> Vec<Key> {
315 let mut seen: HashSet<Key> = HashSet::default();
316 let mut queue: VecDeque<Key> = VecDeque::new();
317 let mut taken = Taken::default();
321 let mut deps = Vec::new();
322 for k in seeds {
323 if self.graph.nodes.contains_key(k) && seen.insert(*k) {
324 queue.push_back(*k);
325 }
326 deps.clear();
327 self.graph.dependents_untaken(k.0, k.1, &mut taken, &mut deps);
328 for d in deps.drain(..) {
329 if seen.insert(d) {
330 queue.push_back(d);
331 }
332 }
333 }
334 for &(si, r) in areas {
335 if let Some(sh) = wb.sheet(si) {
336 for (c, _) in sh.cells.iter_range(r) {
337 if self.graph.nodes.contains_key(&(si, c)) && seen.insert((si, c)) {
338 queue.push_back((si, c));
339 }
340 }
341 }
342 deps.clear();
343 self.graph.dependents_of_range(si, r, &mut deps);
344 for d in deps.drain(..) {
345 if seen.insert(d) {
346 queue.push_back(d);
347 }
348 }
349 }
350 for k in &self.graph.dynamic {
351 if seen.insert(*k) {
352 queue.push_back(*k);
353 }
354 }
355 while let Some(k) = queue.pop_front() {
356 deps.clear();
357 self.graph.dependents_untaken(k.0, k.1, &mut taken, &mut deps);
358 for d in deps.drain(..) {
359 if seen.insert(d) {
360 queue.push_back(d);
361 }
362 }
363 }
364 seen.into_iter().collect()
365 }
366
367 fn run(&mut self, wb: &mut Workbook, mut dirty: Vec<Key>, full: bool) {
369 let mut dynamic_again = !self.graph.dynamic.is_empty();
370 self.last_recalc_cells = 0;
371 for _round in 0..8 {
372 if dirty.is_empty() {
373 break;
374 }
375 dirty.sort_by_key(|(s, c)| (*s, c.row, c.col));
377 let mut exprs: HashMap<Key, std::sync::Arc<Expr>> = HashMap::with_capacity_and_hasher(dirty.len(), Default::default());
378 for k in &dirty {
379 if let Some(e) = wb.sheet(k.0).and_then(|s| s.cell(k.1)).and_then(|c| c.formula.as_ref()).and_then(|f| f.expr_arc()) {
380 exprs.insert(*k, e);
381 }
382 }
383 let python: HashMap<Key, Value> = dirty
384 .iter()
385 .filter_map(|k| wb.sheet(k.0).and_then(|s| s.cell(k.1)).and_then(|c| c.py.as_ref()).and_then(|p| p.result_value()).map(|v| (*k, v)))
386 .collect();
387 let now = self.now();
388 let mut rng = self.rng;
389 let tp = prof_now();
390 let (results, spills, cycles) = {
391 let mut host = PassHost::new(wb, &exprs, &mut rng, now).with_python(python);
392 host.iterate = wb.calc.iterative;
393 for k in &dirty {
394 host.calculate(*k);
395 }
396 if host.iterate && !host.members.is_empty() {
397 let passes = host.iterate_loops(&dirty, wb.calc.max_iterations, wb.calc.max_change);
398 if std::env::var_os("GRIDCRAFT_PROFILE").is_some() {
399 eprintln!(" iterative calculation: {} passes over {} formulas", passes, host.tainted.len());
400 }
401 }
402 (host.results, host.spills, host.cycles)
403 };
404 self.rng = rng;
405 if std::env::var_os("GRIDCRAFT_PROFILE").is_some() {
406 eprintln!(" eval {} formulas {:.1} ms", results.len(), prof_now() - tp);
407 }
408 let tw = prof_now();
409 self.last_recalc_cells += results.len();
410 if !self.circular.is_empty() {
412 let evaluated: HashSet<Key> = dirty.iter().copied().collect();
413 self.circular.retain(|k| !evaluated.contains(k));
414 }
415 self.circular.extend(cycles);
416 let mut spill_changes = self.write_back(wb, &dirty, results, &spills);
417 if dynamic_again && !spills.is_empty() {
420 dynamic_again = false;
421 spill_changes.extend(self.graph.dynamic.iter().copied());
422 }
423 if full {
424 }
426 if std::env::var_os("GRIDCRAFT_PROFILE").is_some() {
427 eprintln!(" write-back {:.1} ms", prof_now() - tw);
428 }
429 spill_changes.sort_by_key(|(s, c)| (*s, c.row, c.col));
430 spill_changes.dedup();
431 if spill_changes.is_empty() {
432 break;
433 }
434 dirty = self.dirty_closure(wb, &spill_changes, &[]);
435 }
436 if wb.calc.iterative {
439 self.circular.clear();
440 } else {
441 self.circular.retain(|k| self.graph.nodes.contains_key(k));
442 }
443 }
444
445 fn write_back(
446 &mut self,
447 wb: &mut Workbook,
448 dirty: &[Key],
449 results: HashMap<Key, Value>,
450 spills: &HashMap<Key, std::sync::Arc<Array>>,
451 ) -> Vec<Key> {
452 let mut spill_changes = Vec::new();
453 let mut freed = Vec::new();
454 for (key, value) in results {
455 let Some(sheet) = wb.sheet_mut(key.0) else { continue };
456 if let Some(cell) = sheet.cells.get_mut(key.1)
457 && cell.value != value
458 {
459 cell.value = value;
460 }
461 }
462 for key in dirty {
463 let new = spills.get(key);
464 let Some(sheet) = wb.sheet_mut(key.0) else { continue };
465 let old = sheet.spill_ranges.get(&key.1).copied();
466 if old.is_none() && new.is_none() {
467 continue;
468 }
469 if let (Some(range), Some(array)) = (old, new)
471 && spills_as_before(sheet, key.1, range, array)
472 {
473 continue;
474 }
475 if let Some(range) = old {
476 for cell in range.iter() {
477 sheet.spill.remove(&cell);
478 }
479 sheet.spill_ranges.remove(&key.1);
480 }
481 let mut changed_range = old;
482 if let Some(array) = new {
483 let end = CellRef::new(key.1.row.saturating_add(array.rows as u32 - 1), key.1.col.saturating_add(array.cols as u32 - 1));
484 let range = RangeRef::new(key.1, end);
485 let blocked = spill_is_blocked(sheet, key.1, range, end);
486 if blocked {
487 if let Some(cell) = sheet.cells.get_mut(key.1) {
488 cell.value = Value::Error(CellError::Spill);
489 }
490 let mut blocked_dependents = Vec::new();
491 self.graph.dependents_of_range(key.0, range, &mut blocked_dependents);
492 spill_changes.extend(blocked_dependents.into_iter().filter(|dependent| dependent != key));
493 } else {
494 fill_spill(sheet, key.1, range, array);
495 changed_range = Some(changed_range.map_or(range, |previous| previous.union(&range)));
496 }
497 }
498 if let Some(range) = changed_range {
499 let mut dependents = Vec::new();
500 self.graph.dependents_of_range(key.0, range, &mut dependents);
501 let freed_old_range = match (old, sheet.spill_ranges.get(&key.1)) {
502 (Some(previous), Some(current)) => !(current.contains(previous.start) && current.contains(previous.end)),
503 (_, None) => true,
504 (None, Some(_)) => false,
505 };
506 spill_changes.extend(dependents.into_iter().filter(|dependent| dependent != key || freed_old_range));
507 }
508 if let Some(previous) = old
509 && !sheet.spill_ranges.get(&key.1).is_some_and(|current| current.contains(previous.start) && current.contains(previous.end))
510 {
511 freed.push((key.0, previous));
512 }
513 }
514 for (sheet_index, range) in freed {
515 for &(sheet, cell) in self.graph.nodes.keys() {
516 let was_blocked_cell = cell.row <= range.end.row
517 && cell.col <= range.end.col
518 && wb.sheet(sheet).is_some_and(|sheet| sheet.value(cell) == Value::Error(CellError::Spill));
519 if sheet == sheet_index && was_blocked_cell {
520 spill_changes.push((sheet, cell));
521 }
522 }
523 }
524 spill_changes
525 }
526}
527
528fn spill_is_blocked(sheet: &crate::model::Sheet, anchor: CellRef, range: RangeRef, end: CellRef) -> bool {
529 end.row >= crate::base::MAX_ROWS
530 || end.col >= crate::base::MAX_COLS
531 || range.iter().any(|cell| cell != anchor && (sheet.cells.has(cell) || sheet.spill.contains_key(&cell)))
532 || sheet.merges.iter().any(|merge| merge.intersects(&range))
533 || sheet.tables.iter().any(|table| table.range.contains(anchor))
534}
535
536fn fill_spill(sheet: &mut crate::model::Sheet, anchor: CellRef, range: RangeRef, array: &Array) {
537 for (index, cell) in range.iter().enumerate() {
538 if cell != anchor {
539 sheet.spill.insert(cell, array.data.get(index).cloned().unwrap_or_default());
540 }
541 }
542 sheet.spill_ranges.insert(anchor, range);
543}
544
545pub fn now_serial() -> f64 {
549 crate::base::date::unix_now_ms().map_or(46302.5, crate::base::date::serial_from_unix_ms)
550}
551
552pub fn evaluate(wb: &Workbook, sheet: usize, at: CellRef, formula: &str) -> Value {
555 let body = formula.strip_prefix('=').unwrap_or(formula);
556 let Ok(expr) = crate::formula::parse(body) else { return Value::Error(CellError::Name) };
557 evaluate_expr(wb, sheet, at, &expr)
558}
559
560pub fn evaluate_expr(wb: &Workbook, sheet: usize, at: CellRef, expr: &Expr) -> Value {
561 let exprs = HashMap::default();
562 let mut rng = 0x1234_5678_9ABC_DEF0u64;
563 let mut host = PassHost::new(wb, &exprs, &mut rng, now_serial());
564 let mut ev = Evaluator::new(&mut host, sheet, at);
565 ev.value(expr)
566}
567
568fn prof_now() -> f64 {
569 crate::base::date::unix_now_ms().unwrap_or(0.0)
570}
571
572#[cfg(test)]
573pub(crate) mod tests {
574 use std::time::Duration;
575
576 use crate::base::{CellRef, RangeRef, Value};
577 use crate::model::{Cell, Formula, Workbook};
578
579 use super::{Calc, HashMap, Host, Intervals, Key, PassHost};
580
581 fn rng(state: &mut u64) -> u64 {
583 *state ^= *state << 13;
584 *state ^= *state >> 7;
585 *state ^= *state << 17;
586 *state
587 }
588
589 fn found(iv: &Intervals<u32>, a: u32, b: u32) -> Vec<u32> {
590 let mut v = Vec::new();
591 iv.query(a, b, &mut |t| v.push(*t));
592 v.sort_unstable();
593 v
594 }
595
596 fn big_python_spill() -> (Workbook, HashMap<Key, Value>) {
599 let n = 1_000usize;
600 let data: Vec<Value> = (0..n * n).map(|i| Value::Number(i as f64)).collect();
601 let array = crate::base::Array::new(n, n, data.clone()).unwrap();
602 let mut wb = Workbook::new();
603 let sh = wb.sheet_mut(0).unwrap();
604 let anchor = CellRef::new(0, 0);
605 let py = crate::model::PyCode::new("big");
606 sh.set_cell(anchor, Cell { value: Value::Number(0.0), py: Some(std::sync::Arc::new(py)), ..Cell::default() });
607 let area = RangeRef::new(anchor, CellRef::new(n as u32 - 1, n as u32 - 1));
608 sh.spill_ranges.insert(anchor, area);
609 for (i, c) in area.iter().enumerate().skip(1) {
610 sh.spill.insert(c, data[i].clone());
611 }
612 let mut python = HashMap::default();
613 python.insert((0, anchor), Value::Array(std::sync::Arc::new(array)));
614 (wb, python)
615 }
616
617 #[test]
618 fn a_read_beside_a_large_spill_walks_none_of_it() {
619 let (wb, python) = big_python_spill();
620 let exprs = HashMap::default();
621 let mut rng = 1u64;
622 let mut host = PassHost::new(&wb, &exprs, &mut rng, 0.0).with_python(python);
623 assert_eq!(host.cell_value(0, CellRef::new(0, 0)), Value::Number(0.0));
624 let beside = RangeRef::parse("ALM1:ALM1000").unwrap();
626 host.spill_visits = 0;
627 assert!(host.range_values(0, beside).iter().all(Value::is_empty));
628 assert!(host.visit_range(0, beside, &mut |_| true), "a disjoint read takes the fast path");
629 assert_eq!(host.spill_visits, 0, "nothing of the spill is walked");
630 let inside = RangeRef::parse("B2:C3").unwrap();
632 host.spill_visits = 0;
633 assert_eq!(host.range_values(0, inside), [1_001.0, 1_002.0, 2_001.0, 2_002.0].map(Value::Number).to_vec());
634 assert!(host.spill_visits <= 8, "{}", host.spill_visits);
635 assert!(!host.visit_range(0, inside, &mut |_| true), "an overlapping read leaves the block to range_values");
636 }
637
638 #[test]
639 fn intervals_match_a_linear_scan() {
640 let mut iv: Intervals<u32> = Intervals::default();
641 let mut model: Vec<(u32, u32, u32)> = Vec::new();
642 let mut s = 0x9E37_79B9_7F4A_7C15;
643 for step in 0..20_000u32 {
644 let roll = rng(&mut s) % 10;
645 if roll < 6 || model.is_empty() {
646 let r0 = (rng(&mut s) % 500) as u32;
647 let r1 = r0 + (rng(&mut s) % 40) as u32;
648 if !model.contains(&(r0, r1, step)) {
649 iv.insert(r0, r1, step);
650 model.push((r0, r1, step));
651 }
652 } else {
653 let i = (rng(&mut s) % model.len() as u64) as usize;
654 let (r0, r1, t) = model.swap_remove(i);
655 iv.remove(r0, r1, t);
656 if roll == 9 {
657 iv.insert(r0, r1, t);
659 model.push((r0, r1, t));
660 }
661 }
662 if rng(&mut s).is_multiple_of(50) {
663 iv.settle();
664 }
665 if step.is_multiple_of(97) {
666 let a = (rng(&mut s) % 560) as u32;
667 let b = a + (rng(&mut s) % 30) as u32;
668 let mut want: Vec<u32> = model.iter().filter(|(r0, r1, _)| *r0 <= b && *r1 >= a).map(|(_, _, t)| *t).collect();
669 want.sort_unstable();
670 assert_eq!(found(&iv, a, b), want, "rows {a}..={b} at step {step}");
671 }
672 }
673 iv.settle();
674 for row in 0..560 {
675 let mut want: Vec<u32> = model.iter().filter(|(r0, r1, _)| *r0 <= row && row <= *r1).map(|(_, _, t)| *t).collect();
676 want.sort_unstable();
677 assert_eq!(found(&iv, row, row), want, "row {row}");
678 }
679 for (r0, r1, t) in std::mem::take(&mut model) {
680 iv.remove(r0, r1, t);
681 }
682 iv.settle();
683 assert!(iv.is_empty());
684 }
685
686 #[test]
687 fn interval_queries_cost_log_n_plus_matches() {
688 let n = 40_000u32;
691 let mut iv: Intervals<u32> = Intervals::default();
692 for r in 0..n {
693 iv.insert(r, r, r);
694 }
695 for t in 0..1_000 {
696 iv.insert(0, n, n + t);
697 }
698 iv.settle();
699 let mut worst = 0;
700 for row in (0..n).step_by(7) {
701 let mut k = 0;
702 let visited = iv.query(row, row, &mut |_| k += 1);
703 assert_eq!(k, 1_001);
704 worst = worst.max(visited);
705 }
706 assert!(worst < 4 * 1_001 + 64, "visited {worst} nodes");
708 let mut k = 0;
709 let visited = iv.query(n + 1, n + 5, &mut |_| k += 1);
710 assert_eq!(k, 0);
711 assert!(visited < 64, "visited {visited} nodes for no match");
712 }
713
714 #[test]
715 fn untaken_queries_list_each_interval_once_in_all() {
716 let n = 5_000u32;
719 let mut iv: Intervals<u32> = Intervals::default();
720 for t in 0..1_000 {
721 iv.insert(0, n, t);
722 }
723 for r in 0..n {
724 iv.insert(r, r, 10_000 + r);
725 }
726 iv.settle();
727 let mut taken = Vec::new();
728 let mut listed = Vec::new();
729 for row in 0..=n + 3 {
730 iv.query_untaken(row, row, &mut taken, &mut |t| {
731 listed.push(*t);
732 true
733 });
734 }
735 listed.sort_unstable();
736 let want: Vec<u32> = (0..1_000).chain(10_000..10_000 + n).collect();
737 assert_eq!(listed, want);
738 let mut taken = Vec::new();
740 let mut offered = 0;
741 for _ in 0..3 {
742 iv.query_untaken(7, 7, &mut taken, &mut |t| {
743 if *t == 10_007 {
744 offered += 1;
745 }
746 *t != 10_007
747 });
748 }
749 assert_eq!(offered, 3);
750 }
751
752 #[test]
753 fn dirty_closure_matches_a_walk_of_every_cell() {
754 use super::{HashSet, Node};
758 use crate::calc::eval::Area;
759 use std::collections::VecDeque;
760 let mut s = 0x2545_F491_4F6C_DD1D;
761 for case in 0..40 {
762 let mut wb = Workbook::new();
763 let mut calc = Calc::new();
764 let pick = |s: &mut u64, n: u64| (rng(s) % n) as u32;
765 let mut keys: Vec<Key> = Vec::new();
766 for _ in 0..300 {
767 let k = (0, CellRef::new(pick(&mut s, 120), pick(&mut s, 150)));
768 let mut areas = Vec::new();
769 for _ in 0..1 + pick(&mut s, 3) {
770 let start = CellRef::new(pick(&mut s, 120), pick(&mut s, 150));
771 let end = match pick(&mut s, 3) {
772 0 => start,
773 1 => CellRef::new(start.row + pick(&mut s, 40), start.col + pick(&mut s, 5)),
774 _ => CellRef::new(start.row + pick(&mut s, 20), start.col + 65 + pick(&mut s, 30)),
775 };
776 areas.push(Area { sheet: 0, range: RangeRef::new(start, end) });
777 }
778 if let Some(sh) = wb.sheet_mut(0) {
779 sh.cells.set(k.1, Cell::formula(Formula::new("=1")));
780 }
781 calc.graph.insert(k, Node { areas, dynamic: false });
782 keys.push(k);
783 if rng(&mut s).is_multiple_of(7) {
784 calc.graph.settle();
785 }
786 }
787 for _ in 0..30 {
788 let k = keys[(rng(&mut s) % keys.len() as u64) as usize];
789 calc.graph.remove(k);
790 if let Some(sh) = wb.sheet_mut(0) {
791 sh.cells.remove(k.1);
792 }
793 }
794 if case % 2 == 0 {
795 calc.graph.settle();
796 }
797 let seeds: Vec<Key> = (0..1 + pick(&mut s, 25)).map(|_| (0, CellRef::new(pick(&mut s, 140), pick(&mut s, 220)))).collect();
798 let areas: Vec<(usize, RangeRef)> = (0..pick(&mut s, 3))
799 .map(|_| {
800 let start = CellRef::new(pick(&mut s, 120), pick(&mut s, 150));
801 (0, RangeRef::new(start, CellRef::new(start.row + pick(&mut s, 30), start.col + pick(&mut s, 80))))
802 })
803 .collect();
804 let got: HashSet<Key> = calc.dirty_closure(&wb, &seeds, &areas).into_iter().collect();
805 let mut all = seeds.clone();
807 for (si, r) in &areas {
808 all.extend(r.iter().map(|c| (*si, c)));
809 }
810 let mut want: HashSet<Key> = HashSet::default();
811 let mut queue: VecDeque<Key> = VecDeque::new();
812 let mut deps = Vec::new();
813 for k in &all {
814 if calc.graph.nodes.contains_key(k) && want.insert(*k) {
815 queue.push_back(*k);
816 }
817 deps.clear();
818 calc.graph.dependents(k.0, k.1, &mut deps);
819 queue.extend(deps.iter().copied().filter(|d| want.insert(*d)));
820 }
821 while let Some(k) = queue.pop_front() {
822 deps.clear();
823 calc.graph.dependents(k.0, k.1, &mut deps);
824 queue.extend(deps.iter().copied().filter(|d| want.insert(*d)));
825 }
826 assert_eq!(got, want, "case {case}");
827 }
828 }
829
830 #[test]
831 fn clearing_a_spill_recalculates_formulas_reading_it_once_each() {
832 let mut wb = Workbook::new();
835 let mut calc = Calc::new();
836 let set = |wb: &mut Workbook, calc: &mut Calc, at: CellRef, input: &str| {
837 if let Some(sh) = wb.sheet_mut(0) {
838 if input.is_empty() {
839 sh.cells.remove(at);
840 } else {
841 sh.cells.set(at, Cell::formula(Formula::new(input)));
842 }
843 }
844 calc.cells_changed(wb, &[(0, at)]);
845 };
846 set(&mut wb, &mut calc, CellRef::new(0, 0), "=SEQUENCE(2000)");
847 for r in 0..300 {
848 set(&mut wb, &mut calc, CellRef::new(r, 2), "=COUNT(A1:A2000)");
849 }
850 assert_eq!(wb.sheet(0).map(|s| s.value(CellRef::new(299, 2))), Some(Value::Number(2000.0)));
851 set(&mut wb, &mut calc, CellRef::new(0, 0), "");
852 for r in 0..300 {
853 assert_eq!(wb.sheet(0).map(|s| s.value(CellRef::new(r, 2))), Some(Value::Number(0.0)), "row {r}");
854 }
855 }
856
857 fn sum_rows(rows: u32) -> Workbook {
859 let mut wb = Workbook::new();
860 if let Some(sh) = wb.sheet_mut(0) {
861 for r in 0..rows {
862 for c in 4..10 {
863 sh.cells.set(CellRef::new(r, c), Cell::value(Value::Number(f64::from(r % 97 + c))));
864 }
865 sh.cells.set(CellRef::new(r, 10), Cell::formula(Formula::new(&format!("=SUM(E{0}:J{0})", r + 1))));
866 }
867 }
868 wb
869 }
870
871 fn open_and_bulk_edit(rows: u32) -> Duration {
873 let mut wb = sum_rows(rows);
874 let mut calc = Calc::new();
875 let started = crate::base::date::unix_now_ms().unwrap_or(0.0);
876 calc.recalc_all(&mut wb);
877 let values: Vec<Key> = (0..rows).map(|r| (0, CellRef::new(r, 4))).collect();
878 if let Some(sh) = wb.sheet_mut(0) {
879 for (_, c) in &values {
880 sh.cells.set(*c, Cell::value(Value::Number(1.0)));
881 }
882 }
883 calc.cells_changed(&mut wb, &values);
884 let formulas: Vec<Key> = (0..rows).map(|r| (0, CellRef::new(r, 10))).collect();
885 calc.cells_changed(&mut wb, &formulas);
886 let ended = crate::base::date::unix_now_ms().unwrap_or(started);
887 let elapsed = Duration::from_secs_f64((ended - started).max(0.0) / 1000.0);
888 let last = wb.sheet(0).map(|s| s.value(CellRef::new(rows - 1, 10)));
889 let r = rows - 1;
890 assert_eq!(last, Some(Value::Number(f64::from(1 + (5..10).map(|c| r % 97 + c).sum::<u32>()))));
891 elapsed
892 }
893
894 #[test]
895 fn range_formulas_recalc_and_bulk_edit_correctly() {
896 open_and_bulk_edit(500);
898 }
899
900 #[test]
901 #[ignore = "timing-sensitive; run with --ignored in release"]
902 fn recalc_with_range_formulas_scales_linearly() {
903 let best = |rows| (0..3).map(|_| open_and_bulk_edit(rows)).min().unwrap_or_default();
907 let small = best(5_000);
908 let large = best(20_000);
909 assert!(large < small * 8 + Duration::from_millis(100), "5k rows {small:?}, 20k rows {large:?}");
910 }
911
912 #[test]
913 fn range_dependents_after_edits() {
914 let mut wb = Workbook::new();
916 let mut calc = Calc::new();
917 let set = |wb: &mut Workbook, calc: &mut Calc, at: &str, input: &str| {
918 let c = CellRef::parse(at).unwrap();
919 let cell = if input.starts_with('=') { Cell::formula(Formula::new(input)) } else { Cell::value(Value::Number(input.parse().unwrap())) };
920 wb.sheet_mut(0).unwrap().cells.set(c, cell);
921 calc.cells_changed(wb, &[(0, c)]);
922 };
923 let num = |wb: &Workbook, at: &str| {
924 let v = wb.sheet(0).unwrap().value(CellRef::parse(at).unwrap());
925 v.as_f64().unwrap_or_else(|| panic!("{at} = {v:?}"))
926 };
927 set(&mut wb, &mut calc, "A1", "1");
928 set(&mut wb, &mut calc, "B2", "2");
929 set(&mut wb, &mut calc, "CZ3", "3");
930 set(&mut wb, &mut calc, "DA5", "=SUM(A1:B2)");
931 set(&mut wb, &mut calc, "DA6", "=SUM(A1:CZ3)");
932 set(&mut wb, &mut calc, "DA7", "=SUM(B:B)");
933 set(&mut wb, &mut calc, "DA8", "=SUM(2:2)");
934 assert_eq!((num(&wb, "DA5"), num(&wb, "DA6"), num(&wb, "DA7"), num(&wb, "DA8")), (3.0, 6.0, 2.0, 2.0));
935 set(&mut wb, &mut calc, "B2", "20");
936 assert_eq!((num(&wb, "DA5"), num(&wb, "DA6"), num(&wb, "DA7"), num(&wb, "DA8")), (21.0, 24.0, 20.0, 20.0));
937 set(&mut wb, &mut calc, "DA5", "=SUM(A1:A2)");
939 set(&mut wb, &mut calc, "B2", "5");
940 assert_eq!(num(&wb, "DA5"), 1.0);
941 let mut deps = Vec::new();
942 calc.graph.dependents(0, CellRef::parse("B2").unwrap(), &mut deps);
943 deps.sort_unstable();
944 let at = |s: &str| (0, CellRef::parse(s).unwrap());
945 assert_eq!(deps, vec![at("DA6"), at("DA7"), at("DA8")]);
946 deps.clear();
947 calc.graph.dependents_of_range(0, RangeRef::new(CellRef::parse("A1").unwrap(), CellRef::parse("A9").unwrap()), &mut deps);
948 deps.sort_unstable();
949 deps.dedup();
950 assert_eq!(deps, vec![at("DA5"), at("DA6"), at("DA8")]);
951 set(&mut wb, &mut calc, "CZ3", "30");
952 assert_eq!(num(&wb, "DA6"), 36.0);
953 wb.sheets.push(std::sync::Arc::new(crate::model::Sheet::new("Data")));
955 set(&mut wb, &mut calc, "DA9", "=SUM(Data!A1:A3)+SUM(Data!A:CZ)");
956 assert_eq!(num(&wb, "DA9"), 0.0);
957 wb.sheet_mut(1).unwrap().cells.set(CellRef::parse("A2").unwrap(), Cell::value(Value::Number(4.0)));
958 calc.cells_changed(&mut wb, &[(1, CellRef::parse("A2").unwrap())]);
959 assert_eq!(num(&wb, "DA9"), 8.0);
960 deps.clear();
961 calc.graph.dependents(1, CellRef::parse("A2").unwrap(), &mut deps);
962 assert_eq!(deps, vec![at("DA9"), at("DA9")]);
963 }
964
965 #[test]
968 fn wide_read_at_the_nesting_limit_suspends_once() {
969 const WIDTH: u32 = 3000;
970 let mut wb = Workbook::new();
971 let mut exprs = HashMap::default();
972 let mut put = |wb: &mut Workbook, at: String, text: String| {
973 let at = CellRef::parse(&at).unwrap();
974 wb.sheet_mut(0).unwrap().cells.set(at, Cell::formula(Formula::new(&text)));
975 exprs.insert((0, at), std::sync::Arc::new(crate::formula::parse(&text[1..]).unwrap()));
976 };
977 for r in 1..64 {
979 put(&mut wb, format!("A{r}"), format!("=A{}+1", r + 1));
980 }
981 put(&mut wb, "A64".to_string(), format!("=SUM(B1:B{WIDTH})"));
982 for r in 1..=WIDTH {
983 put(&mut wb, format!("B{r}"), format!("={r}*2"));
984 }
985 let mut rng = 1u64;
986 let mut host = PassHost::new(&wb, &exprs, &mut rng, 0.0);
987 host.calculate((0, CellRef::parse("A1").unwrap()));
988 let total = (WIDTH * (WIDTH + 1)) as f64;
989 assert_eq!(host.results.get(&(0, CellRef::parse("A1").unwrap())), Some(&Value::Number(total + 63.0)));
990 assert!(host.cycles.is_empty());
991 assert!(host.suspensions <= 3, "{} suspensions", host.suspensions);
992 }
993
994 pub(crate) fn probe_visit_range(wb: &Workbook, pending: &[(&str, &str)], range: RangeRef) -> Option<Vec<Value>> {
998 let mut exprs = HashMap::default();
999 for (at, formula) in pending {
1000 let expr = crate::formula::parse(formula.strip_prefix('=').unwrap_or(formula)).ok()?;
1001 exprs.insert((0, CellRef::parse(at)?), std::sync::Arc::new(expr));
1002 }
1003
1004 let mut rng = 1u64;
1008 let mut host = PassHost::new(wb, &exprs, &mut rng, 0.0);
1009
1010 let mut seen = Vec::new();
1011 host.visit_range(0, range, &mut |v| {
1012 seen.push(v.clone());
1013 true
1014 })
1015 .then_some(seen)
1016 }
1017}