ironcalc_base/evaluation.rs
1//! Evaluation of every formula in the workbook.
2//!
3//! The algorithm in short:
4//!
5//! * A **pass** is the plain top-down recursion: to evaluate a cell, evaluate
6//! the cells it reads as it meets them. A cell met while it is already being
7//! evaluated closes a cycle. Dynamic array anchors are evaluated first, in a
8//! remembered order, then every formula of every sheet.
9//! * A dynamic array writes cells other than its own, and which ones is only
10//! known once it has run. Anything that read one of those positions earlier
11//! in the pass read the wrong thing. So the pass records what formulas saw
12//! at the positions they read, and when an anchor is about to contradict a
13//! record, the pass is **abandoned and started again with that anchor
14//! before the cells that read it**. Each restart is a fact learned about
15//! the sheet, "this anchor runs before that reader"; the driver keeps the
16//! facts and orders the anchors by them. A restart can only happen when
17//! the order breaks a fact not yet known, so there are at most `n²` of
18//! them, and a loop of anchors reading each other's areas shows up as a
19//! cycle among the facts. An anchor whose own inputs read its area is
20//! contradicted by itself wherever it sits: it is circular.
21//! * Spill cells left by a previous evaluation are never read as values
22//! before their anchor has run in the current pass; they still block other
23//! arrays, which is how the array that spilled first keeps its cells. The
24//! one thing they cannot do is stand in for the anchor's output: when an
25//! anchor gives them up and only its own inputs were blocked by them, they
26//! were history, not a dependency. They are dropped for the rest of the
27//! evaluation and the pass starts again without them.
28//!
29//! * The recursion is bounded, in formulas and in stack. When it would go
30//! deeper it **unwinds**: the formulas in progress end without storing
31//! anything but stay on the stack, still `Evaluating`. The outermost one
32//! then **replays** the stack from the top, the deepest cell first, as often
33//! as it takes. A long chain of formulas therefore costs memory and about
34//! one extra run per cell instead of overflowing the stack of the thread.
35//!
36//! The order of the anchors survives across evaluations, so a sheet that
37//! needed restarts once evaluates in a single pass afterwards.
38//!
39//! Nothing here depends on the order in which cells are visited, only the
40//! amount of work does.
41
42use std::collections::{HashMap, HashSet};
43use std::sync::Arc;
44
45use crate::calc_result::CalcResult;
46use crate::constants::{LAST_COLUMN, LAST_ROW};
47use crate::expressions::token::Error;
48use crate::expressions::types::CellReferenceIndex;
49use crate::model::Model;
50use crate::types::{ArrayKind, Cell, Worksheet};
51
52/// The hasher of the maps the evaluation keeps about positions. Their keys are
53/// three small integers and every formula looks several of them up, which
54/// made the default hasher, built to resist chosen keys, a fifth of the cost
55/// of an evaluation. This one multiplies and rotates, a word at a time.
56#[derive(Default, Clone, Copy)]
57pub(crate) struct KeyHasher(u64);
58
59impl std::hash::Hasher for KeyHasher {
60 fn write(&mut self, bytes: &[u8]) {
61 for byte in bytes {
62 self.write_u32(*byte as u32);
63 }
64 }
65
66 fn write_u32(&mut self, i: u32) {
67 self.0 = (self.0.rotate_left(5) ^ i as u64).wrapping_mul(0x517c_c1b7_2722_0a95);
68 }
69
70 fn write_i32(&mut self, i: i32) {
71 self.write_u32(i as u32);
72 }
73
74 fn finish(&self) -> u64 {
75 self.0
76 }
77}
78
79pub(crate) type KeyBuild = std::hash::BuildHasherDefault<KeyHasher>;
80
81/// `(sheet, row, column)`: a position in the workbook.
82pub(crate) type CellKey = (u32, i32, i32);
83
84fn key(cell: CellReferenceIndex) -> CellKey {
85 (cell.sheet, cell.row, cell.column)
86}
87
88/// What the evaluation needs to know about a formula cell in order to run it
89/// and to store its result: everything but its value. A few numbers, copied
90/// out of the cell, so that the sheet is not borrowed while the formula runs
91/// and the cell does not have to be cloned.
92#[derive(Clone, Copy, PartialEq, Eq, Debug)]
93pub(crate) struct FormulaCell {
94 /// Index of the formula in the parsed formulas of the sheet.
95 pub(crate) f: i32,
96 /// Style index.
97 pub(crate) s: i32,
98 /// For the anchor of an array: whether it is dynamic, and the area
99 /// `(width, height)` it occupies now.
100 pub(crate) array: Option<(bool, (i32, i32))>,
101}
102
103impl FormulaCell {
104 /// `None` if the cell does not hold a formula.
105 pub(crate) fn of(cell: &Cell) -> Option<FormulaCell> {
106 match cell {
107 Cell::CellFormula { f, s, .. } => Some(FormulaCell {
108 f: *f,
109 s: *s,
110 array: None,
111 }),
112 Cell::ArrayFormula { f, s, r, kind, .. } => Some(FormulaCell {
113 f: *f,
114 s: *s,
115 array: Some((matches!(kind, ArrayKind::Dynamic), *r)),
116 }),
117 _ => None,
118 }
119 }
120}
121
122/// The state of a formula cell within the current pass. Absent means it has
123/// not been evaluated yet.
124#[derive(Clone, Copy, PartialEq, Eq, Debug)]
125pub(crate) enum CellState {
126 Evaluating,
127 Evaluated,
128}
129
130/// What a formula found at a position when it read it, as far as spills are
131/// concerned. A spill written into a position seen `Empty`, or a spill cell
132/// removed from a position seen `Occupied`, contradicts the reader.
133#[derive(Clone, Copy, PartialEq, Eq, Debug)]
134pub(crate) enum Seen {
135 Empty,
136 Occupied,
137}
138
139/// What was seen at one position in the current pass: the root of the
140/// recursion that first read it as empty, and the root of the recursion on
141/// whose behalf an array was first blocked by it. Both can be set for the
142/// same position: a leftover spill cell of the anchor being evaluated is read
143/// as empty on its behalf, and then blocks an array evaluated on demand
144/// inside it. Keeping only the first record would let the anchor remove the
145/// cell without contradicting the blocked array.
146#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)]
147pub(crate) struct SeenRecord {
148 pub(crate) empty: Option<CellReferenceIndex>,
149 pub(crate) occupied: Option<CellReferenceIndex>,
150}
151
152/// A rectangle read as empty on behalf of `root`, in one go. A function that
153/// walks a whole column or row visits only the sheet's used area; the rest of
154/// the range holds nothing, and the formula depends on it all the same. That
155/// remainder is one record, not one per cell, so a whole-column read costs a
156/// single entry beyond the cells it visits.
157#[derive(Clone, Copy, PartialEq, Eq, Debug)]
158pub(crate) struct SeenRange {
159 pub(crate) root: CellReferenceIndex,
160 pub(crate) sheet: u32,
161 pub(crate) row1: i32,
162 pub(crate) column1: i32,
163 pub(crate) row2: i32,
164 pub(crate) column2: i32,
165}
166
167impl SeenRange {
168 fn contains(&self, (sheet, row, column): CellKey) -> bool {
169 sheet == self.sheet
170 && (self.row1..=self.row2).contains(&row)
171 && (self.column1..=self.column2).contains(&column)
172 }
173}
174
175/// Why the current pass is abandoned. In every case the pass starts again,
176/// after the driver has learned what the restart says about the order.
177#[derive(Clone, PartialEq, Eq, Debug)]
178pub(crate) enum Restart {
179 /// A spill cell of this anchor was read, on behalf of `reader`, before the
180 /// anchor had run. The word "stale" means is a leftover from a previous evaluation.
181 /// Example:
182 /// A1 = SEQUENCE(1,1,5)
183 /// B1 = SEQUENCE(3)
184 /// The evaluation order in A1, B1, no restarts
185 /// Then we switch A1 = SEQUENCE(1,1,B3)
186 /// Then A1 reads B3 which is a leftover from the previous evaluation of B1.
187 StaleRead {
188 anchor: CellReferenceIndex,
189 reader: CellReferenceIndex,
190 },
191 /// What this anchor is about to write contradicts something read on behalf
192 /// of `readers`, other cells: the anchor should have run before them.
193 /// The same two formulas on a fresh sheet, with nothing spilled yet:
194 /// A1 = SEQUENCE(1,1,B3)
195 /// B1 = SEQUENCE(3)
196 /// While evaluating natural order A1 reads B3 and takes note of that. When B1 evaluates it wants to write
197 /// B3 and finds a conflict
198 Conflict {
199 anchor: CellReferenceIndex,
200 readers: Vec<CellReferenceIndex>,
201 },
202 /// What this anchor is about to write contradicts only reads made on its
203 /// own behalf: its inputs depend on its own output. It is circular.
204 SelfContradiction(CellReferenceIndex),
205 /// The spill cells this anchor is giving up, left by a previous
206 /// evaluation, blocked an array evaluated on its own behalf and nothing
207 /// else. The anchor's inputs depended on its history, not on its output:
208 /// the cells are dropped for the rest of the evaluation.
209 /// An example will be:
210 /// A1 = 1
211 /// B2 = IF(A1=1, SEQUENCE(2,2), IF(ISERROR(C1), 5, 7))
212 /// C1 = SEQUENCE(3)
213 /// The evaluation order in A1, B2, C1, no restarts. C1 is #SPILL!
214 /// Now we set A1 = 0. A2 will read C1 and force it's evaluation that will still be blocked by the stale spill from the previous C1 evaluation.
215 /// The evaluation of B2 will want to delete C2 and C3 which are the stale cells.
216 StaleCells(CellReferenceIndex, Vec<CellKey>),
217}
218
219impl Restart {
220 fn anchor(&self) -> CellReferenceIndex {
221 match self {
222 Restart::StaleRead { anchor, .. }
223 | Restart::Conflict { anchor, .. }
224 | Restart::SelfContradiction(anchor)
225 | Restart::StaleCells(anchor, _) => *anchor,
226 }
227 }
228}
229
230/// The driver's memory of one evaluation: the facts learned about the order,
231/// and the anchors marked circular.
232///
233/// Every pass starts from the same sheet, so a pass is a function of the
234/// anchor order and of the circular set. A restart says that some readers
235/// read an anchor's area before it ran: a fact, "the anchor runs before each
236/// of them", that the current order breaks. The driver records the fact and
237/// repairs the order so that every fact holds, moving the anchor and what
238/// must precede it to just before the reader (`learn`). Since the order
239/// always respects the known facts, every restart adds a fact that was not
240/// known; there are at most `n(n-1)` of them. When a fact would close a
241/// cycle, the anchors on the cycle read each other's areas and no order can
242/// serve them: they are marked circular, at most `n` times.
243/// A marked anchor runs no formula and, its stale cells dropped, writes and
244/// blocks nothing, so it never restarts again. Stale-cell restarts drop at
245/// least one cell each. Hence the loop ends, with no cap needed.
246struct RestartLog {
247 /// Anchors marked circular so far; passed to every pass.
248 circular: Vec<CellKey>,
249 /// `(a, r)`: `a` runs before `r`. Learned from restarts; never broken by
250 /// the order; acyclic.
251 facts: Vec<(CellReferenceIndex, CellReferenceIndex)>,
252 /// Every restart of this evaluation.
253 restarts: u32,
254}
255
256impl RestartLog {
257 fn new() -> Self {
258 RestartLog {
259 circular: Vec::new(),
260 facts: Vec::new(),
261 restarts: 0,
262 }
263 }
264
265 /// Records a restart: learns its facts, marks what it proves circular and
266 /// reorders `order` to respect the facts. Returns the anchors newly
267 /// marked circular.
268 fn record(
269 &mut self,
270 restart: &Restart,
271 order: &mut Vec<CellReferenceIndex>,
272 ) -> Vec<CellReferenceIndex> {
273 self.restarts += 1;
274 let anchor = restart.anchor();
275 let readers: Vec<CellReferenceIndex> = match restart {
276 Restart::StaleRead { reader, .. } => vec![*reader],
277 Restart::Conflict { readers, .. } => readers.clone(),
278 Restart::SelfContradiction(_) => vec![],
279 Restart::StaleCells(..) => return Vec::new(),
280 };
281 let mut newly_circular: Vec<CellReferenceIndex> = Vec::new();
282 if let Restart::SelfContradiction(_) = restart {
283 self.mark(anchor, &mut newly_circular);
284 }
285 for reader in readers {
286 if self.circular.contains(&key(anchor)) {
287 break;
288 }
289 if self.circular.contains(&key(reader)) {
290 // Marked while an earlier reader was learned: on a loop,
291 // nothing to order it against.
292 continue;
293 }
294 self.learn(anchor, reader, order, &mut newly_circular);
295 }
296 newly_circular
297 }
298
299 /// Learns that `anchor` runs before `reader`, one restart's fact. If the
300 /// facts already place `reader` before `anchor`, the two are on a loop of
301 /// anchors reading each other's areas, with everything the facts place
302 /// between them: all of it is marked. Otherwise the fact is kept and the
303 /// order repaired: `anchor`, and whatever the facts place before it, move
304 /// from behind `reader` to just before it, in their present order.
305 fn learn(
306 &mut self,
307 anchor: CellReferenceIndex,
308 reader: CellReferenceIndex,
309 order: &mut Vec<CellReferenceIndex>,
310 newly_circular: &mut Vec<CellReferenceIndex>,
311 ) {
312 if reader == anchor {
313 return;
314 }
315 let before_anchor = self.reachable(anchor, |a, r| (r, a));
316 if before_anchor.contains(&reader) {
317 let after_reader = self.reachable(reader, |a, r| (a, r));
318 for cell in before_anchor {
319 if after_reader.contains(&cell) {
320 self.mark(cell, newly_circular);
321 }
322 }
323 return;
324 }
325 self.facts.push((anchor, reader));
326 let Some(reader_position) = order.iter().position(|c| *c == reader) else {
327 return;
328 };
329 let moved: Vec<CellReferenceIndex> = order[reader_position + 1..]
330 .iter()
331 .copied()
332 .filter(|cell| before_anchor.contains(cell))
333 .collect();
334 order.retain(|cell| !moved.contains(cell));
335 order.splice(reader_position..reader_position, moved);
336 }
337
338 /// Marks an anchor circular. A marked anchor neither writes nor reads:
339 /// its facts are spent.
340 fn mark(&mut self, cell: CellReferenceIndex, newly_circular: &mut Vec<CellReferenceIndex>) {
341 if self.circular.contains(&key(cell)) {
342 return;
343 }
344 self.circular.push(key(cell));
345 newly_circular.push(cell);
346 self.facts.retain(|(a, r)| *a != cell && *r != cell);
347 }
348
349 /// Everything reachable from `start` along the facts, `start` included,
350 /// with `edge` giving the direction to follow.
351 fn reachable(
352 &self,
353 start: CellReferenceIndex,
354 edge: fn(
355 CellReferenceIndex,
356 CellReferenceIndex,
357 ) -> (CellReferenceIndex, CellReferenceIndex),
358 ) -> Vec<CellReferenceIndex> {
359 let mut seen = vec![start];
360 let mut todo = vec![start];
361 while let Some(cell) = todo.pop() {
362 for (a, r) in &self.facts {
363 let (from, to) = edge(*a, *r);
364 if from == cell && !seen.contains(&to) {
365 seen.push(to);
366 todo.push(to);
367 }
368 }
369 }
370 seen
371 }
372}
373
374/// Everything the evaluation needs besides the workbook itself.
375pub(crate) struct Evaluation {
376 /// The dynamic anchors, in the order in which they are evaluated. Kept
377 /// across evaluations: the order the restarts arrived at stays, so the
378 /// next evaluation needs no restart. New anchors are appended in natural
379 /// `(sheet, row, column)` order.
380 pub(crate) anchor_order: Vec<CellReferenceIndex>,
381 /// Formula cells touched in the current pass, and their state.
382 pub(crate) cells: HashMap<CellKey, CellState, KeyBuild>,
383 /// Cells being evaluated, innermost last.
384 pub(crate) stack: Vec<CellReferenceIndex>,
385 /// The cell the driver is evaluating: the root of the current recursion,
386 /// on whose behalf every read of the pass is recorded.
387 pub(crate) root: Option<CellReferenceIndex>,
388 /// Cells known to be circular. Marked on the stack when a read closes a
389 /// loop, or by the driver when an anchor's spill contradicts its own inputs;
390 /// the latter survive restarts within one evaluation.
391 pub(crate) circular: HashSet<CellKey, KeyBuild>,
392 /// What formulas saw at the positions they read in the current pass, and
393 /// the root of the recursion the read was made in (the cell the driver was
394 /// evaluating at the time), one record per kind.
395 pub(crate) seen: HashMap<CellKey, SeenRecord, KeyBuild>,
396 /// Rectangles read as empty in one go, with their root (`SeenRange`).
397 pub(crate) seen_ranges: Vec<SeenRange>,
398 /// Set when the current pass must be abandoned. Every evaluation in
399 /// progress then returns without storing anything.
400 pub(crate) restart: Option<Restart>,
401 /// True while `evaluate()` is running a pass. Outside a pass (conditional
402 /// formatting at load time, formula helpers) there is no driver to restart
403 /// anything, so cells are simply evaluated on demand.
404 pub(crate) in_pass: bool,
405 /// Number of restarts the last `evaluate()` needed. For tests and tuning.
406 pub(crate) restarts_in_last_evaluation: u32,
407 /// Number of formulas running right now, that is the depth of the
408 /// recursion. It can be smaller than `stack.len()`: see `unwinding`.
409 pub(crate) depth: usize,
410 /// The deepest the recursion is allowed to go, in formulas.
411 pub(crate) max_depth: usize,
412 /// The most stack the recursion is allowed to use, in bytes, measured from
413 /// the outermost formula. How much one formula needs varies a lot, with
414 /// the formula and with the build, so a number of formulas alone does not
415 /// bound the stack.
416 pub(crate) max_stack: usize,
417 /// Where the stack was when the outermost formula started.
418 pub(crate) stack_base: usize,
419 /// Set when the recursion got too deep. Like a pending restart, every
420 /// evaluation in progress then returns without storing anything; unlike a
421 /// restart, the cells stay on `stack`, still `Evaluating`, to be run again
422 /// from the top once the recursion has unwound.
423 pub(crate) unwinding: bool,
424}
425
426/// How deep the recursion may go when nobody has said otherwise, in formulas
427/// and in bytes of stack. Measured on a chain of `=A2+1`: a formula takes about
428/// 3 KB of stack in a release build and 16 KB in a debug build, and three to
429/// four times that when it nests a few functions. The limits are far below
430/// what the smallest common stacks hold (1 MB in WebAssembly, 2 MB for a
431/// thread) and cost nothing to workbooks that never reach them.
432const DEFAULT_MAX_DEPTH: usize = 64;
433const DEFAULT_MAX_STACK: usize = 256 * 1024;
434
435/// Roughly where the stack is: the address of a local.
436#[inline(never)]
437fn stack_address() -> usize {
438 let marker = 0u8;
439 std::hint::black_box(&marker) as *const u8 as usize
440}
441
442/// The limit a new model starts with. Under test it can be set from outside,
443/// `IRONCALC_MAX_DEPTH=1 cargo test`, so that every test of the crate runs
444/// through the unwind and the replay.
445fn default_max_depth() -> usize {
446 #[cfg(test)]
447 if let Some(max_depth) = std::env::var("IRONCALC_MAX_DEPTH")
448 .ok()
449 .and_then(|value| value.parse().ok())
450 {
451 return max_depth;
452 }
453 DEFAULT_MAX_DEPTH
454}
455
456impl Default for Evaluation {
457 fn default() -> Self {
458 Evaluation {
459 anchor_order: Vec::new(),
460 cells: HashMap::default(),
461 stack: Vec::new(),
462 root: None,
463 circular: HashSet::default(),
464 seen: HashMap::default(),
465 seen_ranges: Vec::new(),
466 restart: None,
467 in_pass: false,
468 restarts_in_last_evaluation: 0,
469 depth: 0,
470 max_depth: default_max_depth(),
471 max_stack: DEFAULT_MAX_STACK,
472 stack_base: 0,
473 unwinding: false,
474 }
475 }
476}
477
478impl Evaluation {
479 /// See `Model::record_seen`. Here so that a read can be put on record
480 /// while the sheet it was made in is still borrowed.
481 pub(crate) fn record_seen(&mut self, position: CellReferenceIndex, seen: Seen) {
482 if !self.in_pass {
483 return;
484 }
485 if let Some(root) = self.root {
486 let record = self.seen.entry(key(position)).or_default();
487 let slot = match seen {
488 Seen::Empty => &mut record.empty,
489 Seen::Occupied => &mut record.occupied,
490 };
491 if slot.is_none() {
492 *slot = Some(root);
493 }
494 }
495 }
496}
497
498impl<'a> Model<'a> {
499 /// Evaluates every formula in the workbook.
500 ///
501 /// Runs passes until one completes without a restart. `RestartLog` learns
502 /// from each restart, reorders the anchors and marks what it proves
503 /// circular; see there for why the loop ends. A marked anchor stores
504 /// `#CIRC!` and keeps no spill cells: its stale cells are dropped when it
505 /// is marked, so there is nothing of it to read or to contradict.
506 pub fn evaluate(&mut self) {
507 self.sync_anchor_order();
508 // Every pass starts from the same sheet: what an abandoned pass wrote
509 // is undone. This is what makes a pass a function of the anchor order.
510 // The only changes to that sheet within an evaluation are the dropping
511 // of stale spill cells: an anchor's own when it gave them up, a marked
512 // anchor's when it is marked.
513 let mut spills_before = self.dynamic_spills();
514 let mut log = RestartLog::new();
515 while let Some(restart) = self.run_pass(&log.circular) {
516 let marked = log.record(&restart, &mut self.evaluation.anchor_order);
517 spills_before.retain(|(position, cell)| {
518 let dropped = match &restart {
519 Restart::StaleCells(_, cells) => cells.contains(&key(*position)),
520 _ => false,
521 };
522 let of_marked = match cell {
523 Cell::SpillCell { a, .. } => marked
524 .iter()
525 .any(|m| m.sheet == position.sheet && (m.row, m.column) == *a),
526 _ => false,
527 };
528 !dropped && !of_marked
529 });
530 self.restore_dynamic_spills(&spills_before);
531 }
532 self.evaluation.restarts_in_last_evaluation = log.restarts;
533 self.evaluate_conditional_formatting();
534 }
535
536 /// Sets how many formulas deep the evaluation may recurse. A formula that
537 /// reads a formula that reads a formula, and so on, uses the stack of the
538 /// thread for every step. Beyond this limit the evaluation unwinds and
539 /// carries on from where it stopped instead of going deeper, so the limit
540 /// bounds the stack used and never changes a result. Lower it where the
541 /// stack is small. The least it can be is 1. See also
542 /// `set_max_evaluation_stack`: whichever limit is reached first applies.
543 pub fn set_max_evaluation_depth(&mut self, max_depth: usize) {
544 self.evaluation.max_depth = max_depth.max(1);
545 }
546
547 /// The limit set by `set_max_evaluation_depth`.
548 pub fn get_max_evaluation_depth(&self) -> usize {
549 self.evaluation.max_depth
550 }
551
552 /// Sets how many bytes of stack the evaluation may use before it unwinds,
553 /// counted from the first formula of the recursion. The evaluation can
554 /// overshoot by what one formula needs, so leave room: a quarter of the
555 /// stack is a reasonable value.
556 pub fn set_max_evaluation_stack(&mut self, bytes: usize) {
557 self.evaluation.max_stack = bytes;
558 }
559
560 /// The limit set by `set_max_evaluation_stack`.
561 pub fn get_max_evaluation_stack(&self) -> usize {
562 self.evaluation.max_stack
563 }
564
565 /// The sheets that can hold a formula, with their index. A formula cell
566 /// points into the shared formulas of its sheet, so a sheet that has none
567 /// holds only constants: no formula, no anchor, and no spill cell that
568 /// still has one. Such a sheet is not walked at all. In a workbook that is
569 /// mostly data, walking the constants cost more than running the formulas.
570 fn sheets_with_formulas(&self) -> impl Iterator<Item = (u32, &Worksheet)> {
571 self.workbook
572 .worksheets
573 .iter()
574 .enumerate()
575 .filter(|(_, worksheet)| !worksheet.shared_formulas.is_empty())
576 .map(|(sheet, worksheet)| (sheet as u32, worksheet))
577 }
578
579 /// Every formula cell, in natural `(sheet, row, column)` order. The other
580 /// cells are left out: there is nothing to evaluate in a constant, and a
581 /// spill cell holds what its anchor, a formula, writes.
582 fn formula_positions(&self) -> Vec<CellReferenceIndex> {
583 let mut found = Vec::new();
584 for (sheet, worksheet) in self.sheets_with_formulas() {
585 for (row, column, cell) in worksheet.sheet_data.cells() {
586 if FormulaCell::of(&cell).is_some() {
587 found.push(CellReferenceIndex { sheet, row, column });
588 }
589 }
590 }
591 found
592 }
593
594 /// Every dynamic anchor and every spill cell of a dynamic anchor, as they
595 /// are now, in natural order, which is the order the sheets give their
596 /// cells in.
597 fn dynamic_spills(&self) -> Vec<(CellReferenceIndex, Cell)> {
598 let mut found = Vec::new();
599 for (sheet, worksheet) in self.sheets_with_formulas() {
600 for (row, column, cell) in worksheet.sheet_data.cells() {
601 if !matches!(*cell, Cell::ArrayFormula { .. } | Cell::SpillCell { .. }) {
602 continue;
603 }
604 let position = CellReferenceIndex { sheet, row, column };
605 if self.belongs_to_a_dynamic_array(position, &cell) {
606 found.push((position, cell.into_owned()));
607 }
608 }
609 }
610 found
611 }
612
613 /// Puts the dynamic anchors and their spill cells back to `snapshot`,
614 /// removing the spill cells written since.
615 fn restore_dynamic_spills(&mut self, snapshot: &[(CellReferenceIndex, Cell)]) {
616 let written_since: Vec<CellReferenceIndex> = self
617 .dynamic_spills()
618 .into_iter()
619 .filter(|(_, cell)| matches!(cell, Cell::SpillCell { .. }))
620 .map(|(position, _)| position)
621 .collect();
622 for position in written_since {
623 if let Ok(worksheet) = self.workbook.worksheet_mut(position.sheet) {
624 let _ = worksheet.cell_clear_contents(position.row, position.column);
625 }
626 }
627 for (position, cell) in snapshot {
628 if let Ok(worksheet) = self.workbook.worksheet_mut(position.sheet) {
629 let _ = worksheet.update_cell(position.row, position.column, cell.clone());
630 }
631 }
632 }
633
634 fn belongs_to_a_dynamic_array(&self, position: CellReferenceIndex, cell: &Cell) -> bool {
635 match cell {
636 Cell::ArrayFormula {
637 kind: ArrayKind::Dynamic,
638 ..
639 } => true,
640 Cell::SpillCell { a, .. } => matches!(
641 self.fetch_cell(CellReferenceIndex {
642 sheet: position.sheet,
643 row: a.0,
644 column: a.1,
645 })
646 .as_deref(),
647 Some(Cell::ArrayFormula {
648 kind: ArrayKind::Dynamic,
649 ..
650 })
651 ),
652 _ => false,
653 }
654 }
655
656 /// Brings `anchor_order` in line with the dynamic anchors that exist now:
657 /// anchors that are gone are dropped, new ones are appended in natural
658 /// order, the rest keep their relative order.
659 fn sync_anchor_order(&mut self) {
660 let current = self.dynamic_anchors_in_natural_order();
661 let current_set: HashSet<CellReferenceIndex> = current.iter().copied().collect();
662 let order = &mut self.evaluation.anchor_order;
663 order.retain(|anchor| current_set.contains(anchor));
664 let known: HashSet<CellReferenceIndex> = order.iter().copied().collect();
665 order.extend(current.into_iter().filter(|a| !known.contains(a)));
666 }
667
668 fn dynamic_anchors_in_natural_order(&self) -> Vec<CellReferenceIndex> {
669 let mut found = Vec::new();
670 for (sheet, worksheet) in self.sheets_with_formulas() {
671 for (row, column, cell) in worksheet.sheet_data.cells() {
672 if matches!(
673 *cell,
674 Cell::ArrayFormula {
675 kind: ArrayKind::Dynamic,
676 ..
677 }
678 ) {
679 found.push(CellReferenceIndex { sheet, row, column });
680 }
681 }
682 }
683 found
684 }
685
686 /// One pass over the workbook: anchors first, then every formula. Returns
687 /// the reason the pass had to be abandoned, if any.
688 fn run_pass(&mut self, circular_anchors: &[CellKey]) -> Option<Restart> {
689 let state = &mut self.evaluation;
690 state.cells.clear();
691 state.stack.clear();
692 state.seen.clear();
693 state.seen_ranges.clear();
694 state.restart = None;
695 state.depth = 0;
696 state.unwinding = false;
697 state.circular = circular_anchors.iter().copied().collect();
698 state.in_pass = true;
699 // dynamic links (HYPERLINK) are rebuilt on every evaluation
700 self.links.clear();
701 self.clear_variable_stack();
702 self.clear_lambdas();
703
704 let anchors = self.evaluation.anchor_order.clone();
705 let formulas = self.formula_positions();
706 let mut restart = None;
707 for cell in anchors.into_iter().chain(formulas) {
708 self.evaluation.root = Some(cell);
709 self.evaluate_cell(cell);
710 if self.evaluation.restart.is_some() {
711 restart = self.evaluation.restart.take();
712 break;
713 }
714 }
715 self.evaluation.in_pass = false;
716 self.evaluation.root = None;
717 restart
718 }
719
720 /// The value of a cell, evaluating it first if it is a formula that has
721 /// not been evaluated in this pass. Reads are performed on behalf of the
722 /// cell on top of the stack.
723 pub(crate) fn evaluate_cell(&mut self, cell_reference: CellReferenceIndex) -> CalcResult {
724 if self.evaluation.restart.is_some() || self.evaluation.unwinding {
725 // The pass is being abandoned, or the recursion is unwinding;
726 // nothing computed now is kept. This comes before anything is put
727 // on record: a read that was not made is nobody's dependency.
728 return CalcResult::EmptyCell;
729 }
730 // The cell is looked at, not cloned: what is needed of it is copied
731 // out, and the sheet is free again before anything else is evaluated.
732 enum Found {
733 Nothing,
734 Spill((i32, i32)),
735 Formula(FormulaCell),
736 Constant(CalcResult),
737 }
738 let found = match self.fetch_cell(cell_reference).as_deref() {
739 None | Some(Cell::EmptyCell { .. }) => Found::Nothing,
740 Some(Cell::SpillCell { a, .. }) => Found::Spill(*a),
741 Some(cell) => match FormulaCell::of(cell) {
742 Some(formula_cell) => Found::Formula(formula_cell),
743 None => Found::Constant(self.get_cell_value(cell, cell_reference)),
744 },
745 };
746 match found {
747 Found::Nothing => {
748 self.record_seen(cell_reference, Seen::Empty);
749 CalcResult::EmptyCell
750 }
751 Found::Spill(anchor) => self.evaluate_spill_cell(cell_reference, anchor),
752 Found::Formula(formula_cell) => {
753 self.evaluate_formula_cell(cell_reference, formula_cell)
754 }
755 Found::Constant(value) => value,
756 }
757 }
758
759 /// A spill cell holds what its anchor wrote. Only a value written by the
760 /// anchor in the current pass is a value; anything else is either the
761 /// anchor's business (a CSE area is fixed and forwards to its anchor) or a
762 /// leftover of a previous evaluation, which is read as empty or triggers a
763 /// restart so that the anchor runs first.
764 fn evaluate_spill_cell(
765 &mut self,
766 cell_reference: CellReferenceIndex,
767 anchor: (i32, i32),
768 ) -> CalcResult {
769 let anchor_reference = CellReferenceIndex {
770 sheet: cell_reference.sheet,
771 row: anchor.0,
772 column: anchor.1,
773 };
774 let kind = match self.fetch_cell(anchor_reference).as_deref() {
775 Some(Cell::ArrayFormula { kind, .. }) => Some(kind.clone()),
776 _ => None,
777 };
778 let Some(kind) = kind else {
779 // An orphan: its anchor is gone. Nothing will ever write it again.
780 self.record_seen(cell_reference, Seen::Empty);
781 return CalcResult::EmptyCell;
782 };
783 let anchor_state = self.evaluation.cells.get(&key(anchor_reference)).copied();
784 match (kind, anchor_state) {
785 // Written by the anchor in this pass.
786 (_, Some(CellState::Evaluated)) => self.stored_value(cell_reference),
787 // A CSE area is fixed: its anchor is evaluated first and the cell
788 // holds what it wrote. If the anchor is running, this read closes a
789 // cycle, which `evaluate_cell` reports.
790 (ArrayKind::Cse, Some(CellState::Evaluating)) => self.evaluate_cell(anchor_reference),
791 (ArrayKind::Cse, None) => {
792 self.evaluate_cell(anchor_reference);
793 self.stored_value(cell_reference)
794 }
795 // The anchor is running and this read is on its behalf: the area
796 // counts as empty until the anchor commits. If the anchor then
797 // writes this position, its inputs depended on its own output.
798 (ArrayKind::Dynamic, Some(CellState::Evaluating)) => {
799 self.record_seen(cell_reference, Seen::Empty);
800 CalcResult::EmptyCell
801 }
802 // Left over from a previous evaluation: the anchor should have run
803 // before anyone read its cells. Restart with it before the reader.
804 // Outside a pass there is no driver, so the anchor is simply
805 // evaluated now.
806 (ArrayKind::Dynamic, None) => {
807 if self.evaluation.in_pass {
808 // Inside a pass the driver has always set the root.
809 let reader = self.evaluation.root.unwrap_or(anchor_reference);
810 self.evaluation.restart = Some(Restart::StaleRead {
811 anchor: anchor_reference,
812 reader,
813 });
814 CalcResult::EmptyCell
815 } else {
816 self.evaluate_cell(anchor_reference);
817 self.stored_value(cell_reference)
818 }
819 }
820 }
821 }
822
823 fn evaluate_formula_cell(
824 &mut self,
825 cell_reference: CellReferenceIndex,
826 cell: FormulaCell,
827 ) -> CalcResult {
828 let key = key(cell_reference);
829 match self.evaluation.cells.get(&key) {
830 Some(CellState::Evaluating) => {
831 self.mark_cycle(cell_reference);
832 return circular_reference(cell_reference);
833 }
834 Some(CellState::Evaluated) => return self.stored_value(cell_reference),
835 None => {}
836 }
837 self.evaluation.cells.insert(key, CellState::Evaluating);
838 // Everything above this length is this cell and what it needs.
839 let base = self.evaluation.stack.len();
840 self.evaluation.stack.push(cell_reference);
841 if self.too_deep() {
842 // Too deep. The cell is not run: it stays on the stack, still
843 // `Evaluating`, and so will every cell above it as the recursion
844 // unwinds. They are run again from the top once it has.
845 self.evaluation.unwinding = true;
846 return CalcResult::EmptyCell;
847 }
848 let result = self.run_top(cell_reference, cell);
849 if self.evaluation.depth > 0 || !self.evaluation.unwinding {
850 return result;
851 }
852 // This is the outermost formula and the recursion below it got too
853 // deep. It has unwound: nothing is running, and the stack holds this
854 // cell and the cells it is waiting for, the deepest on top, none of
855 // them evaluated. Run them from the top. A cell that completes leaves
856 // the stack and the one below finds it evaluated. A cell that reads
857 // further down than the limit allows unwinds again, with more cells on
858 // the stack, and the replay carries on from the new top. Every cell is
859 // pushed once in a pass, so this ends.
860 self.replay(base);
861 if self.evaluation.restart.is_some() {
862 return CalcResult::EmptyCell;
863 }
864 self.stored_value(cell_reference)
865 }
866
867 /// Whether the recursion has gone as deep as it is allowed to, in formulas
868 /// or in stack. Never true for the outermost formula, which has to run.
869 fn too_deep(&mut self) -> bool {
870 let here = stack_address();
871 if self.evaluation.depth == 0 {
872 self.evaluation.stack_base = here;
873 return false;
874 }
875 self.evaluation.depth >= self.evaluation.max_depth
876 || self.evaluation.stack_base.abs_diff(here) >= self.evaluation.max_stack
877 }
878
879 /// Runs the cells left on the stack above `base` by an unwind, from the
880 /// top, until they are all evaluated or the pass is abandoned.
881 fn replay(&mut self, base: usize) {
882 while self.evaluation.unwinding {
883 self.evaluation.unwinding = false;
884 while self.evaluation.stack.len() > base
885 && !self.evaluation.unwinding
886 && self.evaluation.restart.is_none()
887 {
888 let Some(&top) = self.evaluation.stack.last() else {
889 break;
890 };
891 let formula_cell = self.fetch_cell(top).as_deref().and_then(FormulaCell::of);
892 match formula_cell {
893 Some(cell) => {
894 self.run_top(top, cell);
895 }
896 None => {
897 // Not expected: nothing replaces a formula during a
898 // pass. Let go of it rather than loop on it.
899 self.evaluation.stack.pop();
900 self.evaluation.cells.insert(key(top), CellState::Evaluated);
901 }
902 }
903 }
904 }
905 }
906
907 /// Runs the formula of the cell on top of the stack, stores the result and
908 /// takes the cell off the stack. If the pass is being abandoned nothing is
909 /// stored. If the recursion is unwinding nothing is stored either, and the
910 /// cell stays on the stack, `Evaluating`: it has not been evaluated yet.
911 /// A read that closes a loop through it is then still a cycle.
912 fn run_top(&mut self, cell_reference: CellReferenceIndex, cell: FormulaCell) -> CalcResult {
913 let key = key(cell_reference);
914 // A formula can attach a link to its cell as it runs (HYPERLINK). A
915 // run that was abandoned to an unwind may have attached one on the
916 // strength of values it never really read; this run decides afresh.
917 if !self.links.is_empty() {
918 self.links.remove(&key);
919 }
920 self.evaluation.depth += 1;
921 let result = if self.evaluation.circular.contains(&key) {
922 circular_reference(cell_reference)
923 } else {
924 self.compute_formula(cell_reference, cell.f)
925 };
926 self.evaluation.depth -= 1;
927 if self.evaluation.unwinding {
928 return CalcResult::EmptyCell;
929 }
930 // The cell may have been found on a cycle while it ran.
931 let result = if self.evaluation.circular.contains(&key) {
932 circular_reference(cell_reference)
933 } else {
934 result
935 };
936 let stored = if self.evaluation.restart.is_some() {
937 // Abandoned pass: store nothing.
938 Ok(())
939 } else {
940 self.set_cells_with_result(cell_reference, cell, &result)
941 };
942 self.evaluation.stack.pop();
943 if let Err(message) = stored {
944 // Not expected to happen; keep the cell from being evaluated again.
945 self.evaluation.cells.insert(key, CellState::Evaluated);
946 return CalcResult::new_error(Error::ERROR, cell_reference, message);
947 }
948 if self.evaluation.restart.is_some() {
949 return CalcResult::EmptyCell;
950 }
951 self.evaluation.cells.insert(key, CellState::Evaluated);
952 // Return what was stored, so that a dependent evaluated in this pass
953 // sees the same value it would read from the sheet later: the first
954 // element of a spilled array, or the error the result was turned into.
955 self.stored_value(cell_reference)
956 }
957
958 /// Runs the parsed formula and turns a reference into a value or an array.
959 fn compute_formula(&mut self, cell_reference: CellReferenceIndex, formula: i32) -> CalcResult {
960 // Shares the formula, it does not copy it.
961 let node =
962 Arc::clone(&self.parsed_formulas[cell_reference.sheet as usize][formula as usize].0);
963 match self.evaluate_node_in_context(&node, cell_reference) {
964 CalcResult::Range { left, right } => {
965 if left == right {
966 self.evaluate_cell(left)
967 } else {
968 let height = right.row - left.row + 1;
969 let width = right.column - left.column + 1;
970 if cell_reference.row + height - 1 > LAST_ROW
971 || cell_reference.column + width - 1 > LAST_COLUMN
972 {
973 CalcResult::new_error(
974 Error::SPILL,
975 cell_reference,
976 "Spill would exceed worksheet bounds".to_string(),
977 )
978 } else {
979 CalcResult::Array(self.evaluate_range(left, right))
980 }
981 }
982 }
983 CalcResult::Lambda(_) => CalcResult::new_error(
984 Error::CALC,
985 cell_reference,
986 "A LAMBDA was returned but not called".to_string(),
987 ),
988 result => result,
989 }
990 }
991
992 /// The value currently stored at a position, empty if there is no cell.
993 fn stored_value(&self, cell_reference: CellReferenceIndex) -> CalcResult {
994 match self.fetch_cell(cell_reference) {
995 Some(cell) => self.get_cell_value(&cell, cell_reference),
996 None => CalcResult::EmptyCell,
997 }
998 }
999
1000 /// A read closed a loop at `from`: every cell on the stack from `from` to the
1001 /// top is on the loop. They all store `#CIRC!`, whatever their formula would
1002 /// have made of the error, so that the verdict does not depend on where the
1003 /// recursion entered the loop.
1004 fn mark_cycle(&mut self, from: CellReferenceIndex) {
1005 let Some(start) = self.evaluation.stack.iter().rposition(|c| *c == from) else {
1006 return;
1007 };
1008 for cell in &self.evaluation.stack[start..] {
1009 self.evaluation.circular.insert(key(*cell));
1010 }
1011 }
1012
1013 /// Records what the formula being evaluated found at a position, together
1014 /// with the root of the current recursion, the cell the driver is
1015 /// evaluating. The first record of each kind is kept. Reads made outside
1016 /// a pass are nobody's dependency.
1017 pub(crate) fn record_seen(&mut self, position: CellReferenceIndex, seen: Seen) {
1018 self.evaluation.record_seen(position, seen);
1019 }
1020
1021 /// Records that the formula being evaluated depends on every position of a
1022 /// rectangle and found them all empty, on behalf of the root. Used for the
1023 /// part of a whole-column or whole-row range that a walk skips because it
1024 /// lies outside the sheet's used area (`clip_to_used_area`).
1025 pub(crate) fn record_seen_empty_range(
1026 &mut self,
1027 sheet: u32,
1028 row1: i32,
1029 column1: i32,
1030 row2: i32,
1031 column2: i32,
1032 ) {
1033 if !self.evaluation.in_pass || row1 > row2 || column1 > column2 {
1034 return;
1035 }
1036 if let Some(root) = self.evaluation.root {
1037 self.evaluation.seen_ranges.push(SeenRange {
1038 root,
1039 sheet,
1040 row1,
1041 column1,
1042 row2,
1043 column2,
1044 });
1045 }
1046 }
1047
1048 /// Clips a whole-column or whole-row range to the sheet's used area, so
1049 /// that a function walking it visits only the cells that can hold
1050 /// something. Returns the bottom-right corner to walk to. The cells the
1051 /// walk will skip are put on record as read empty first: the formula
1052 /// depends on them, and a spill into them must restart the pass, however
1053 /// the used area changes while the walk runs.
1054 pub(crate) fn clip_to_used_area(
1055 &mut self,
1056 sheet: u32,
1057 row1: i32,
1058 column1: i32,
1059 row2: i32,
1060 column2: i32,
1061 ) -> Result<(i32, i32), String> {
1062 let whole_rows = row1 == 1 && row2 == LAST_ROW;
1063 let whole_columns = column1 == 1 && column2 == LAST_COLUMN;
1064 if !whole_rows && !whole_columns {
1065 return Ok((row2, column2));
1066 }
1067 let dimension = self
1068 .workbook
1069 .worksheet(sheet)
1070 .map_err(|_| format!("Invalid worksheet index: '{sheet}'"))?
1071 .dimension();
1072 let last_row = if whole_rows { dimension.max_row } else { row2 };
1073 let last_column = if whole_columns {
1074 dimension.max_column
1075 } else {
1076 column2
1077 };
1078 // The remainder: the rows below the used area over the whole width,
1079 // and the columns beyond it within the rows that are walked.
1080 self.record_seen_empty_range(sheet, last_row + 1, column1, row2, column2);
1081 self.record_seen_empty_range(sheet, row1, last_column + 1, last_row, column2);
1082 Ok((last_row, last_column))
1083 }
1084
1085 /// Checks, on behalf of the dynamic anchor about to commit, whether writing
1086 /// spill cells at `writes` and removing its own spill cells at `clears` would
1087 /// contradict what a formula read earlier in this pass. If so, the pass is
1088 /// abandoned so that it can start again with the anchor first, and `true`
1089 /// is returned: the anchor must not write anything. When every contradicted
1090 /// read was made on the anchor's own behalf, the anchor is circular, unless
1091 /// only the removals contradict: then the cells were stale, and they go.
1092 pub(crate) fn spill_contradicts_a_read(
1093 &mut self,
1094 anchor: CellReferenceIndex,
1095 writes: &[CellKey],
1096 clears: &[CellKey],
1097 ) -> bool {
1098 if !self.evaluation.in_pass {
1099 return false;
1100 }
1101 let seen = &self.evaluation.seen;
1102 let ranges = &self.evaluation.seen_ranges;
1103 let contradicted_by_writes: Vec<CellReferenceIndex> = writes
1104 .iter()
1105 .filter_map(|p| seen.get(p).and_then(|record| record.empty))
1106 .chain(writes.iter().flat_map(|p| {
1107 ranges
1108 .iter()
1109 .filter(move |range| range.contains(*p))
1110 .map(|range| range.root)
1111 }))
1112 .collect();
1113 let contradicted_by_clears: Vec<CellReferenceIndex> = clears
1114 .iter()
1115 .filter_map(|p| seen.get(p).and_then(|record| record.occupied))
1116 .collect();
1117 if contradicted_by_writes.is_empty() && contradicted_by_clears.is_empty() {
1118 return false;
1119 }
1120 let mut readers: Vec<CellReferenceIndex> = Vec::new();
1121 for root in contradicted_by_writes.iter().chain(&contradicted_by_clears) {
1122 if *root != anchor && !readers.contains(root) {
1123 readers.push(*root);
1124 }
1125 }
1126 self.evaluation.restart = Some(if !readers.is_empty() {
1127 Restart::Conflict { anchor, readers }
1128 } else if contradicted_by_writes.is_empty() {
1129 Restart::StaleCells(anchor, clears.to_vec())
1130 } else {
1131 Restart::SelfContradiction(anchor)
1132 });
1133 true
1134 }
1135}
1136
1137fn circular_reference(cell_reference: CellReferenceIndex) -> CalcResult {
1138 CalcResult::new_error(
1139 Error::CIRC,
1140 cell_reference,
1141 "Circular reference detected".to_string(),
1142 )
1143}