1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//! The cross-recalculation dependency-graph cache.
//!
//! [`DependencyGraph::build`](crate::DependencyGraph::build) is a pure function
//! of the workbook's *structure*, and it used to run from scratch on every
//! [`recalc`](crate::Workbook::recalc) and every
//! [`recalc_incremental`](crate::Workbook::recalc_incremental) — together with
//! the [`evaluation_order`](crate::DependencyGraph::evaluation_order) derived
//! from it, the single largest fixed cost of a recalculation on a large
//! workbook, paid whether one cell changed or none did.
//!
//! This module holds that result on the workbook and hands it back until a
//! mutation could have changed it.
//!
//! # What the graph is a function of
//!
//! Reading [`DependencyGraph::build`](crate::DependencyGraph::build) and every
//! resolver it calls, the graph depends on exactly:
//!
//! 1. the sheet **name set** (a reference to a missing sheet resolves to
//! `Unresolved`, and every node is keyed by folded sheet name);
//! 2. every formula cell's `(sheet, address, formula text)`;
//! 3. the workbook's **named ranges** — their names and their `ref`s;
//! 4. the workbook's **table declarations** — their names and their `ref`s;
//! 5. **the text values stored in a declared table's header row**, because a
//! structured reference resolves its column by matching the header cell's
//! stored `Value::Text`.
//!
//! Point 5 is the one that is easy to get wrong, and it is why "writing a
//! literal cannot change the graph" is **false** in general: writing `"qty"`
//! into a table's header cell moves what `T[qty]` reads. It is true only when
//! the workbook declares no tables at all, since with no table declaration no
//! cell value can reach the graph.
//!
//! It is *not* a function of any other stored value, of spill footprints, or of
//! tab order: spill occupancy is judged against the grid at recalc time, and
//! the graph keys sheets by name, never by tab index.
//!
//! # The invalidation contract
//!
//! The cache is invalidated by the mutation, not by a later check, so the
//! invariant is: **if the entry is `Some`, it equals a build against the
//! workbook as it is now.** Every `&mut self` method of
//! [`Workbook`](crate::Workbook) either invalidates or is documented here as
//! provably structure-preserving. The two exceptions are:
//!
//! * `Workbook::set` / `Workbook::clear` of a **literal over a non-formula
//! cell in a workbook that declares no tables** — by points 2 and 5 above,
//! that adds and removes no node, no edge, and no header text the graph can
//! see;
//! * recalc's own value write-back, which rewrites an existing formula cell's
//! stored value while preserving its formula text — same reasoning, and
//! likewise only while no table is declared.
//!
//! Everything else — any formula write, any clear of a formula, any name or
//! table definition, any sheet operation, and **every** `&mut` accessor that
//! hands out interior state ([`Workbook::sheets_mut`](crate::Workbook::sheets_mut),
//! [`Workbook::sheet_mut`](crate::Workbook::sheet_mut),
//! [`Workbook::names_mut`](crate::Workbook::names_mut),
//! [`Workbook::tables_mut`](crate::Workbook::tables_mut)) — invalidates. The
//! `&mut` accessors invalidate on the *borrow*: what a caller does with the
//! borrow is unobservable from here, so the only sound assumption is the worst
//! one.
use BTreeSet;
use ;
use Arc;
use crate;
/// A built dependency graph together with the evaluation order derived from
/// it. Immutable once constructed: invalidation replaces the whole entry, it
/// never edits one in place, which is what makes sharing it across a
/// [`Workbook`](crate::Workbook) clone sound.
///
/// `pub`, not `pub(crate)`: [`Workbook::cached_graph_entry`](crate::Workbook::cached_graph_entry)
/// hands this out to any crate that only holds `&Workbook` and wants to reuse
/// a warm graph without rebuilding one (the wasm `precedentsOf`/`dependentsOf`
/// binding). Its fields stay `pub(crate)` — external callers reach the graph
/// through [`graph`](Self::graph), not by construction or field access.
/// The workbook's dependency-graph cache slot.
///
/// A field of [`Workbook`](crate::Workbook), so it must not disturb the
/// workbook's value-object contract: it is skipped by serde, compares equal to
/// every other cache, and hashes to nothing. Two workbooks with the same
/// content are still equal and still hash the same whether or not either has
/// recalculated.
pub
/// Every cache compares equal to every other: the cache is derived state, so
/// two workbooks that differ only in whether they have recalculated are the
/// same workbook (schema spec §8 — the document is the value).
/// Hashes to nothing, for the same reason [`PartialEq`] ignores it: `a == b`
/// must imply `hash(a) == hash(b)`.