Skip to main content

vla_lang/calc/
shape.rs

1//! The plan of shapes (KERNEL.22, 2026-10-09): every formula of a grid
2//! parsed once per shape, each formula cell evaluated at its offset from
3//! the shape's first cell, and the order of evaluation read off the parse.
4//!
5//! An engine evaluates every formula every frame, so a parse at each
6//! evaluation, as [`super::Calc`] makes, costs most of a frame (Life's
7//! 62,964 formulas: about two seconds of a frame's 2.5, measured 2026-10-09),
8//! and a parse of every cell held once costs memory no browser holds (8.9 KB
9//! for one of Life's formulas, 562 MB for all of them). A shape is the
10//! answer: a shared formula's text is read once at its master and its other
11//! cells are the same formula moved, and two plain formula cells whose R1C1
12//! text is the same (`refers::r1c1`, the reference's own normal form) are
13//! the same formula moved too, so a save of Life, 62,964 plain formula rows,
14//! loads back to one shape. A cell at an offset from its shape's first cell
15//! is evaluated with every reference moved by that offset, which is what the
16//! text `refers::shift_a1_references` moves would parse to
17//! ([`super::formula::RefExpr::moved`]); the references inside a defined
18//! name's text never move.
19//!
20//! [`super::Calc`] keeps its plan of texts: it evaluates each formula once,
21//! so a parse held would buy it nothing. The plan here is an engine's, kept
22//! for the life of a grid and read again when a write changes a formula; a
23//! shape's references find the sheets they name once, as it is placed,
24//! among the grid's and its twins, which are fixed for the grid's life
25//! (KERNEL.20), where a reference found its sheet by name at every
26//! evaluation.
27//!
28//! The order is by level, a cell's being one past the deepest it reads, then
29//! by shape and cell, which computes the frame any order after what each cell
30//! reads computes; its runs are the cells of a shape that stand together in
31//! it, on one sheet, reading none of one another, and an engine evaluates
32//! each run at once, node by node (`super::vector`, KERNEL.20). What each
33//! shape reads is found with the order, so that an engine follows a changed
34//! cell to the cells of the order that read it and computes those alone
35//! (`super::reach`, KERNEL.20).
36//!
37//! The plan is printed as rows on request ([`Shapes::census`], KERNEL.30,
38//! `super::census`): each shape's cells, the levels of the order it spans,
39//! its runs and the paths its reads take, so that a grid's cost is read
40//! before a frame runs. Of all that, the plan keeps for it only where each
41//! level of the order ends, the array its counting sort already makes.
42
43use std::collections::HashMap;
44
45use crate::fx::FxHashMap;
46use crate::messages::{raise, Refusal};
47use crate::refers::{self, quote_sheet};
48use crate::sheet::{cell_ref, shared_masters, strip_future_prefixes, Content, Workbook};
49
50use super::eval::{Computed, Ctx, Env, Library, Reason};
51use super::formula::{self, Expr, Read, RefExpr};
52use super::graph::{
53    self, areas_at, cells_in, dependencies, is_formula, Area, CellId, Extent, Names,
54};
55use super::reach::{Positions, Reach};
56use super::vector::{self, Lanes, Repeats};
57
58/// One formula, parsed once, and the cell it was read at.
59#[derive(Clone, Debug)]
60pub struct Shape {
61    /// The text at the shape's first cell, without its `=`, as the formula
62    /// bar shows it.
63    pub text: String,
64    /// The parse, or the construct it stopped at.
65    pub expr: Result<Expr, String>,
66    /// The cell the text was read at; every other cell of the shape is at
67    /// an offset from it.
68    pub anchor: CellId,
69    /// The functions the parse calls that the library does not hold,
70    /// sorted, each once: what a load refuses, read once a shape.
71    missing: Vec<String>,
72    /// Whether a cell of the shape may stand for a text the parse does not
73    /// read: the text holds a 3D span (`Jan:Dec!A1`) or a broken reference
74    /// with a cell after it (`#REF!A1`), each of which the mover writes as
75    /// a construct when it moves off the sheet (`Jan:Dec!#REF!`,
76    /// `#REF!#REF!`). Found 2026-10-09 by holding the offset to the moved
77    /// text's parse over the refers fixture.
78    pub(crate) risky: bool,
79    /// The most buffers an evaluation of the parse at many cells holds at
80    /// once, found once (KERNEL.20, `vector::holds`), a lane for each of its
81    /// repeated parts with them (KERNEL.27).
82    holds: (usize, usize),
83    /// The parts the parse writes more than once that a run computes once
84    /// (KERNEL.27, `vector::Repeats`).
85    repeats: Repeats,
86}
87
88impl Shape {
89    /// The most buffers of each kind a run of the shape holds at once.
90    #[cfg(test)]
91    pub(crate) fn holds(&self) -> (usize, usize) {
92        self.holds
93    }
94
95    /// The functions the parse calls that the library does not hold, sorted,
96    /// each once (KERNEL.30's census prints them).
97    pub(crate) fn missing(&self) -> &[String] {
98        &self.missing
99    }
100
101    /// The construct a cell at this offset stands for that the shape's
102    /// first cell does not, as the moved text's parse names it; `None` when
103    /// the moved text reads as the shape does, which for a shape that is not
104    /// risky it always does (the refers fixture moved to every edge of the
105    /// sheet holds that).
106    pub fn unread_at(&self, d_row: i64, d_col: i64) -> Option<String> {
107        if !self.risky || (d_row == 0 && d_col == 0) || self.expr.is_err() {
108            return None;
109        }
110        formula::parse(&refers::shift_a1_references(&self.text, d_row, d_col)).err()
111    }
112}
113
114/// Where a formula cell's formula comes from: its shape, and the cell's
115/// offset from the shape's first cell.
116#[derive(Clone, Copy, Debug, PartialEq, Eq)]
117pub struct Place {
118    pub shape: usize,
119    pub d_row: i64,
120    pub d_col: i64,
121}
122
123/// The plan: every formula cell placed on a shape, the order, the cycles.
124#[derive(Clone, Debug, Default)]
125pub struct Shapes {
126    shapes: Vec<Shape>,
127    /// A plain formula's shape, by its R1C1 text.
128    by_r1c1: HashMap<String, usize>,
129    /// A shared formula's shape, by its sheet and its index.
130    by_shared: HashMap<(usize, u32), usize>,
131    /// Every formula cell's place, looked up at each of its evaluations,
132    /// under the crate's own hash (KERNEL.25).
133    places: FxHashMap<CellId, Place>,
134    /// A cell of a risky shape whose own text the parse does not read: the
135    /// construct it names, found once when the cell is placed.
136    unread: FxHashMap<CellId, String>,
137    order: Vec<CellId>,
138    cycles: Vec<Vec<CellId>>,
139    /// The runs of the order an engine evaluates at once (KERNEL.20), by
140    /// their first position: each of at least [`RUN`] cells of one parsed
141    /// shape that is not risky, on one sheet, consecutive in the order, none
142    /// reading another.
143    runs: Vec<Run>,
144    /// What each shape reads, found with the order (KERNEL.20).
145    reach: Reach,
146    /// Where each level of the order ends, by level (KERNEL.30): the
147    /// position after its last cell, the order's own counting sort kept
148    /// rather than dropped, so that the census finds each cell's level
149    /// without planning again.
150    level_ends: Vec<u32>,
151}
152
153/// A run of the order: its cells are `order[start..end]`, of one shape.
154#[derive(Clone, Copy, Debug, PartialEq, Eq)]
155struct Run {
156    start: usize,
157    end: usize,
158    shape: usize,
159}
160
161/// The fewest cells a run is evaluated at once for; fewer are evaluated one
162/// by one, as every cell was before (KERNEL.20).
163pub(crate) const RUN: usize = 4;
164
165impl Shapes {
166    /// A plan with nothing in it.
167    pub fn new() -> Shapes {
168        Shapes::default()
169    }
170
171    /// Every formula cell of the sheets `0..sheets` placed, the shapes
172    /// parsed, and the order found. The plan is made in a box: every formula
173    /// is parsed beneath this frame, the deepest a load reaches, which
174    /// `VLA.1`'s limits pin on a stack of 1 MiB, and the plan held here by
175    /// value would stand on it, 480 bytes since it kept its level ends
176    /// (KERNEL.30).
177    pub fn of(
178        model: &Workbook,
179        sheets: usize,
180        library: &Library,
181        names: &Names,
182        extents: &[Extent],
183    ) -> Shapes {
184        let mut s: Box<Shapes> = Box::default();
185        for (si, sheet) in model.sheets.iter().enumerate().take(sheets) {
186            let masters = shared_masters(sheet);
187            for (&(row, col), cell) in &sheet.cells {
188                s.place_with(model, (si, row, col), &cell.content, &masters, library);
189            }
190        }
191        s.plan(model, names, extents);
192        *s
193    }
194
195    /// Place one cell after a write: its old place forgotten, and a new one
196    /// found when it holds a formula. The order waits for [`Shapes::plan`].
197    pub fn replace(&mut self, model: &Workbook, id: CellId, library: &Library) {
198        self.places.remove(&id);
199        self.unread.remove(&id);
200        let Some(sheet) = model.sheets.get(id.0) else {
201            return;
202        };
203        let Some(cell) = sheet.cells.get(&(id.1, id.2)) else {
204            return;
205        };
206        if !is_formula(&cell.content) {
207            return;
208        }
209        let masters = shared_masters(sheet);
210        self.place_with(model, id, &cell.content, &masters, library);
211    }
212
213    fn place_with(
214        &mut self,
215        model: &Workbook,
216        id: CellId,
217        content: &Content,
218        masters: &HashMap<u32, (&str, u32, u32)>,
219        library: &Library,
220    ) {
221        let (si, row, col) = id;
222        let (shape, anchor) = match content {
223            Content::SharedMaster { si: index, .. } | Content::SharedChild { si: index } => {
224                let Some(&(text, m_row, m_col)) = masters.get(index) else {
225                    // A child whose master is gone has no text; the machine
226                    // never leaves one (a write over a master gives its
227                    // other cells their own text first).
228                    return;
229                };
230                let anchor = (si, m_row, m_col);
231                let key = (si, *index);
232                let shape = match self.by_shared.get(&key) {
233                    Some(&s) => s,
234                    None => {
235                        let s = self.add(model, strip_future_prefixes(text), anchor, library);
236                        self.by_shared.insert(key, s);
237                        s
238                    }
239                };
240                (shape, anchor)
241            }
242            Content::Formula(t) | Content::DynamicFormula(t) => {
243                let text = strip_future_prefixes(t);
244                let key = refers::r1c1(&text, row, col);
245                match self.by_r1c1.get(&key) {
246                    Some(&s) => (s, self.shapes[s].anchor),
247                    None => {
248                        let s = self.add(model, text, id, library);
249                        self.by_r1c1.insert(key, s);
250                        (s, id)
251                    }
252                }
253            }
254            _ => return,
255        };
256        let (d_row, d_col) = (
257            i64::from(row) - i64::from(anchor.1),
258            i64::from(col) - i64::from(anchor.2),
259        );
260        self.places.insert(
261            id,
262            Place {
263                shape,
264                d_row,
265                d_col,
266            },
267        );
268        match self.shapes[shape].unread_at(d_row, d_col) {
269            Some(construct) => self.unread.insert(id, construct),
270            None => self.unread.remove(&id),
271        };
272    }
273
274    fn add(&mut self, model: &Workbook, text: String, anchor: CellId, library: &Library) -> usize {
275        let mut expr = formula::parse(&text);
276        if let Ok(e) = expr.as_mut() {
277            find_sheets(e, model);
278        }
279        // The calls as the text's tokens hold them, parsed or not, as
280        // `Calc::missing_functions` reads them.
281        let mut missing: Vec<String> = formula::calls(&text)
282            .into_iter()
283            .filter(|name| !library.has(name))
284            .collect();
285        let risky = refers::scan(&text).iter().any(|r| {
286            r.kind == refers::Kind::ThreeD
287                || (r.kind == refers::Kind::Broken && r.shape != refers::Shape::None)
288        });
289        missing.sort_unstable();
290        missing.dedup();
291        self.push(text, expr, anchor, missing, risky, library)
292    }
293
294    /// A shape kept, with what an evaluation of it at many cells holds found
295    /// once (KERNEL.20, `vector::holds`) and the parts it writes more than
296    /// once (KERNEL.27, `vector::Repeats`), in a frame of its own: `add`'s
297    /// frame stands beneath the parse, the deepest a load reaches, which
298    /// `VLA.1`'s limits pin on a stack of 1 MiB.
299    #[inline(never)]
300    fn push(
301        &mut self,
302        text: String,
303        expr: Result<Expr, String>,
304        anchor: CellId,
305        missing: Vec<String>,
306        risky: bool,
307        library: &Library,
308    ) -> usize {
309        let (holds, repeats) = match &expr {
310            Ok(e) => {
311                let repeats = Repeats::of(library, e);
312                let (values, positions) = vector::holds(library, e);
313                ((values + repeats.parts(), positions), repeats)
314            }
315            Err(_) => ((0, 0), Repeats::default()),
316        };
317        self.shapes.push(Shape {
318            text,
319            expr,
320            anchor,
321            missing,
322            risky,
323            holds,
324            repeats,
325        });
326        self.shapes.len() - 1
327    }
328
329    /// The order found again from the places: every formula cell's edges
330    /// read off its shape's parse at its offset (a defined name's text read
331    /// as `graph::dependencies` reads it), then Tarjan's components, as
332    /// `graph::plan` finds them for a plan of texts.
333    ///
334    /// Each shape's references are read off its parse once (KERNEL.33,
335    /// asked by Alonzo's playtest pass of Sand, whose load spent 341 ms of
336    /// 491 finding 2.37 million edges a cell at a time): each reference once,
337    /// one to a sheet holding no formula left out, one every `$` holds found
338    /// once for the whole shape; each cell then moves only the rest. A cell's
339    /// vertex is found as reach finds a position, by an array over a sheet
340    /// whose formulas stand close together, and the edges are one flat array.
341    /// Every cell's edges are the ones a walk of its parse at its offset
342    /// finds, sorted and each once, so the order, its runs and its cycles
343    /// are the same.
344    pub fn plan(&mut self, model: &Workbook, names: &Names, extents: &[Extent]) {
345        let mut ids: Vec<CellId> = self.places.keys().copied().collect();
346        ids.sort_unstable();
347        let index = Positions::of(&ids, model.sheets.len());
348        let mut holds = vec![false; model.sheets.len()];
349        for id in &ids {
350            holds[id.0] = true;
351        }
352        let vertices = Vertices {
353            model,
354            extents,
355            index: &index,
356            holds: &holds,
357        };
358        let reads: Vec<ShapeReads> = self
359            .shapes
360            .iter()
361            .map(|shape| vertices.reads_of(shape))
362            .collect();
363        // Every cell's edges in one array, cell `v`'s at
364        // `targets[offsets[v]..offsets[v + 1]]`, where a vector of edges was
365        // made for each cell.
366        let n = ids.len();
367        let mut offsets: Vec<usize> = Vec::with_capacity(n + 1);
368        let mut targets: Vec<u32> = Vec::with_capacity(n * 8);
369        offsets.push(0);
370        let mut shape_of: Vec<usize> = Vec::with_capacity(n);
371        let mut deps: Vec<CellId> = Vec::new();
372        let mut edges: Vec<u32> = Vec::new();
373        for id in &ids {
374            edges.clear();
375            deps.clear();
376            let p = self.places[id];
377            shape_of.push(p.shape);
378            let shape = &self.shapes[p.shape];
379            let read = !self.unread.contains_key(id);
380            match &shape.expr {
381                Ok(e) if read => {
382                    let r = &reads[p.shape];
383                    edges.extend_from_slice(&r.fixed);
384                    for re in &r.moving {
385                        let Ok(areas) = areas_at(model, id.0, re, p.d_row, p.d_col) else {
386                            continue;
387                        };
388                        for a in &areas {
389                            vertices.formulas_in(a, &mut edges);
390                        }
391                    }
392                    if r.named {
393                        e.each_read(&mut |r| {
394                            if let Read::Name { name, sheet } = r {
395                                let scope = sheet.and_then(|s| model.find_sheet(s));
396                                if let Some(t) = names.resolve(name, scope, id.0) {
397                                    dependencies(
398                                        model,
399                                        names,
400                                        extents,
401                                        scope.unwrap_or(id.0),
402                                        t,
403                                        &mut deps,
404                                    );
405                                }
406                            }
407                        });
408                    }
409                }
410                // A text this version does not parse, here or at this
411                // offset: its edges as the text scan reads them, so that a
412                // cycle through it is still found.
413                _ => {
414                    let moved = refers::shift_a1_references(&shape.text, p.d_row, p.d_col);
415                    dependencies(model, names, extents, id.0, &moved, &mut deps);
416                }
417            }
418            for d in &deps {
419                if let Some(v) = index.get(d.0, d.1, d.2) {
420                    edges.push(vertex(v));
421                }
422            }
423            edges.sort_unstable();
424            edges.dedup();
425            targets.extend_from_slice(&edges);
426            offsets.push(targets.len());
427        }
428        let (members, starts) = graph::tarjan_flat(&offsets, &targets);
429        let shape_at =
430            self.order_and_runs(&ids, (&offsets, &targets), &shape_of, (&members, &starts));
431        let places = &self.places;
432        let formula = |id: CellId| places.contains_key(&id);
433        self.reach = Reach::of(model, &self.shapes, &self.order, shape_at, &formula);
434    }
435
436    /// The order, the cycles and the runs, from the cells, each one's edges
437    /// (one flat array, as [`graph::tarjan_flat`] reads them) and shape, and
438    /// the components, which come each after everything it reads; the shape
439    /// of each cell of the order answered.
440    ///
441    /// Each cell's level is 0 when it reads no formula of the plan, else one
442    /// past the deepest it reads, its component's. The order is by level,
443    /// then shape, then cell, sheet first: any order that puts a cell after
444    /// what it reads computes the same frame, and in this one the cells of a
445    /// shape that read none of one another stand together, whether the shape
446    /// fills rows or a column beside others (KERNEL.20).
447    fn order_and_runs(
448        &mut self,
449        ids: &[CellId],
450        (offsets, targets): (&[usize], &[u32]),
451        shape_of: &[usize],
452        (members, starts): (&[u32], &[usize]),
453    ) -> Vec<u32> {
454        self.order.clear();
455        self.cycles.clear();
456        self.runs.clear();
457        let adj = |v: usize| &targets[offsets[v]..offsets[v + 1]];
458        let mut component = vec![0; ids.len()];
459        let mut level = vec![0; ids.len()];
460        let mut alone = vec![false; ids.len()];
461        for (c, ends) in starts.windows(2).enumerate() {
462            let comp = &members[ends[0]..ends[1]];
463            for &v in comp {
464                component[v as usize] = c;
465            }
466            let deepest = comp
467                .iter()
468                .flat_map(|&v| adj(v as usize))
469                .filter(|&&u| component[u as usize] != c)
470                .map(|&u| level[u as usize] + 1)
471                .max()
472                .unwrap_or(0);
473            for &v in comp {
474                level[v as usize] = deepest;
475            }
476            if comp.len() == 1 && !adj(comp[0] as usize).contains(&comp[0]) {
477                alone[comp[0] as usize] = true;
478            } else {
479                let mut cells: Vec<CellId> = comp.iter().map(|&i| ids[i as usize]).collect();
480                cells.sort_unstable();
481                self.cycles.push(cells);
482            }
483        }
484        self.cycles.sort();
485        // By shape and then by level, each pass keeping the order before it,
486        // so by level, shape and cell, a cell's sheet first among its own;
487        // the second pass's counts are where each level ends (KERNEL.30).
488        let cells: Vec<usize> = (0..ids.len()).filter(|&v| alone[v]).collect();
489        let (cells, _) = by_key(&cells, self.shapes.len(), |v| shape_of[v]);
490        let levels = level.iter().max().map_or(0, |deepest| deepest + 1);
491        let (order, ends) = by_key(&cells, levels, |v| level[v]);
492        self.level_ends = ends;
493        // The run each cell of the order joined, by the run's first position:
494        // a cell joins the run before it when it is of the run's shape and
495        // sheet and reads no cell of the run, so that the run's cells may be
496        // computed in any order, all at once among them (KERNEL.20).
497        let mut joined = vec![usize::MAX; ids.len()];
498        let mut run = Run {
499            start: 0,
500            end: 0,
501            shape: usize::MAX,
502        };
503        let mut shape_at = Vec::with_capacity(order.len());
504        for v in order {
505            let (id, shape) = (ids[v], shape_of[v]);
506            let pos = self.order.len();
507            let joins = shape == run.shape
508                && id.0 == self.order[run.start].0
509                && adj(v).iter().all(|&u| joined[u as usize] != run.start);
510            if !joins {
511                self.keep(run);
512                run = Run {
513                    start: pos,
514                    end: pos,
515                    shape,
516                };
517            }
518            joined[v] = run.start;
519            run.end = pos + 1;
520            self.order.push(id);
521            shape_at.push(shape as u32);
522        }
523        self.keep(run);
524        shape_at
525    }
526
527    /// A run of the order kept to be evaluated at once when it is long
528    /// enough and its shape parsed and not risky, so that every cell of it
529    /// reads as the shape does (KERNEL.20). (A risky shape's cells all read
530    /// as it does in a grid that loaded, every other being refused; the
531    /// shape is evaluated cell by cell all the same.)
532    fn keep(&mut self, run: Run) {
533        let Some(shape) = self.shapes.get(run.shape) else {
534            return;
535        };
536        if run.end - run.start >= RUN && !shape.risky && shape.expr.is_ok() {
537            self.runs.push(run);
538        }
539    }
540
541    /// How many cells of the order are in runs.
542    #[cfg(test)]
543    pub(crate) fn cells_in_runs(&self) -> usize {
544        self.runs.iter().map(|r| r.end - r.start).sum()
545    }
546
547    /// The run a position of the order is in, as its shape and the position
548    /// after its last cell; `None` for a cell evaluated alone.
549    pub(crate) fn run_at(&self, pos: usize) -> Option<(usize, usize)> {
550        let after = self.runs.partition_point(|r| r.start <= pos);
551        let run = self.runs.get(after.checked_sub(1)?)?;
552        (pos < run.end).then_some((run.shape, run.end))
553    }
554
555    /// Cells of one run, a part of it or the whole, evaluated at once
556    /// against the grid as `env` shows it (KERNEL.20, `super::vector`), each
557    /// cell's answer pushed to `out` in order: what [`Shapes::evaluate`]
558    /// answers for each, the environment told each cell as it is computed.
559    pub(crate) fn evaluate_run(
560        &self,
561        env: &dyn Env,
562        library: &Library,
563        shape: usize,
564        cells: &[CellId],
565        lanes: &mut Lanes,
566        out: &mut Vec<Computed>,
567    ) {
568        let s = &self.shapes[shape];
569        // The paths the run takes are noted against its shape, in a test
570        // (KERNEL.30's oracle: the census names each of them).
571        #[cfg(test)]
572        vector::taken::shape(shape);
573        match &s.expr {
574            Ok(e) => {
575                let held = s.holds;
576                vector::evaluate(
577                    env, library, e, held, &s.repeats, s.anchor, cells, lanes, out,
578                );
579            }
580            // A run holds a parsed shape; were it not, each cell alone.
581            Err(_) => out.extend(cells.iter().map(|&id| {
582                env.enter(id);
583                self.evaluate(env, library, id)
584            })),
585        }
586    }
587
588    /// The formula cells in the order of evaluation, each after everything
589    /// it reads; the cells of a cycle are not in it.
590    pub fn order(&self) -> &[CellId] {
591        &self.order
592    }
593
594    /// What each shape reads, and where the cells of the order stand
595    /// (KERNEL.20).
596    pub(crate) fn reach(&self) -> &Reach {
597        &self.reach
598    }
599
600    /// Every cycle, each its cells sorted, the cycles by their first cell.
601    pub fn cycles(&self) -> &[Vec<CellId>] {
602        &self.cycles
603    }
604
605    /// How many formula cells are placed.
606    pub fn formulas(&self) -> usize {
607        self.places.len()
608    }
609
610    /// How many shapes the formulas make.
611    pub fn shapes(&self) -> usize {
612        self.shapes.len()
613    }
614
615    /// Whether a cell holds a placed formula.
616    pub fn holds(&self, id: CellId) -> bool {
617        self.places.contains_key(&id)
618    }
619
620    /// A formula cell's place.
621    pub fn place(&self, id: CellId) -> Option<Place> {
622        self.places.get(&id).copied()
623    }
624
625    /// A shape, by its index.
626    pub fn shape(&self, index: usize) -> Option<&Shape> {
627        self.shapes.get(index)
628    }
629
630    /// The plan printed as rows, the census (KERNEL.30): a `(plan ...)` row
631    /// of totals, a `(readers ...)` row for each sheet whose changed cells
632    /// are checked against readers, then a `(shape ...)` row for each shape
633    /// a cell holds, each read named by the path an engine's machine takes
634    /// for it (`super::census`). Every sheet is read as a frame's own, as a
635    /// workbook's are: this is the census of a plan [`Shapes::of`] made over
636    /// a grid with no `.last` twin. A machine's, its twins read as it keeps
637    /// them, is `Machine::census`. Computed when asked from the plan as it
638    /// stands, which keeps nothing for it but where each level ends.
639    pub fn census(&self, model: &Workbook, library: &Library) -> String {
640        super::census::rows(self, model, library, &|_| false)
641    }
642
643    /// Where each level of the order ends, by level (KERNEL.30).
644    pub(crate) fn level_ends(&self) -> &[u32] {
645        &self.level_ends
646    }
647
648    /// Each run of the order as its first position, the position after its
649    /// last cell and its shape, by first position (KERNEL.30).
650    pub(crate) fn run_spans(&self) -> impl Iterator<Item = (usize, usize, usize)> + '_ {
651        self.runs.iter().map(|r| (r.start, r.end, r.shape))
652    }
653
654    /// Every formula cell and its place, in no order (KERNEL.30).
655    pub(crate) fn places(&self) -> impl Iterator<Item = (CellId, Place)> + '_ {
656        self.places.iter().map(|(&id, &p)| (id, p))
657    }
658
659    /// Every shape the plan holds, a shape whose cells a write has since
660    /// replaced among them (KERNEL.30).
661    pub(crate) fn every_shape(&self) -> &[Shape] {
662        &self.shapes
663    }
664
665    /// One formula cell evaluated against the grid as `env` shows it, the
666    /// cell being computed being `env.here()`.
667    pub fn evaluate(&self, env: &dyn Env, library: &Library, id: CellId) -> Computed {
668        let Some(p) = self.places.get(&id) else {
669            return Computed::NotComputed(Reason::Construct(String::new()));
670        };
671        let shape = &self.shapes[p.shape];
672        if let Some(construct) = self.unread.get(&id) {
673            return Computed::NotComputed(Reason::Construct(construct.clone()));
674        }
675        match &shape.expr {
676            Err(construct) => Computed::NotComputed(Reason::Construct(construct.clone())),
677            Ok(e) => {
678                let ctx = Ctx::at(env, library, p.d_row, p.d_col);
679                match ctx.eval(e).and_then(|arg| ctx.one(&arg)) {
680                    Ok(v) => Computed::Value(v.settled()),
681                    Err(reason) => Computed::NotComputed(reason),
682                }
683            }
684        }
685    }
686
687    /// The first thing that stops the grid from being stepped, in the order
688    /// [`super::Calc::first_refusal`] asks it: a cycle (`calc-cycle`), the
689    /// first cell calling a function the library does not hold
690    /// (`calc-function-not-computed`), the first cell whose text this
691    /// version does not read (`calc-construct-not-read`); with the cell the
692    /// refusal stands on, for its line. `only` limits the second and third
693    /// to the cells a write placed.
694    pub fn first_refusal(
695        &self,
696        model: &Workbook,
697        only: Option<&[CellId]>,
698    ) -> Option<(CellId, Refusal)> {
699        if let Some(cycle) = self.cycles.first() {
700            let cells: Vec<String> = cycle.iter().map(|id| spell_cell(model, *id)).collect();
701            return Some((
702                cycle[0],
703                raise("calc-cycle", &[("cells", &cells.join(", "))]),
704            ));
705        }
706        let mut ids: Vec<CellId> = match only {
707            Some(cells) => cells
708                .iter()
709                .copied()
710                .filter(|id| self.places.contains_key(id))
711                .collect(),
712            None => self.places.keys().copied().collect(),
713        };
714        ids.sort_unstable();
715        for id in &ids {
716            let shape = &self.shapes[self.places[id].shape];
717            if let Some(name) = shape.missing.first() {
718                return Some((
719                    *id,
720                    raise(
721                        "calc-function-not-computed",
722                        &[("cell", &spell_cell(model, *id)), ("function", name)],
723                    ),
724                ));
725            }
726        }
727        for id in &ids {
728            let p = self.places[id];
729            let shape = &self.shapes[p.shape];
730            let label = match (&shape.expr, self.unread.get(id).cloned()) {
731                (_, Some(unread)) => Some(unread),
732                // The construct as the cell's own text spells it.
733                (Err(construct), None) if p.d_row == 0 && p.d_col == 0 => Some(construct.clone()),
734                (Err(construct), None) => {
735                    let moved = refers::shift_a1_references(&shape.text, p.d_row, p.d_col);
736                    Some(
737                        formula::parse(&moved)
738                            .err()
739                            .unwrap_or_else(|| construct.clone()),
740                    )
741                }
742                (Ok(_), None) => None,
743            };
744            if let Some(label) = label {
745                return Some((
746                    *id,
747                    formula::unread_refusal(&spell_cell(model, *id), &label),
748                ));
749            }
750        }
751        None
752    }
753}
754
755/// Cells ordered by a key below `keys`, cells of one key in the order they
756/// came in: a counting sort, a pass over the cells and one over the keys,
757/// for the order of a plan (KERNEL.20); and, by key, where its cells end in
758/// that order, which the sort's own counts hold once it is done (KERNEL.30).
759/// A plan's cells are positions of an order a `u32` holds, as its reach's
760/// are.
761fn by_key(cells: &[usize], keys: usize, key: impl Fn(usize) -> usize) -> (Vec<usize>, Vec<u32>) {
762    let mut start = vec![0u32; keys + 1];
763    for &v in cells {
764        start[key(v) + 1] += 1;
765    }
766    for k in 0..keys {
767        start[k + 1] += start[k];
768    }
769    let mut out = vec![0; cells.len()];
770    for &v in cells {
771        let k = key(v);
772        out[start[k] as usize] = v;
773        start[k] += 1;
774    }
775    start.truncate(keys);
776    (out, start)
777}
778
779/// A formula cell's vertex, its position among a plan's sorted cells, as an
780/// edge holds it: a plan's cells are positions a `u32` holds, as its
781/// order's are.
782fn vertex(position: usize) -> u32 {
783    position as u32
784}
785
786/// A plan's formula cells as the vertices of its edges (KERNEL.33): each
787/// cell's position among the plan's sorted cells, and whether each sheet
788/// holds any formula.
789struct Vertices<'a> {
790    model: &'a Workbook,
791    extents: &'a [Extent],
792    index: &'a Positions,
793    holds: &'a [bool],
794}
795
796/// A shape's references as its plan reads them, once a shape (KERNEL.33):
797/// the vertices those every `$` holds name, the same at every cell, sorted
798/// and each once; the references each cell moves, each once; and whether
799/// the parse reads a name, which each cell resolves from its own sheet.
800struct ShapeReads<'e> {
801    fixed: Vec<u32>,
802    moving: Vec<&'e RefExpr>,
803    named: bool,
804}
805
806impl Vertices<'_> {
807    /// The formula cells inside an area, each by its vertex, to `out`: by
808    /// the array where the sheet's formulas stand close enough together to
809    /// be held as one, else by the sheet's tree; none on a sheet holding no
810    /// formula, from which no edge comes.
811    fn formulas_in(&self, a: &Area, out: &mut Vec<u32>) {
812        if !self.holds.get(a.sheet).copied().unwrap_or(false) {
813            return;
814        }
815        let rect = (a.top, a.left, a.bottom, a.right);
816        if self
817            .index
818            .each_in(a.sheet, rect, &mut |v| out.push(vertex(v)))
819        {
820            return;
821        }
822        let sheet = &self.model.sheets[a.sheet];
823        let extent = self.extents.get(a.sheet).copied().flatten();
824        for ((row, col), cell) in cells_in(sheet, extent, a) {
825            if is_formula(&cell.content) {
826                if let Some(v) = self.index.get(a.sheet, row, col) {
827                    out.push(vertex(v));
828                }
829            }
830        }
831    }
832
833    /// A shape's references read off its parse once: each once, however
834    /// often the parse repeats it; one to the single sheet its placement
835    /// found, when that sheet holds no formula, left out; one every `$`
836    /// holds there, found now, since it names the same cells at every
837    /// offset and `graph::areas_at` reads a found sheet and not the cell's;
838    /// every other kept for each cell to move.
839    fn reads_of<'e>(&self, shape: &'e Shape) -> ShapeReads<'e> {
840        let mut reads = ShapeReads {
841            fixed: Vec::new(),
842            moving: Vec::new(),
843            named: false,
844        };
845        let Ok(e) = &shape.expr else {
846            return reads;
847        };
848        let mut each: Vec<&RefExpr> = Vec::new();
849        e.each_read(&mut |r| match r {
850            Read::Ref(re) => {
851                if !each.contains(&re) {
852                    each.push(re);
853                }
854            }
855            Read::Name { .. } => reads.named = true,
856        });
857        for re in each {
858            let found_once = match (re.found, &re.last_sheet) {
859                (Some((first, last)), None) if first == last => Some(usize::from(first)),
860                _ => None,
861            };
862            if let Some(sheet) = found_once {
863                if !self.holds.get(sheet).copied().unwrap_or(false) {
864                    continue;
865                }
866                if every_dollar(re) {
867                    if let Ok(areas) = areas_at(self.model, sheet, re, 0, 0) {
868                        for a in &areas {
869                            self.formulas_in(a, &mut reads.fixed);
870                        }
871                    }
872                    continue;
873                }
874            }
875            reads.moving.push(re);
876        }
877        reads.fixed.sort_unstable();
878        reads.fixed.dedup();
879        reads
880    }
881}
882
883/// Whether every `$` holds a reference, so that it names the same cells at
884/// every offset (KERNEL.33): a cell's two, a range's four, a whole column's
885/// columns, a whole row's rows.
886fn every_dollar(r: &RefExpr) -> bool {
887    let c = &r.corners;
888    match r.shape {
889        refers::Shape::Cell => c.row_abs1 && c.col_abs1,
890        refers::Shape::Range => c.row_abs1 && c.col_abs1 && c.row_abs2 && c.col_abs2,
891        refers::Shape::Column => c.col_abs1 && c.col_abs2,
892        refers::Shape::Row => c.row_abs1 && c.row_abs2,
893        refers::Shape::None => false,
894    }
895}
896
897/// Every reference of a parse that names its sheets, those sheets found once
898/// against the grid it is placed in (KERNEL.20): a machine's sheets, its
899/// twins among them, are fixed for its life, so the index a name finds at
900/// placement is the one it would find at every evaluation. A reference to
901/// the formula's own sheet, and one naming a sheet the grid does not hold,
902/// stay unfound and are found by name at each evaluation, as before. The
903/// walk keeps its own stack, so that a chain of operators costs it no depth.
904fn find_sheets(expr: &mut Expr, model: &Workbook) {
905    let mut stack = vec![expr];
906    while let Some(e) = stack.pop() {
907        match e {
908            Expr::Ref(r) => r.found = sheets_of(model, r),
909            Expr::Call(_, args) => stack.extend(args.iter_mut()),
910            Expr::Neg(x) | Expr::Pos(x) | Expr::Percent(x) => stack.push(x),
911            Expr::Binary(_, a, b) => {
912                stack.push(a);
913                stack.push(b);
914            }
915            Expr::Name { .. }
916            | Expr::Number(_)
917            | Expr::Text(_)
918            | Expr::Bool(_)
919            | Expr::Error(_)
920            | Expr::Empty => {}
921        }
922    }
923}
924
925/// A reference's named sheets as `graph::areas_at` finds them by name: the
926/// first, and the last of a span or the first again; `None` when it names
927/// none, one the grid does not hold, or one past the 256th, which a byte
928/// cannot hold and the name finds as before.
929fn sheets_of(model: &Workbook, r: &RefExpr) -> Option<(u8, u8)> {
930    let first = model.find_sheet(r.sheet.as_deref()?)?;
931    let last = match &r.last_sheet {
932        None => first,
933        Some(name) => model.find_sheet(name)?,
934    };
935    Some((u8::try_from(first).ok()?, u8::try_from(last).ok()?))
936}
937
938/// `Sheet!A1` for a cell, the sheet quoted as a reference quotes it.
939pub fn spell_cell(model: &Workbook, id: CellId) -> String {
940    let sheet = model
941        .sheets
942        .get(id.0)
943        .map(|s| s.name.as_str())
944        .unwrap_or("");
945    format!("{}!{}", quote_sheet(sheet), cell_ref(id.1, id.2))
946}
947
948#[cfg(test)]
949mod tests {
950    use super::*;
951
952    #[test]
953    fn every_dollar_holds_a_reference_whose_every_number_is_held() {
954        // KERNEL.33: a plan finds a reference once for its whole shape only
955        // when every `$` holds it, so that it names the same cells at every
956        // offset: a cell's two numbers, a range's four, a whole column's
957        // columns, a whole row's rows. One unheld number moves it.
958        for (text, held) in [
959            ("$C$2", true),
960            ("$C2", false),
961            ("C$2", false),
962            ("C2", false),
963            ("$C$2:$D$4", true),
964            ("$C$2:D4", false),
965            ("$C$2:$D4", false),
966            ("$C$2:D$4", false),
967            ("C2:$D$4", false),
968            ("$C:$D", true),
969            ("$C:D", false),
970            ("C:D", false),
971            ("$2:$4", true),
972            ("$2:4", false),
973            ("2:4", false),
974            ("S!$C$2:$D$4", true),
975            ("S!C2:$D$4", false),
976        ] {
977            let Ok(Expr::Ref(r)) = formula::parse(text) else {
978                panic!("{text} reads as one reference")
979            };
980            assert_eq!(every_dollar(&r), held, "{text}");
981        }
982    }
983}