@sheetxl/sdk 0.7.28-beta.3 → 0.8.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/cjs/{BX6A6xGDOr_td9mw.cjs → BatsV2UUraVDCRXm.cjs} +1 -1
- package/browser/cjs/CUz53T65cRGQRgvv.cjs +33 -0
- package/browser/cjs/{CWCKcp58KidyndaL.cjs → DU0kYUtdFa3jK-im.cjs} +1 -1
- package/browser/cjs/{B4PKKgaDTG9aSO-f.cjs → Dv4yIPLV98M5zLHa.cjs} +1 -1
- package/browser/cjs/a0Z-SXs_N71CtqYb.cjs +1 -0
- package/browser/cjs/index.cjs +2 -2
- package/browser/esm/{Dv8FyXyeygJPXoHo.mjs → 6JKSOvc5czaQB2Bj.mjs} +3 -3
- package/browser/esm/{DZfds6s3ayTZrozO.mjs → B0HS42a7lga1k1U_.mjs} +1 -1
- package/browser/esm/{C78IaCXqwCcraBPe.mjs → BtuJ9AGzeURtAsGo.mjs} +6946 -5790
- package/browser/esm/{CX1NO_a1QPjXDvTU.mjs → DGU6sM25u0EWCKl1.mjs} +3 -3
- package/browser/esm/{B1gpnr-7kLU7jTy-.mjs → DlPMeIyxT9tG_WDk.mjs} +2 -2
- package/browser/esm/{CPgz9_iM7xyAcytg.mjs → f2G3QRgEbvFJcoa2.mjs} +2 -2
- package/browser/esm/index.mjs +17 -14
- package/browser/esm/{Rj8e7SashcMlcdZJ.mjs → uyWBMSIxMlt8n5Z3.mjs} +2 -2
- package/node/esm/{8VtzCFSmLPKxJjOa.mjs → -pxdn3M3tc6PLHs7.mjs} +20 -20
- package/node/esm/{BkY99r-c3BhlT_Kv.mjs → 0n5bslM9jhO1JnI2.mjs} +1 -1
- package/node/esm/{DE3eIaqkk5xzmV1c.mjs → Cku7XiUvw1xrRasi.mjs} +1 -1
- package/node/esm/index.mjs +9650 -8487
- package/package.json +6 -6
- package/types/scripting-enums.json +6 -0
- package/types/scripting.d.ts +586 -27
- package/types/src/anchored/IAnchored.d.ts +27 -0
- package/types/src/calculation/Calculation.d.ts +26 -12
- package/types/src/calculation/ICalculation.d.ts +178 -0
- package/types/src/calculation/executor/CalcExecutor.d.ts +314 -14
- package/types/src/calculation/executor/CalcExecutorUtils.d.ts +31 -0
- package/types/src/calculation/executor/CircularReference.d.ts +81 -0
- package/types/src/calculation/grid/CalcGrid.d.ts +3 -1
- package/types/src/calculation/grid/CalcGridUtils.d.ts +3 -2
- package/types/src/calculation/grid/ICalcGrid.d.ts +12 -1
- package/types/src/calculation/spatial/CircularEntry.d.ts +18 -0
- package/types/src/calculation/spatial/index.d.ts +1 -0
- package/types/src/calculation/state/CalcNode.d.ts +44 -17
- package/types/src/calculation/state/CalcState.d.ts +78 -15
- package/types/src/calculation/state/CalculationContext.d.ts +42 -1
- package/types/src/calculation/state/IContextContainer.d.ts +31 -0
- package/types/src/calculation/state/_ReadObservedGrid.d.ts +90 -0
- package/types/src/cell/ICell.d.ts +16 -0
- package/types/src/cellRange/ICellRange.d.ts +51 -21
- package/types/src/condition/AnchoredConditional.d.ts +26 -6
- package/types/src/conditionalFormat/ConditionalFormat.d.ts +8 -1
- package/types/src/conditionalFormat/IConditionalFormat.d.ts +4 -1
- package/types/src/conditionalFormat/IConditionalFormatCollection.d.ts +6 -2
- package/types/src/dataValidation/IDataValidationCollection.d.ts +6 -2
- package/types/src/hyperlink/IHyperlink.d.ts +3 -1
- package/types/src/hyperlink/_Hyperlink.d.ts +6 -0
- package/types/src/lib/utils/_Utils.d.ts +7 -0
- package/types/src/lib/utils/utils.browser.d.ts +5 -0
- package/types/src/license/LicenseDetails.d.ts +5 -0
- package/types/src/primitives/CalcSignal.d.ts +20 -0
- package/types/src/primitives/PrimitiveUtils.d.ts +4 -2
- package/types/src/primitives/Range.d.ts +3 -1
- package/types/src/protectedRange/IProtectedRangeCollection.d.ts +7 -2
- package/types/src/scripting/FunctionArguments.d.ts +22 -0
- package/types/src/scripting/TypedFunction.d.ts +60 -0
- package/types/src/scripting/_ScriptUtils.d.ts +13 -0
- package/types/src/sheet/Sheet.d.ts +41 -2
- package/types/src/sheet/SheetCell.d.ts +2 -0
- package/types/src/sheet/SheetRange.d.ts +3 -0
- package/types/src/sheet/_SheetAutoFitUtils.d.ts +28 -0
- package/types/src/sheet/_SheetStateTypes.d.ts +14 -1
- package/types/src/style/IStyle.d.ts +5 -0
- package/types/src/table/AnchoredTable.d.ts +17 -1
- package/types/src/text/TextUtils.d.ts +18 -0
- package/browser/cjs/1mH2uhx_Dmceu88H.cjs +0 -33
- package/browser/cjs/D6cjnwWR_iUSe1f6.cjs +0 -1
|
@@ -4,6 +4,8 @@ import { ISpatialMap, ISpatialMap3D } from '../../collections';
|
|
|
4
4
|
import { FormulaSpatialValue, PrecedentSpatialValue, PrecedentKind } from '../spatial';
|
|
5
5
|
import { ICalcGrid } from '../grid';
|
|
6
6
|
import { IContextContainer, CalcNode } from '../state';
|
|
7
|
+
import { ReadHook } from '../state/_ReadObservedGrid';
|
|
8
|
+
import { CircularReference, CycleHost } from './CircularReference';
|
|
7
9
|
/**
|
|
8
10
|
* Executes a calculation cycle: collect → mark-and-plan → evaluate.
|
|
9
11
|
*
|
|
@@ -15,18 +17,129 @@ export declare class CalcExecutor {
|
|
|
15
17
|
protected _container: IContextContainer;
|
|
16
18
|
protected _context: IFormulaContext;
|
|
17
19
|
private readonly _requestCalculate;
|
|
18
|
-
protected _pendingCalcs:
|
|
20
|
+
protected _pendingCalcs: Map<CalcNode, number>;
|
|
21
|
+
/** Debounced drain of {@link _pendingCalcs} into the volatile set; one instance for the executor's lifetime. */
|
|
22
|
+
private readonly _flushPendingCalcs;
|
|
19
23
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
24
|
+
* The last pass's circular roster: every node it evaluated by demand (a `doMarkAndPlan` that
|
|
25
|
+
* could not order, or the iteration guard's hand-over) plus every node whose dynamic read
|
|
26
|
+
* landed on its own cell, each carrying its `cycleRole`. The module scratch `_circularNodes`,
|
|
27
|
+
* this many; a node in it is flagged `CalcState.Rostered`. Emptied when a pass starts.
|
|
22
28
|
*/
|
|
23
|
-
protected
|
|
29
|
+
protected _circularNodesLength: number;
|
|
30
|
+
/** The nodes the current `doMarkAndPlan` handed to demand evaluation, in worklist order. */
|
|
31
|
+
protected _demandBatch: CalcNode[];
|
|
32
|
+
/** {@link _onPassRead} as a bound hook — one closure for the executor's lifetime. */
|
|
33
|
+
protected _passHook: ReadHook;
|
|
24
34
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
35
|
+
* Marks a node downstream of the cycle `_taintGroup` names (design §2.1: every cell that reads
|
|
36
|
+
* a cycle, however it came to, is `0`). It skips evaluation — `Done` with a demand result of
|
|
37
|
+
* `0` — and taints its own dependents when the plan reaches it. A node already in a cycle keeps
|
|
38
|
+
* its role.
|
|
28
39
|
*/
|
|
29
|
-
protected
|
|
40
|
+
protected _taintDownstream: (calc: CalcNode) => void;
|
|
41
|
+
protected _taintGroup: number;
|
|
42
|
+
/**
|
|
43
|
+
* Iterative calculation for the pass in progress (design §6.3); `null` with it off. Under it a
|
|
44
|
+
* read of a `Waiting` node is not an abort: the reader goes on with the node's previous value
|
|
45
|
+
* and the slice is marked once the reader returns (`_iterHits`); a read of a member's result
|
|
46
|
+
* makes the reader downstream without aborting it either; and {@link _demandSweep}
|
|
47
|
+
* re-evaluates the members until they settle.
|
|
48
|
+
*/
|
|
49
|
+
protected _iterate: CalcExecutor.IterateOptions | null;
|
|
50
|
+
/** Inside {@link doDemandEvaluate} — where a `Waiting` read under iteration is marked in place. */
|
|
51
|
+
protected _demandActive: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Lengths of the module scratch lists of one {@link doDemandEvaluate}: `_iterHits`, the
|
|
54
|
+
* `Waiting` nodes the evaluation in progress read under iteration (each flagged
|
|
55
|
+
* `CalcState.IterHit`; consumed when it returns); `_iterMembers`, the members the call marked,
|
|
56
|
+
* in marking order — the sweep order; `_iterDownstream`, the nodes it marked downstream,
|
|
57
|
+
* evaluated once more after the sweeps. On the executor rather than local because the lists
|
|
58
|
+
* grow across the runs a sweep nests.
|
|
59
|
+
*/
|
|
60
|
+
protected _iterHitsLength: number;
|
|
61
|
+
protected _iterMembersLength: number;
|
|
62
|
+
protected _iterDownstreamLength: number;
|
|
63
|
+
/** The pass the hook answers for; inert otherwise. Set by `doMarkAndPlan`, released with `_gridUpdates`. */
|
|
64
|
+
protected _passActive: boolean;
|
|
65
|
+
protected _passCycleId: number;
|
|
66
|
+
protected _passMarkId: number;
|
|
67
|
+
/**
|
|
68
|
+
* Commits `doEvaluate`'s pending batch — installed for the length of a `doEvaluate`. A read
|
|
69
|
+
* that lands on a node evaluated but not yet committed needs nothing more than this: the
|
|
70
|
+
* value is right, it is just not in the grid yet.
|
|
71
|
+
*/
|
|
72
|
+
protected _flush: (() => void) | null;
|
|
73
|
+
/**
|
|
74
|
+
* The cycle table (design §7.3): per cycle id, one live {@link CircularReference} holding the
|
|
75
|
+
* nodes the sheet index currently shows as its members and as downstream of it — the object
|
|
76
|
+
* the index payloads carry and `ICell.getCircularReference()` answers. Persists across passes
|
|
77
|
+
* (a node moves when a commit writes its delta, `_trackCircular`) and is confirmed against the
|
|
78
|
+
* graph on read, since a deleted formula's node lingers here until something re-evaluates its
|
|
79
|
+
* cell. A cycle is created on first sight and dropped when its last node leaves.
|
|
80
|
+
*/
|
|
81
|
+
protected _cycles: Map<number, CircularReference>;
|
|
82
|
+
/** What a {@link CircularReference} may ask of this executor; built once, shared by all. */
|
|
83
|
+
protected _cycleHost: CycleHost;
|
|
84
|
+
/** Last cycle id handed out by a demand pass; ids are unique across passes. */
|
|
85
|
+
protected _cycleGroupNext: number;
|
|
86
|
+
/**
|
|
87
|
+
* Counters over every demand pass since construction: nodes evaluated by demand and
|
|
88
|
+
* `NeedsFirst` retries. A retry is the cost of the path (a formula run and thrown away), so a
|
|
89
|
+
* chain Kahn could not order should show about one per node at most.
|
|
90
|
+
*/
|
|
91
|
+
protected _demandStats: {
|
|
92
|
+
evaluations: number;
|
|
93
|
+
retries: number;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Counters and per-phase timings over every plan since construction — the measurement rig the
|
|
97
|
+
* performance plan's §1.6 rollout asks for, and what validates the §1.2/§1.3 caches while
|
|
98
|
+
* {@link planCacheMode} is `'assert'`.
|
|
99
|
+
*/
|
|
100
|
+
protected _planStats: CalcExecutor.PlanStats;
|
|
101
|
+
/**
|
|
102
|
+
* The plan caches of CALC-PERFORMANCE-PLAN.md §1.2/§1.3.
|
|
103
|
+
*
|
|
104
|
+
* - `'on'` (default): an eligible pass skips the in-degree + Kahn phases (plan = affected set
|
|
105
|
+
* sorted by order stamp) and discovery reuses cached `dependents`.
|
|
106
|
+
* - `'assert'` (the §1.6 rollout mode; the diagnostic to flip back to if values ever look
|
|
107
|
+
* stale — `__sxlCalc.mode('assert')`): stamps are written and eligibility computed, but every
|
|
108
|
+
* pass still runs the full search + Kahn phases; the fast path's order and the cached
|
|
109
|
+
* `dependents` are VERIFIED against them, mismatches counted in
|
|
110
|
+
* `planAssertFailures`/`dependentsAssertFailures` and logged. Costs the old planning price
|
|
111
|
+
* plus epsilon.
|
|
112
|
+
* - `'off'`: stamps are neither written nor consulted.
|
|
113
|
+
*/
|
|
114
|
+
planCacheMode: 'off' | 'assert' | 'on';
|
|
115
|
+
/** Next order-stamp plan id to hand out; ids are unique across passes. */
|
|
116
|
+
protected _planIdNext: number;
|
|
117
|
+
/** Id of the last fully-ordered plan that stamped (§1.2); `0` = none yet. */
|
|
118
|
+
protected _lastPlanId: number;
|
|
119
|
+
/** Length of the last stamped plan — the `orderIndex` range the fast path's bucket order spans. */
|
|
120
|
+
protected _lastStampedLength: number;
|
|
121
|
+
/**
|
|
122
|
+
* Whole-closure reuse (§1.4): remembered fully-ordered passes — seeds, plan, and the
|
|
123
|
+
* `precedentEpoch` each was planned at (`-1` = empty). A pass with the identical seed set at
|
|
124
|
+
* the same epoch has the identical affected closure and the same valid order, so discovery and
|
|
125
|
+
* the sort are skipped outright: the plan is reused verbatim and each node gets only its
|
|
126
|
+
* per-pass reset. TWO slots, because one was not enough for the common workflow — "type a value,
|
|
127
|
+
* then fill it down": the single-cell pass overwrote the remembered bulk plan every time. Slots
|
|
128
|
+
* are recycled least-recently-USED (a hit refreshes a memo's clock), so the two plans in play
|
|
129
|
+
* both stay resident: bulk → small edits → same bulk hits, and two alternating bulk edits both
|
|
130
|
+
* hit. NOT "keep the largest": a full calc's plan is the same size as a bulk edit's over the
|
|
131
|
+
* same sheet, and that tie evicted the bulk memo on the very next small edit. Per executor, not
|
|
132
|
+
* module scope (§1.5).
|
|
133
|
+
*/
|
|
134
|
+
protected _planMemos: [CalcExecutor.PlanMemo, CalcExecutor.PlanMemo];
|
|
135
|
+
/** The memos' LRU clock. Its own counter, NOT `PlanStats.passes`: a stats reset must not reorder the slots. */
|
|
136
|
+
protected _planMemoClock: number;
|
|
137
|
+
/**
|
|
138
|
+
* Nodes with an async promise in flight. Per executor, NOT module scope — module scope would
|
|
139
|
+
* cross-pollute workbook instances (undo to 0 formulas needs to clear these; TODO - revisit
|
|
140
|
+
* how we manage promises).
|
|
141
|
+
*/
|
|
142
|
+
protected _pendingPromises: Set<CalcNode>;
|
|
30
143
|
_gridUpdates: Map<string, ICalcGrid.IncrementalUpdates> | null;
|
|
31
144
|
_addDynamicPrecedent: ((node: CalcNode, range: IRange.Coords | undefined, kind: PrecedentKind) => void) | null;
|
|
32
145
|
constructor(container: IContextContainer, context: IFormulaContext, requestCalculate: () => void);
|
|
@@ -45,14 +158,130 @@ export declare class CalcExecutor {
|
|
|
45
158
|
*/
|
|
46
159
|
execute(cycleId: number, options: CalcExecutor.ExecuteOptions, state: CalcExecutor.ExecuteState): CalcExecutor.ExecuteResult | null;
|
|
47
160
|
/**
|
|
48
|
-
*
|
|
161
|
+
* The read hook of a pass (DESIGN-CIRCULAR-CALC.md §4.3, §5), shared by both paths: armed for
|
|
162
|
+
* every reference while a node is evaluated by demand, and for dynamic-branch references
|
|
163
|
+
* (`OFFSET`/`INDIRECT`/`INDEX` results) on the ordinary path. A read that lands on a node of
|
|
164
|
+
* THIS pass decides: `Waiting` (on the in-progress stack — transitively waiting on the reader)
|
|
165
|
+
* is a cycle; `Unevaluated` suspends the reader until it is done; `Evaluated` (in the pending
|
|
166
|
+
* batch) is committed on the spot and read; `Done` with a cycle role makes the reader
|
|
167
|
+
* downstream. Any other cell is a value. Inert outside a pass, so a reference built during one
|
|
168
|
+
* and read later (a spill committed afterwards) is plain values.
|
|
169
|
+
*/
|
|
170
|
+
private _onPassRead;
|
|
171
|
+
/**
|
|
172
|
+
* Under iteration, a read of a `Done` cycle node by the node being evaluated: the reader is
|
|
173
|
+
* downstream of that cycle (design §6.3) unless it is in one itself. Nothing is aborted — the
|
|
174
|
+
* reader goes on with the value — so the mark is made here, on the reader.
|
|
175
|
+
*/
|
|
176
|
+
private _iterRead;
|
|
177
|
+
/**
|
|
178
|
+
* The nodes of this pass inside `coords` that are still `Unevaluated`, written into `into` from
|
|
179
|
+
* slot `length` on; returns the new length.
|
|
180
|
+
*/
|
|
181
|
+
private _collectUnevaluated;
|
|
182
|
+
/**
|
|
183
|
+
* Evaluates `worklist` by demand (DESIGN-CIRCULAR-CALC.md §4.3): each node is evaluated with the
|
|
184
|
+
* read hook armed; a read that lands on one not yet evaluated suspends the reader and evaluates
|
|
185
|
+
* that one first (`NeedsFirst`); a read that lands on one already suspended — waiting,
|
|
186
|
+
* transitively, on the reader — is a cycle (`Circular`), and the in-progress stack from it up
|
|
187
|
+
* to the reader are the members, written `0`. A node that reads a member is downstream and
|
|
188
|
+
* writes `0` too. Everything else simply computes: an unorderable static edge that is never
|
|
189
|
+
* read (`OFFSET`'s anchor, `ROW`'s argument, an untaken `IF` branch) makes no cycle.
|
|
49
190
|
*
|
|
50
191
|
* @remarks
|
|
51
|
-
*
|
|
52
|
-
* (
|
|
53
|
-
*
|
|
192
|
+
* Two callers: `doEvaluate` when the plan reaches the nodes `doMarkAndPlan` could not order
|
|
193
|
+
* (the batch, after everything ordered is committed), and `doEvaluate` again for a single
|
|
194
|
+
* ordered node whose dynamic read landed on a node not yet evaluated — the same suspension,
|
|
195
|
+
* on the ordinary path (§5). Every node evaluated here is flagged `circular`, so the plan
|
|
196
|
+
* takes its `demandValue` when it reaches it, and its finalized scalar is already in the grid
|
|
197
|
+
* for later lazy reads; `flush` commits the ordered path's pending batch when a read needs a
|
|
198
|
+
* value still sitting in it. Flat: the stack is an array, so a 1,560-row chain is 1,560
|
|
199
|
+
* evaluations, not 1,560 JS frames. A node is retried at most once per `NeedsFirst` it raises,
|
|
200
|
+
* and with the prefetch of its static precedents that is normally once.
|
|
54
201
|
*/
|
|
55
|
-
protected
|
|
202
|
+
protected doDemandEvaluate(cycleId: number, getUpdates: (scope: string) => ICalcGrid.IncrementalUpdates, worklist: readonly CalcNode[], flush: () => void): void;
|
|
203
|
+
/**
|
|
204
|
+
* The finalized scalar goes into the grid now, so a later lazy read sees it; `doEvaluate`
|
|
205
|
+
* commits the same value with the notifications when it reaches the node.
|
|
206
|
+
*/
|
|
207
|
+
private _writeEarly;
|
|
208
|
+
/** The worklist loop of {@link doDemandEvaluate}; re-entered by a sweep for the nodes a read needs first. */
|
|
209
|
+
private _demandRun;
|
|
210
|
+
/**
|
|
211
|
+
* Marks the started (`Waiting`) nodes on the in-progress stack from `read` up to the top as one
|
|
212
|
+
* cycle — the slice a `Waiting` read names (design §4.3). A slice touching a member of an
|
|
213
|
+
* earlier cycle joins that cycle. The started nodes are on the stack in READ order — each is
|
|
214
|
+
* there because the one below it read it, and the reader on top read `read` — so the slice is
|
|
215
|
+
* the walk `getPath` answers: each member is linked to the next, the last back to the first.
|
|
216
|
+
* The demand state is left as it is: the caller finishes the members (iteration off) or lets
|
|
217
|
+
* them run on (iteration on). Returns the stack index of `read`.
|
|
218
|
+
*/
|
|
219
|
+
private _markCycleSlice;
|
|
220
|
+
/**
|
|
221
|
+
* Iterative calculation (design §6.3) over the members {@link _demandRun} marked, which it
|
|
222
|
+
* evaluated once each — sweep 1. Every further sweep re-evaluates them in marking order, each
|
|
223
|
+
* read seeing the latest value (Gauss–Seidel, as Excel: `probe-iterative.ps1`), until no member
|
|
224
|
+
* moved by `maxChange` or `maxIterations` sweeps ran. The limits are global, as in Excel — one
|
|
225
|
+
* unsettled member keeps every cycle of the pass sweeping. Downstream nodes read the members
|
|
226
|
+
* once more afterwards; they never feed a cycle, so they need no sweeping. The members are then
|
|
227
|
+
* registered as volatile so the next recalculation re-runs them, which is how `=A1+1` advances
|
|
228
|
+
* per recalc (Excel re-iterates every cycle on every `Calculate()`).
|
|
229
|
+
*/
|
|
230
|
+
private _demandSweep;
|
|
231
|
+
/** Clears the `IterHit` flags of the hits not consumed by {@link _demandRun}'s loop and empties the list. */
|
|
232
|
+
private _drainIterHits;
|
|
233
|
+
/**
|
|
234
|
+
* One evaluation of a `Done` node under iteration, with the read hook armed. A read that
|
|
235
|
+
* needs a node not yet evaluated (a handed-over node's sweep runs before the batch) runs those
|
|
236
|
+
* by demand and retries; a `Waiting` read cannot occur (nothing is waiting) and a member's
|
|
237
|
+
* result is a value (`_iterRead`), so nothing aborts.
|
|
238
|
+
*/
|
|
239
|
+
private _demandEvaluateOne;
|
|
240
|
+
/**
|
|
241
|
+
* @internal
|
|
242
|
+
* Every cell the sheets currently show in a cycle or downstream of one, as dirty regions — what
|
|
243
|
+
* a change of the iterative-calculation setting re-evaluates.
|
|
244
|
+
*/
|
|
245
|
+
getCycleCells(): ISpatialMap.Entry[];
|
|
246
|
+
/** Whether the pass in progress found a cycle member. */
|
|
247
|
+
protected _hasCycle(): boolean;
|
|
248
|
+
/** Adds `node` to the pass's circular roster, once. */
|
|
249
|
+
private _addCircular;
|
|
250
|
+
/** Empties the roster for a new pass, clearing each node's `Rostered` flag on the way out. */
|
|
251
|
+
private _resetCircular;
|
|
252
|
+
/**
|
|
253
|
+
* Records the difference between what the sheet's circular index shows for `node` and what
|
|
254
|
+
* this pass decided, in the cycle table and in the pass's conflating delta — the same path a
|
|
255
|
+
* pending status takes to the sheet (design §7.2). Called only when one of the two is set.
|
|
256
|
+
*/
|
|
257
|
+
protected _trackCircular(node: CalcNode, updates: ICalcGrid.IncrementalUpdates, major: number, minor: number, isColumn: boolean): void;
|
|
258
|
+
/** The public face of cycle `group`, created on first sight. */
|
|
259
|
+
private _cycleOf;
|
|
260
|
+
/**
|
|
261
|
+
* Whether `node` is still the node registered for its own cell.
|
|
262
|
+
*
|
|
263
|
+
* @remarks
|
|
264
|
+
* A `CalcNode` is a plain graph object reachable by identity long after the grid has replaced or
|
|
265
|
+
* dropped its block — the same hazard `doCollectVolatileCalcs` guards against with `isLiveBlock`.
|
|
266
|
+
*/
|
|
267
|
+
protected isLiveNode(node: CalcNode): boolean;
|
|
268
|
+
/**
|
|
269
|
+
* @internal
|
|
270
|
+
* A copy of the last pass's circular roster — see {@link _circularNodesLength} — each node
|
|
271
|
+
* carrying the {@link CycleRole} its reads gave it. Describes the last pass only.
|
|
272
|
+
*/
|
|
273
|
+
getCircularNodes(): readonly CalcNode[];
|
|
274
|
+
/**
|
|
275
|
+
* @internal
|
|
276
|
+
* Demand-pass counters since construction — evaluations and `NeedsFirst` retries — plus the
|
|
277
|
+
* plan-phase counters and timings (see {@link _planStats}).
|
|
278
|
+
*/
|
|
279
|
+
getDemandStats(): {
|
|
280
|
+
evaluations: number;
|
|
281
|
+
retries: number;
|
|
282
|
+
} & CalcExecutor.PlanStats;
|
|
283
|
+
/** @internal Zeroes every counter/timing so a measurement window starts clean (`__sxlCalc.reset()`). */
|
|
284
|
+
resetStats(): void;
|
|
56
285
|
/**
|
|
57
286
|
* Collects and marks all volatile CalcNodes.
|
|
58
287
|
*
|
|
@@ -79,7 +308,18 @@ export declare class CalcExecutor {
|
|
|
79
308
|
* @remarks
|
|
80
309
|
* Uses Kahn's algorithm.
|
|
81
310
|
*/
|
|
82
|
-
doMarkAndPlan(cycleId: number, markId: number, initial: FixedPackedArray<CalcNode>, plan: FixedPackedArray<CalcNode
|
|
311
|
+
doMarkAndPlan(cycleId: number, markId: number, initial: FixedPackedArray<CalcNode>, plan: FixedPackedArray<CalcNode>, forceDemand?: boolean): boolean;
|
|
312
|
+
/**
|
|
313
|
+
* The remembered plan whose seed set is exactly this pass's, at this epoch — or `null`. The
|
|
314
|
+
* length check first: it is what rejects the common miss (a single-cell pass against a bulk
|
|
315
|
+
* memo) before any node is touched.
|
|
316
|
+
*/
|
|
317
|
+
private _matchPlanMemo;
|
|
318
|
+
/**
|
|
319
|
+
* Records this pass's seeds and plan for whole-closure reuse (§1.4). The victim slot is a stale
|
|
320
|
+
* one if there is one, else the least recently used.
|
|
321
|
+
*/
|
|
322
|
+
private _rememberPlan;
|
|
83
323
|
doEvaluate(cycleId: number, getUpdates: (scope: string) => ICalcGrid.IncrementalUpdates, scheduleRecalculation: (cycleId: number, calcNode?: CalcNode) => void, commit: (calcNodes: CalcNode[], calcNodesLength: number) => void, forceFull: boolean, plan: FixedPackedArray<CalcNode>): ISpatialMap | undefined;
|
|
84
324
|
}
|
|
85
325
|
export declare namespace CalcExecutor {
|
|
@@ -96,6 +336,24 @@ export declare namespace CalcExecutor {
|
|
|
96
336
|
* The grid to seed the pass FROM — `ICalculation.CalculateOptions.scope`, i.e. "Calculate Sheet".
|
|
97
337
|
*/
|
|
98
338
|
scopeGridId?: string;
|
|
339
|
+
/**
|
|
340
|
+
* Iterative calculation (design §6.3) for the pass; `null`/absent with it off.
|
|
341
|
+
*/
|
|
342
|
+
iterate?: IterateOptions | null;
|
|
343
|
+
}
|
|
344
|
+
/** The two limits of `ICalculation.IterativeCalculation`. */
|
|
345
|
+
interface IterateOptions {
|
|
346
|
+
maxIterations: number;
|
|
347
|
+
maxChange: number;
|
|
348
|
+
}
|
|
349
|
+
/** How a cycle's last iterated evaluation ended — what `ICircularReference` answers. */
|
|
350
|
+
interface IterationResult {
|
|
351
|
+
/** Every member moved by less than `maxChange` in the last sweep. */
|
|
352
|
+
converged: boolean;
|
|
353
|
+
/** Sweeps run, the first evaluation included. */
|
|
354
|
+
iterationCount: number;
|
|
355
|
+
/** The largest change any member made in the last sweep. */
|
|
356
|
+
lastChange: number;
|
|
99
357
|
}
|
|
100
358
|
/** Snapshot of Calculation's persistent state for one pass */
|
|
101
359
|
interface ExecuteState {
|
|
@@ -105,6 +363,48 @@ export declare namespace CalcExecutor {
|
|
|
105
363
|
/** Volatiles accumulated before this cycle — drained and cleared by execute() */
|
|
106
364
|
oldVolatiles: Set<CalcNode>;
|
|
107
365
|
}
|
|
366
|
+
/** A remembered fully-ordered pass for whole-closure reuse (§1.4) — see `CalcExecutor._planMemos`. */
|
|
367
|
+
interface PlanMemo {
|
|
368
|
+
seeds: CalcNode[];
|
|
369
|
+
seedsLength: number;
|
|
370
|
+
plan: CalcNode[];
|
|
371
|
+
planLength: number;
|
|
372
|
+
/** The `precedentEpoch` the plan was made at; `-1` = empty. */
|
|
373
|
+
epoch: number;
|
|
374
|
+
/** `CalcExecutor._planMemoClock` when last written or matched — the LRU key for slot recycling. */
|
|
375
|
+
used: number;
|
|
376
|
+
}
|
|
377
|
+
/** Plan-phase counters and timings — see `CalcExecutor.planCacheMode` and performance plan §1.6. */
|
|
378
|
+
interface PlanStats {
|
|
379
|
+
/** In-degree formula lookups answered by the single-entry block cache. */
|
|
380
|
+
formulaCacheHits: number;
|
|
381
|
+
formulaCacheMisses: number;
|
|
382
|
+
/** doMarkAndPlan invocations. */
|
|
383
|
+
passes: number;
|
|
384
|
+
/** Passes whose whole affected set carried valid order stamps (fast-path candidates). */
|
|
385
|
+
fastEligible: number;
|
|
386
|
+
/** Eligible passes that actually skipped in-degree + Kahn (`'on'` mode only). */
|
|
387
|
+
fastHits: number;
|
|
388
|
+
/** Passes whose seed set and precedent epoch matched the last plan (§1.4 candidates). */
|
|
389
|
+
closureEligible: number;
|
|
390
|
+
/** Closure-eligible passes that reused the last plan verbatim, skipping discovery too (`'on'` only). */
|
|
391
|
+
closureHits: number;
|
|
392
|
+
/** `'assert'` mode: closure-eligible passes whose fresh affected set differed from the last plan. */
|
|
393
|
+
closureAssertFailures: number;
|
|
394
|
+
/** Fully-ordered plans that wrote order stamps. */
|
|
395
|
+
stampedPlans: number;
|
|
396
|
+
/** `'assert'` mode: eligible passes whose stamp order contradicted a real static edge. */
|
|
397
|
+
planAssertFailures: number;
|
|
398
|
+
/** `'assert'` mode: cached `dependents` that did not match a fresh search. */
|
|
399
|
+
dependentsAssertFailures: number;
|
|
400
|
+
/** Discovery searches skipped (or verified) because `dependentsEpoch` was current. */
|
|
401
|
+
dependentsCacheHits: number;
|
|
402
|
+
dependentsCacheMisses: number;
|
|
403
|
+
discoveryMs: number;
|
|
404
|
+
inDegreeMs: number;
|
|
405
|
+
kahnMs: number;
|
|
406
|
+
evaluateMs: number;
|
|
407
|
+
}
|
|
108
408
|
/** Updated spatial maps and committed grid batches returned to Calculation */
|
|
109
409
|
interface ExecuteResult {
|
|
110
410
|
precedentSpatial: ISpatialMap3D<PrecedentSpatialValue>;
|
|
@@ -1,7 +1,38 @@
|
|
|
1
1
|
import { IRange, Scalar } from '@sheetxl/primitives';
|
|
2
2
|
import { Tuple, ISpatialMap } from '../../collections';
|
|
3
|
+
import { CalcSignal } from '../../primitives/CalcSignal';
|
|
3
4
|
import { CalcNode } from '../state';
|
|
4
5
|
import { PrecedentSpatialValue, FormulaSpatialValue } from '../spatial';
|
|
6
|
+
/**
|
|
7
|
+
* Raised from a read that landed on unorderable nodes not yet evaluated: the reader is suspended,
|
|
8
|
+
* `nodes` are evaluated first, and the reader is retried from scratch.
|
|
9
|
+
*/
|
|
10
|
+
export declare class NeedsFirstSignal extends CalcSignal {
|
|
11
|
+
readonly nodes: readonly CalcNode[];
|
|
12
|
+
readonly count: number;
|
|
13
|
+
/**
|
|
14
|
+
* @param nodes A scratch array the executor reuses across reads; only the first `count` slots
|
|
15
|
+
* belong to this read, and the catcher consumes them before the next read can overwrite them.
|
|
16
|
+
*/
|
|
17
|
+
constructor(nodes: readonly CalcNode[], count: number);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Raised from a read that landed on a node already suspended — one that is, transitively,
|
|
21
|
+
* waiting on the reader. The in-progress stack from `node` up to the reader is a cycle.
|
|
22
|
+
*/
|
|
23
|
+
export declare class CircularSignal extends CalcSignal {
|
|
24
|
+
readonly node: CalcNode;
|
|
25
|
+
constructor(node: CalcNode);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Raised from a read that landed on a cycle member or a cell downstream of one: the reader is
|
|
29
|
+
* downstream too and writes `0` without finishing (Excel, iterative calc off — `=A1+100` beside
|
|
30
|
+
* `=A1+1` in A1 is `0`, not `100`).
|
|
31
|
+
*/
|
|
32
|
+
export declare class DownstreamSignal extends CalcSignal {
|
|
33
|
+
readonly node: CalcNode;
|
|
34
|
+
constructor(node: CalcNode);
|
|
35
|
+
}
|
|
5
36
|
export declare function rangeToTuples(range: IRange, offsetMajor?: number, offsetMinor?: number): Tuple.BoundedTuples<Scalar>;
|
|
6
37
|
/**
|
|
7
38
|
* TODO - document and/or rationalize with visitPrecedentBlock
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { IRange } from '@sheetxl/primitives';
|
|
2
|
+
import { ICalculation } from '../ICalculation';
|
|
3
|
+
import { CalcNode } from '../state';
|
|
4
|
+
import { CircularEntry } from '../spatial';
|
|
5
|
+
import { CalcExecutor } from './CalcExecutor';
|
|
6
|
+
/**
|
|
7
|
+
* The two graph questions a {@link CircularReference} cannot answer on its own; built once by
|
|
8
|
+
* the executor, shared by every cycle.
|
|
9
|
+
*/
|
|
10
|
+
export interface CycleHost {
|
|
11
|
+
/** Whether `node` is still the node registered for its own cell (a deleted formula's is not). */
|
|
12
|
+
isLiveNode(node: CalcNode): boolean;
|
|
13
|
+
/** The sheet name for a grid id, or `undefined` for a virtual grid (a named formula's body). */
|
|
14
|
+
resolveScope(gridId: string): string | undefined;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* One reference cycle (DESIGN-CIRCULAR-CALC.md §7.3): the executor's record of it and its public
|
|
18
|
+
* face in one — the object a `Feature.Circular` region carries as its payload and
|
|
19
|
+
* `ICell.getCircularReference()` answers. It holds the nodes the sheet index currently shows as
|
|
20
|
+
* its members and as downstream of it, the interned index payloads, and how the sweep that
|
|
21
|
+
* settled it ended. It persists across passes — a node moves between cycles when a commit writes
|
|
22
|
+
* its delta (`_trackCircular`) — and is dropped when its last node leaves.
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* Every read filters through {@link CycleHost.isLiveNode}: a pass that deletes the offending
|
|
26
|
+
* formula never plans, so the sets cannot clear themselves, and a member is counted only while
|
|
27
|
+
* its cell still holds the block the cycle was found in (design §7.3, trap 5).
|
|
28
|
+
*
|
|
29
|
+
* The iteration answers come from the sweep that settled the cycle (design §6.3). A cycle id is
|
|
30
|
+
* minted by the pass that finds it, so a cycle evaluated with iteration off is a different object
|
|
31
|
+
* with no record — `isIterative()` is exactly "a sweep settled this one".
|
|
32
|
+
*/
|
|
33
|
+
export declare class CircularReference implements ICalculation.ICircularReference {
|
|
34
|
+
protected readonly _host: CycleHost;
|
|
35
|
+
protected readonly _cycleId: number;
|
|
36
|
+
/** @internal The nodes the sheet index shows in the cycle; maintained by the executor. */
|
|
37
|
+
readonly members: Set<CalcNode>;
|
|
38
|
+
/** @internal The nodes the sheet index shows downstream of the cycle; maintained by the executor. */
|
|
39
|
+
readonly downstream: Set<CalcNode>;
|
|
40
|
+
/** One payload per kind, so equal payloads are one object and adjacent cells one region. */
|
|
41
|
+
protected _memberEntry: CircularEntry | null;
|
|
42
|
+
protected _downstreamEntry: CircularEntry | null;
|
|
43
|
+
protected _iteration: CalcExecutor.IterationResult | null;
|
|
44
|
+
constructor(host: CycleHost, cycleId: number);
|
|
45
|
+
/** @internal The executor's key for this cycle. */
|
|
46
|
+
getCycleId(): number;
|
|
47
|
+
/** @internal Whether the sheet index shows no cell in or downstream of the cycle any more. */
|
|
48
|
+
isEmpty(): boolean;
|
|
49
|
+
/** @internal The index payload for the cycle's members or for its downstream cells. */
|
|
50
|
+
getEntry(isMember: boolean): CircularEntry;
|
|
51
|
+
/** @internal How the sweep that settled this cycle ended; written by the executor. */
|
|
52
|
+
setIteration(result: CalcExecutor.IterationResult): void;
|
|
53
|
+
/** @inheritdoc ICalculation.ICircularReference.getRepresentative */
|
|
54
|
+
getRepresentative(): IRange.CellCoords | null;
|
|
55
|
+
/** @inheritdoc ICalculation.ICircularReference.getMembers */
|
|
56
|
+
getMembers(): readonly IRange.CellCoords[];
|
|
57
|
+
/** @inheritdoc ICalculation.ICircularReference.getMemberCount */
|
|
58
|
+
getMemberCount(): number;
|
|
59
|
+
/** @inheritdoc ICalculation.ICircularReference.getDownstreamCount */
|
|
60
|
+
getDownstreamCount(): number;
|
|
61
|
+
/** @inheritdoc ICalculation.ICircularReference.isMember */
|
|
62
|
+
isMember(coords: IRange.CellCoords): boolean;
|
|
63
|
+
/** @inheritdoc ICalculation.ICircularReference.getPath */
|
|
64
|
+
getPath(from: IRange.CellCoords): readonly IRange.CellCoords[];
|
|
65
|
+
/** The live member at `coords`, or `null`. */
|
|
66
|
+
protected _memberAt(coords: IRange.CellCoords): CalcNode | null;
|
|
67
|
+
/** @inheritdoc ICalculation.ICircularReference.isIterative */
|
|
68
|
+
isIterative(): boolean;
|
|
69
|
+
/** @inheritdoc ICalculation.ICircularReference.isConverged */
|
|
70
|
+
isConverged(): boolean;
|
|
71
|
+
/** @inheritdoc ICalculation.ICircularReference.getIterationCount */
|
|
72
|
+
getIterationCount(): number;
|
|
73
|
+
/** @inheritdoc ICalculation.ICircularReference.getLastChange */
|
|
74
|
+
getLastChange(): number;
|
|
75
|
+
/** The members still on the graph, in row-major order (virtual grids first, then by sheet). */
|
|
76
|
+
protected _liveMembers(): CalcNode[];
|
|
77
|
+
protected _countLive(nodes: Set<CalcNode>): number;
|
|
78
|
+
protected _scopeOf(node: CalcNode): string | null;
|
|
79
|
+
protected _toCoords(node: CalcNode): IRange.CellCoords;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=CircularReference.d.ts.map
|
|
@@ -3,7 +3,7 @@ import { IListener } from '../../listener';
|
|
|
3
3
|
import { ListenerEmitter } from '../../listener/ListenerEmitter';
|
|
4
4
|
import { Tuple, ISpatialQuery, ISparseGrid, ISpatialMap, ISpatialMap3D } from '../../collections';
|
|
5
5
|
import { IContextContainer, CalcNode } from '../state';
|
|
6
|
-
import { SpillEntry, FormulaSpatialValue, ContentfulSpatialValue, PrecedentSpatialValue } from '../spatial';
|
|
6
|
+
import { SpillEntry, FormulaSpatialValue, ContentfulSpatialValue, PrecedentSpatialValue, CircularEntry } from '../spatial';
|
|
7
7
|
import { IFormulaBlock } from '../formula';
|
|
8
8
|
import { ReferenceCoords } from '../Types';
|
|
9
9
|
import { ICalculation } from '../ICalculation';
|
|
@@ -20,12 +20,14 @@ export declare class CalcGrid implements ICalcGrid {
|
|
|
20
20
|
_spillSpatial: ISpatialMap<SpillEntry>;
|
|
21
21
|
_errorSpatial: ISpatialMap<string>;
|
|
22
22
|
_pendingSpatial: ISpatialMap<boolean>;
|
|
23
|
+
_circularSpatial: ISpatialMap<CircularEntry>;
|
|
23
24
|
_contentfulSpatial: ISpatialMap<ContentfulSpatialValue>;
|
|
24
25
|
_notifyValues: ISpatialMap;
|
|
25
26
|
_notifySpillsInsert: ISpatialMap.Entry<SpillEntry>[];
|
|
26
27
|
_notifySpillsRemove: IRange.Coords[];
|
|
27
28
|
_notifyFormats: ISpatialMap;
|
|
28
29
|
_notifyPending: ISpatialMap<boolean>;
|
|
30
|
+
_notifyCircular: ISpatialMap<CircularEntry | false>;
|
|
29
31
|
_cloneKey?: CalcGrid;
|
|
30
32
|
_version: number;
|
|
31
33
|
_iterationId: number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { IRange, Scalar } from '@sheetxl/primitives';
|
|
2
2
|
import { Tuple, ISparseGrid, ISpatialMap, ISpatialMap3D } from '../../collections';
|
|
3
3
|
import { CalcNode } from '../state';
|
|
4
|
-
import { ContentfulSpatialValue, FormulaSpatialValue, SpillEntry, PrecedentSpatialValue, PrecedentKind } from '../spatial';
|
|
4
|
+
import { ContentfulSpatialValue, FormulaSpatialValue, SpillEntry, PrecedentSpatialValue, CircularEntry, PrecedentKind } from '../spatial';
|
|
5
5
|
import { ReferenceCoords } from '../Types';
|
|
6
6
|
export declare function anchorCompareValues(a: ISpatialMap.Entry<FormulaSpatialValue>, removing: ISpatialMap.Entry<FormulaSpatialValue>): boolean;
|
|
7
7
|
/**
|
|
@@ -19,7 +19,8 @@ export declare function isTopLeft(rowIndex: number, colIndex: number, range: IRa
|
|
|
19
19
|
* This code will inline spill errors but spill results are handled in calculations.
|
|
20
20
|
*/
|
|
21
21
|
export declare function applyContentful(cycleId: number, removes: readonly IRange.Coords[] | null | undefined, inserts: ISpatialMap.Entry[] | null | undefined, contentfulSpatial: ISpatialMap<ContentfulSpatialValue>, spillSpatial: ISpatialMap<SpillEntry>, values: ISparseGrid<Scalar>, addDirty?: (colStart: number, rowStart: number, colEnd: number, rowEnd: number) => void, notifyValuesSpatial?: ISpatialMap, notifySpills?: (insert: ISpatialMap.Entry<SpillEntry>, remove: ISpatialMap.Entry<SpillEntry>) => void, notifyPending?: ISpatialMap<boolean>, errorSpatial?: ISpatialMap<string>, // notifyErrorSpatial,
|
|
22
|
-
isColumn?: boolean, notifyCalc?: boolean
|
|
22
|
+
isColumn?: boolean, notifyCalc?: boolean, // should notify. this is to differentiate between user set values and calc set values
|
|
23
|
+
circularSpatial?: ISpatialMap<CircularEntry>): IRange.Coords | null;
|
|
23
24
|
/**
|
|
24
25
|
* Creates a map from tuples
|
|
25
26
|
*
|
|
@@ -5,7 +5,7 @@ import { IListener } from '../../listener';
|
|
|
5
5
|
import { IHistory } from '../../history';
|
|
6
6
|
import { ISpatialQuery, ISpatialMap, ISparseGrid, Tuple } from '../../collections';
|
|
7
7
|
import { IFormulaBlock } from '../formula';
|
|
8
|
-
import { ContentfulSpatialValue, SpillEntry } from '../spatial';
|
|
8
|
+
import { ContentfulSpatialValue, SpillEntry, CircularEntry } from '../spatial';
|
|
9
9
|
import { ReferenceCoords, NamedReference } from '../Types';
|
|
10
10
|
import { ICalculation } from '../ICalculation';
|
|
11
11
|
/**
|
|
@@ -311,6 +311,14 @@ export declare namespace ICalcGrid {
|
|
|
311
311
|
* These are guaranteed to not overlap.
|
|
312
312
|
*/
|
|
313
313
|
pendingUpdates?: ISpatialMap.Entry<boolean>[];
|
|
314
|
+
/**
|
|
315
|
+
* Cells that entered or left a reference cycle (or the set downstream of one). A `null`
|
|
316
|
+
* value is a removal.
|
|
317
|
+
*
|
|
318
|
+
* @remarks
|
|
319
|
+
* These are guaranteed to not overlap.
|
|
320
|
+
*/
|
|
321
|
+
circularUpdates?: ISpatialMap.Entry<CircularEntry | null>[];
|
|
314
322
|
}
|
|
315
323
|
/**
|
|
316
324
|
* Returns from `ICalcGrid.beginUpdates()`.
|
|
@@ -324,13 +332,16 @@ export declare namespace ICalcGrid {
|
|
|
324
332
|
spillSpatial: ISpatialMap<SpillEntry>;
|
|
325
333
|
errorSpatial: ISpatialMap<string>;
|
|
326
334
|
pendingSpatial: ISpatialMap<boolean>;
|
|
335
|
+
circularSpatial: ISpatialMap<CircularEntry>;
|
|
327
336
|
contentfulSpatial: ISpatialMap<ContentfulSpatialValue>;
|
|
328
337
|
notifyValues: ISpatialMap;
|
|
329
338
|
notifySpillsInsert: ISpatialMap.Entry<SpillEntry>[];
|
|
330
339
|
notifySpillsRemove: IRange.Coords[];
|
|
331
340
|
notifyFormats: ISpatialMap;
|
|
332
341
|
notifyPending: ISpatialMap<boolean>;
|
|
342
|
+
notifyCircular: ISpatialMap<CircularEntry | false>;
|
|
333
343
|
pendingConflating: ISpatialMap.IConflatingEntries<boolean> | null;
|
|
344
|
+
circularConflating: ISpatialMap.IConflatingEntries<CircularEntry | false> | null;
|
|
334
345
|
errorRemovesConflating: ISpatialMap.IConflatingEntries<null> | null;
|
|
335
346
|
errorInsertsConflating: ISpatialMap.IConflatingEntries<string> | null;
|
|
336
347
|
formatsConflating: ISpatialMap.IConflatingEntries | null;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ICalculation } from '../ICalculation';
|
|
2
|
+
/**
|
|
3
|
+
* What a sheet's circular index says about a cell (DESIGN-CIRCULAR-CALC.md §7.2): which
|
|
4
|
+
* reference cycle it belongs to, and whether it is in the cycle or merely downstream of it.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* The value stays what it is (`0`, Excel's answer with iterative calculation off); this is
|
|
8
|
+
* metadata beside it, the way `SpillEntry` marks a spill zone. Entries are interned per
|
|
9
|
+
* `(cycle, kind)` by the executor, so equal payloads are the same object and adjacent cells of
|
|
10
|
+
* one cycle coalesce into one region — and a region's payload IS the cycle, no id to look up.
|
|
11
|
+
*/
|
|
12
|
+
export interface CircularEntry {
|
|
13
|
+
/** The cycle; one live object per cycle, shared by every cell in it or downstream of it. */
|
|
14
|
+
readonly cycle: ICalculation.ICircularReference;
|
|
15
|
+
/** In the cycle, or a reader of it (transitively). Both evaluate to `0`. */
|
|
16
|
+
readonly kind: 'member' | 'downstream';
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=CircularEntry.d.ts.map
|