Skip to main content

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}