/** * The calculation engine: the orchestrator that turns a formula edit or a cell * change into a minimal, correctly-ordered recomputation and writes results back * to the grid. * * ### Pipeline * ``` * setFormula / onCellsChanged * │ compile → extract references → resolve precedents * ▼ * DependencyGraph ──▶ dirty subgraph (only affected cells) * │ │ Kahn topological order + cycle detection * ▼ ▼ * Evaluator over EvalContext ─▶ write changed values via the adapter * ``` * * It never recomputes the whole grid: a change dirties only its transitive * dependents (plus volatile cells), and those are evaluated precedents-first. * Circular references are detected and flagged `#CIRC!` instead of looping. * * @packageDocumentation */ import type { FormulaGridAdapter } from '../formula-grid-adapter'; import type { ConfigurationManager } from '../config/formula-config'; import type { FunctionRegistry } from '../functions/function-registry'; import type { Evaluator } from '../evaluator/evaluator'; import type { FormulaStore } from '../store/formula-store'; import type { NamedRangeManager } from '../named-range-manager'; /** A source of "now" and randomness, injected so volatile functions are testable. */ export interface FormulaClock { /** Current time as epoch milliseconds. */ now(): number; /** A uniform random number in `[0, 1)`. */ random(): number; } /** The default clock (wall clock + `Math.random`). */ export declare const SYSTEM_CLOCK: FormulaClock; /** The set of cells whose displayed value changed as a result of a recompute. */ export interface RecalculationResult { /** Stable row ids of every cell whose value changed. */ readonly changedNodeIds: ReadonlySet; } export declare class CalculationEngine { private readonly adapter; private readonly store; private readonly configManager; private readonly registry; private readonly evaluator; private readonly namedRanges; private readonly clock; private readonly resolver; private readonly graph; private readonly cache; /** * Live set of volatile formula cells, maintained incrementally so gathering * them is O(volatile count) rather than an O(store size) scan on every edit. */ private readonly volatileCells; /** * @param adapter - Port to grid cell values and coordinate spaces. * @param store - The formula-cell store (shared with the facade). * @param configManager - Resolved-config provider. * @param registry - Function registry for the evaluation context. * @param evaluator - The shared AST evaluator. * @param namedRanges - Runtime named-range registry. * @param clock - Time/random source for volatile functions. */ constructor(adapter: FormulaGridAdapter, store: FormulaStore, configManager: ConfigurationManager, registry: FunctionRegistry, evaluator: Evaluator, namedRanges: NamedRangeManager, clock?: FormulaClock); /** * Compiles and stores a formula on a cell, updates the dependency graph, and * recomputes that cell plus everything downstream of it. * * @param nodeId - Stable row identity. * @param colId - Immutable column identity. * @param source - Raw formula source (including the leading `=`). * @returns The cells whose value changed. */ setFormula(nodeId: string, colId: string, source: string): RecalculationResult; /** * Bulk-registers many formulas and recomputes once. Each registration defers * its own recompute, so seeding N declarative formulas at load costs a single * ordered pass over the seeded cells plus their dependents (and volatiles) — * not N cascading recomputes. Forward references resolve correctly because the * final pass is topologically ordered. * * @param entries - The formulas to register. * @returns The cells whose value changed. */ registerFormulas(entries: readonly { nodeId: string; colId: string; source: string; }[]): RecalculationResult; /** * Removes every formula cell owned by any of `nodeIds` (e.g. deleted rows) and * recomputes the dependents that referenced them, so structural row changes * leave no orphaned store/graph entries. * * @param nodeIds - Stable ids of the removed rows. * @returns The cells whose value changed. */ purgeNodes(nodeIds: ReadonlySet): RecalculationResult; /** * Compiles a formula, stores the cell, and (re)wires its dependency-graph edges * — WITHOUT recomputing. The shared registration step behind {@link setFormula} * and {@link registerFormulas}. * * @returns The cell's stable {@link CellId}. */ private registerFormula; /** * Adds per-row precedents for a formula's row-relative bare names (field-name * `quantity` or column-letter `B`). A row-relative reference always points at * the SAME row as the formula cell, so each precedent is `(nodeId, resolvedColId)`. * Named ranges are skipped (resolved at eval time, as today). * * @param nodeId - The formula cell's row (also its precedents' row). * @param ast - The compiled formula AST. * @param out - Precedent list to append to (mutated). */ private addNameDependencies; /** * Removes a cell's formula (leaving its last computed value in the grid) and * recomputes anything that depended on it. * * @returns `{ removed, result }` — whether a formula existed, and the changes. */ removeFormula(nodeId: string, colId: string): { removed: boolean; result: RecalculationResult; }; /** * Reacts to one or more non-formula cell changes (edit/paste/fill/undo): * recomputes their dependents plus all volatile cells. * * @param changes - The changed cells. * @returns The cells whose value changed. */ onCellsChanged(changes: readonly { nodeId: string; colId: string; }[]): RecalculationResult; /** * Recomputes formula cells. When `force` is `true`, every formula cell is * recomputed; otherwise only volatile cells (and their dependents). * * @param force - Recompute all cells, not just volatile ones. * @returns The cells whose value changed. */ recalculate(force: boolean): RecalculationResult; /** Detaches all formulas and graph edges. */ clear(): void; /** * Builds the transitive dirty set. `formulaSeeds` are formula cells that must * themselves recompute (an edited formula, volatile cells); `literalSeeds` are * changed non-formula cells whose *dependents* must recompute. */ private buildDirty; /** Adds `cellId`'s not-yet-seen dependents to `dirty` and the work `stack`. */ private addDependents; /** The formula seeds for a `setFormula`: the edited cell plus all volatiles. */ private formulaSeeds; /** Every currently-volatile formula cell id. */ private volatileCellIds; /** Orders `dirty` precedents-first and evaluates it, writing changed values back. */ private recompute; /** Writes a cell's new value to the store + grid, tracking real changes. */ private assign; /** Builds the evaluation context for a recompute pass. */ private buildContext; /** Positional coordinates of a stored cell (`-1` when off-grid). */ private position; } //# sourceMappingURL=calculation-engine.d.ts.map