{"version":3,"sources":["/Users/shyun/comcom/ain-enterprise/ain-adk/dist/cjs/chunk-WFCHWNIX.cjs","../../src/services/workflow-table/formula-evaluator.ts"],"names":[],"mappings":"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","file":"/Users/shyun/comcom/ain-enterprise/ain-adk/dist/cjs/chunk-WFCHWNIX.cjs","sourcesContent":[null,"import {\n\ttype MatrixCellRef,\n\ttype MatrixDefinition,\n\ttype MatrixTable,\n\ttype ParsedMatrixFormula,\n\ttype ParsedRecordFormula,\n\tparseNumberish,\n\tparseRecordCell,\n\ttype RecordDefinition,\n\ttype RecordTableRow,\n\tsafeDivide,\n} from \"@/services/workflow-table/shared.js\";\nimport type { WorkflowTableColumnFormat } from \"@/types/memory.js\";\n\nexport class WorkflowTableFormulaEvaluator {\n\tevaluateMatrix(\n\t\trawContent: string,\n\t\tdefinition: MatrixDefinition,\n\t): { matrix: MatrixTable; warnings: string[] } {\n\t\tconst warnings: string[] = [];\n\t\tconst extracted = this.parseMatrixContent(\n\t\t\trawContent,\n\t\t\tdefinition.sourceRows,\n\t\t\tdefinition.sourceColumns,\n\t\t);\n\t\tconst matrix = this.createEmptyMatrix(definition.rows, definition.columns);\n\n\t\tfor (const row of definition.sourceRows) {\n\t\t\tfor (const column of definition.sourceColumns) {\n\t\t\t\tmatrix[row][column] = extracted[row]?.[column] ?? null;\n\t\t\t}\n\t\t}\n\n\t\tthis.applyMatrixFormulas(definition, matrix, warnings);\n\t\treturn { matrix, warnings };\n\t}\n\n\tevaluateRecords(\n\t\trawContent: string,\n\t\tdefinition: RecordDefinition,\n\t): {\n\t\trows: RecordTableRow[];\n\t\ttotalRow?: RecordTableRow;\n\t\twarnings: string[];\n\t} {\n\t\tthis.validateRecordFormulaDependencies(definition);\n\n\t\tconst warnings: string[] = [];\n\t\tconst rows = this.parseRecordContent(\n\t\t\trawContent,\n\t\t\tdefinition.sourceColumns,\n\t\t\tdefinition.columnFormats,\n\t\t);\n\n\t\tfor (const formula of definition.formulas) {\n\t\t\tif (formula.type === \"expression\") {\n\t\t\t\tthis.applyRecordExpressionFormula(formula, rows, warnings);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (formula.type === \"sum\") {\n\t\t\t\tthis.applyRecordSumFormula(formula, rows);\n\t\t\t}\n\t\t}\n\n\t\tlet totalRow: RecordTableRow | undefined;\n\t\tif (definition.totalFormula) {\n\t\t\ttotalRow = this.buildRecordTotalRow(definition, rows);\n\t\t\tconst totalIndex = definition.formulas.findIndex(\n\t\t\t\t(formula) => formula.type === \"total\",\n\t\t\t);\n\t\t\tconst postTotalFormulas = definition.formulas.slice(totalIndex + 1);\n\t\t\tfor (const formula of postTotalFormulas) {\n\t\t\t\tif (formula.type === \"expression\") {\n\t\t\t\t\tthis.applyRecordExpressionFormula(formula, [totalRow], warnings);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (formula.type === \"sum\") {\n\t\t\t\t\tthis.applyRecordSumFormula(formula, [totalRow]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { rows, totalRow, warnings };\n\t}\n\n\tprivate validateRecordFormulaDependencies(\n\t\tdefinition: RecordDefinition,\n\t): void {\n\t\tconst computedFormulas = definition.formulas.filter(\n\t\t\t(formula): formula is Exclude<ParsedRecordFormula, { type: \"total\" }> =>\n\t\t\t\tformula.type !== \"total\",\n\t\t);\n\t\tconst available = new Set(definition.sourceColumns);\n\t\tconst producedByFormula = computedFormulas.map((formula) => formula.target);\n\n\t\tfor (const [index, formula] of computedFormulas.entries()) {\n\t\t\tconst futureProduced = new Set(producedByFormula.slice(index + 1));\n\t\t\tconst referencedColumns =\n\t\t\t\tformula.type === \"expression\"\n\t\t\t\t\t? formula.operands\n\t\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t\t(operand): operand is { kind: \"column\"; name: string } =>\n\t\t\t\t\t\t\t\t\toperand.kind === \"column\",\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.map((operand) => operand.name)\n\t\t\t\t\t: formula.columns;\n\t\t\tconst laterColumns = referencedColumns.filter(\n\t\t\t\t(column) => !available.has(column) && futureProduced.has(column),\n\t\t\t);\n\t\t\tif (laterColumns.length > 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Record formula \"${formula.raw}\" depends on values from later formulas: ${laterColumns.join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst missingColumns = referencedColumns.filter(\n\t\t\t\t(column) => !available.has(column) && !futureProduced.has(column),\n\t\t\t);\n\t\t\tif (missingColumns.length > 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Record formula \"${formula.raw}\" references values that are never produced: ${missingColumns.join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tavailable.add(formula.target);\n\t\t}\n\t}\n\n\tprivate parseMatrixContent(\n\t\trawContent: string,\n\t\trows: string[],\n\t\tcolumns: string[],\n\t): MatrixTable {\n\t\tconst jsonText = this.extractJsonValue(rawContent, \"object\");\n\t\tconst parsed = JSON.parse(jsonText) as Record<string, unknown>;\n\t\tconst matrix = this.createEmptyMatrix(rows, columns);\n\n\t\tfor (const row of rows) {\n\t\t\tconst rowValue = parsed[row];\n\t\t\tif (\n\t\t\t\t!rowValue ||\n\t\t\t\ttypeof rowValue !== \"object\" ||\n\t\t\t\tArray.isArray(rowValue)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const column of columns) {\n\t\t\t\tmatrix[row][column] = parseNumberish(\n\t\t\t\t\t(rowValue as Record<string, unknown>)[column],\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn matrix;\n\t}\n\n\tprivate parseRecordContent(\n\t\trawContent: string,\n\t\tsourceColumns: string[],\n\t\tcolumnFormats: Record<string, WorkflowTableColumnFormat>,\n\t): RecordTableRow[] {\n\t\tconst jsonText = this.extractJsonValue(rawContent, \"array\");\n\t\tconst parsed = JSON.parse(jsonText) as unknown;\n\t\tif (!Array.isArray(parsed)) {\n\t\t\tthrow new Error(\"Record table extraction did not return a JSON array.\");\n\t\t}\n\n\t\treturn parsed\n\t\t\t.filter(\n\t\t\t\t(item): item is Record<string, unknown> =>\n\t\t\t\t\tBoolean(item) && typeof item === \"object\" && !Array.isArray(item),\n\t\t\t)\n\t\t\t.map((item) =>\n\t\t\t\tObject.fromEntries(\n\t\t\t\t\tsourceColumns.map((column) => [\n\t\t\t\t\t\tcolumn,\n\t\t\t\t\t\tparseRecordCell(item[column], columnFormats[column]),\n\t\t\t\t\t]),\n\t\t\t\t),\n\t\t\t);\n\t}\n\n\tprivate extractJsonValue(\n\t\trawContent: string,\n\t\texpected: \"object\" | \"array\",\n\t): string {\n\t\tconst trimmed = rawContent.trim();\n\t\tconst unfenced = trimmed.startsWith(\"```\")\n\t\t\t? trimmed\n\t\t\t\t\t.replace(/^```[a-zA-Z]*\\n?/, \"\")\n\t\t\t\t\t.replace(/\\n?```$/, \"\")\n\t\t\t\t\t.trim()\n\t\t\t: trimmed;\n\n\t\tif (expected === \"array\") {\n\t\t\tconst firstBracket = unfenced.indexOf(\"[\");\n\t\t\tconst lastBracket = unfenced.lastIndexOf(\"]\");\n\t\t\tif (\n\t\t\t\tfirstBracket === -1 ||\n\t\t\t\tlastBracket === -1 ||\n\t\t\t\tlastBracket < firstBracket\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Table extraction did not return a JSON array.\");\n\t\t\t}\n\t\t\treturn unfenced.slice(firstBracket, lastBracket + 1);\n\t\t}\n\n\t\tconst firstBrace = unfenced.indexOf(\"{\");\n\t\tconst lastBrace = unfenced.lastIndexOf(\"}\");\n\t\tif (firstBrace === -1 || lastBrace === -1 || lastBrace < firstBrace) {\n\t\t\tthrow new Error(\"Table extraction did not return a JSON object.\");\n\t\t}\n\t\treturn unfenced.slice(firstBrace, lastBrace + 1);\n\t}\n\n\tprivate createEmptyMatrix(rows: string[], columns: string[]): MatrixTable {\n\t\treturn Object.fromEntries(\n\t\t\trows.map((row) => [\n\t\t\t\trow,\n\t\t\t\tObject.fromEntries(columns.map((column) => [column, null])),\n\t\t\t]),\n\t\t);\n\t}\n\n\tprivate applyMatrixFormulas(\n\t\tdefinition: MatrixDefinition,\n\t\tmatrix: MatrixTable,\n\t\twarnings: string[],\n\t): void {\n\t\tconst initialAvailable = this.buildInitialMatrixAvailability(definition);\n\n\t\tconst producedByFormula: Set<MatrixCellRef>[] = [];\n\t\tconst projectedAvailable = new Set(initialAvailable);\n\t\tfor (const formula of definition.formulas) {\n\t\t\tconst refs = this.getMatrixProducedRefs(\n\t\t\t\tformula,\n\t\t\t\tdefinition,\n\t\t\t\tprojectedAvailable,\n\t\t\t);\n\t\t\tproducedByFormula.push(refs);\n\t\t\tfor (const ref of refs) {\n\t\t\t\tprojectedAvailable.add(ref);\n\t\t\t}\n\t\t}\n\n\t\tconst available = new Set(initialAvailable);\n\t\tfor (const [index, formula] of definition.formulas.entries()) {\n\t\t\tconst futureProduced = new Set<MatrixCellRef>(\n\t\t\t\tproducedByFormula.slice(index + 1).flatMap((refs) => [...refs]),\n\t\t\t);\n\t\t\tthis.validateMatrixFormulaDependencies(\n\t\t\t\tformula,\n\t\t\t\tdefinition,\n\t\t\t\tavailable,\n\t\t\t\tfutureProduced,\n\t\t\t);\n\t\t\tthis.applyMatrixFormula(formula, definition, matrix, warnings);\n\t\t\tfor (const ref of producedByFormula[index]) {\n\t\t\t\tavailable.add(ref);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate buildInitialMatrixAvailability(\n\t\tdefinition: MatrixDefinition,\n\t): Set<MatrixCellRef> {\n\t\tconst refs = new Set<MatrixCellRef>();\n\t\tfor (const row of definition.sourceRows) {\n\t\t\tfor (const column of definition.sourceColumns) {\n\t\t\t\trefs.add(this.toMatrixCellRef(row, column));\n\t\t\t}\n\t\t}\n\t\treturn refs;\n\t}\n\n\tprivate getMatrixProducedRefs(\n\t\tformula: ParsedMatrixFormula,\n\t\tdefinition: MatrixDefinition,\n\t\tavailable: Set<MatrixCellRef>,\n\t): Set<MatrixCellRef> {\n\t\tswitch (formula.type) {\n\t\t\tcase \"col_sum\":\n\t\t\t\treturn new Set(\n\t\t\t\t\tdefinition.rows\n\t\t\t\t\t\t.filter((row) =>\n\t\t\t\t\t\t\tformula.args.every((column) =>\n\t\t\t\t\t\t\t\tavailable.has(this.toMatrixCellRef(row, column)),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.map((row) => this.toMatrixCellRef(row, formula.target)),\n\t\t\t\t);\n\t\t\tcase \"row_sum\":\n\t\t\t\treturn new Set(\n\t\t\t\t\tdefinition.columns\n\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t(column) =>\n\t\t\t\t\t\t\t\t!definition.comparisonColumnTargets.has(column) &&\n\t\t\t\t\t\t\t\tformula.args.every((row) =>\n\t\t\t\t\t\t\t\t\tavailable.has(this.toMatrixCellRef(row, column)),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.map((column) => this.toMatrixCellRef(formula.target, column)),\n\t\t\t\t);\n\t\t\tcase \"row_share\": {\n\t\t\t\tconst [, baseColumn] = formula.args;\n\t\t\t\tconst baseIndex = definition.columns.indexOf(baseColumn);\n\t\t\t\tif (baseIndex === -1) {\n\t\t\t\t\tthrow new Error(`Invalid share formula: ${formula.raw}`);\n\t\t\t\t}\n\t\t\t\treturn new Set(\n\t\t\t\t\tdefinition.columns\n\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t(column, index) =>\n\t\t\t\t\t\t\t\tindex <= baseIndex &&\n\t\t\t\t\t\t\t\t!definition.comparisonColumnTargets.has(column),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.map((column) => this.toMatrixCellRef(formula.target, column)),\n\t\t\t\t);\n\t\t\t}\n\t\t\tcase \"row_ratio\":\n\t\t\tcase \"row_delta\":\n\t\t\tcase \"row_rate\":\n\t\t\tcase \"row_growth\":\n\t\t\t\treturn new Set(\n\t\t\t\t\tdefinition.columns\n\t\t\t\t\t\t.filter((column) => !definition.comparisonColumnTargets.has(column))\n\t\t\t\t\t\t.map((column) => this.toMatrixCellRef(formula.target, column)),\n\t\t\t\t);\n\t\t\tcase \"col_share\": {\n\t\t\t\tconst [, baseRow] = formula.args;\n\t\t\t\tconst baseIndex = definition.rows.indexOf(baseRow);\n\t\t\t\tif (baseIndex === -1) {\n\t\t\t\t\tthrow new Error(`Invalid col_share formula: ${formula.raw}`);\n\t\t\t\t}\n\t\t\t\treturn new Set(\n\t\t\t\t\tdefinition.rows\n\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t(row, index) =>\n\t\t\t\t\t\t\t\tindex <= baseIndex && !definition.comparisonRowTargets.has(row),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.map((row) => this.toMatrixCellRef(row, formula.target)),\n\t\t\t\t);\n\t\t\t}\n\t\t\tcase \"col_ratio\":\n\t\t\t\treturn new Set(\n\t\t\t\t\tdefinition.rows\n\t\t\t\t\t\t.filter((row) => !definition.comparisonRowTargets.has(row))\n\t\t\t\t\t\t.map((row) => this.toMatrixCellRef(row, formula.target)),\n\t\t\t\t);\n\t\t\tcase \"col_delta\":\n\t\t\tcase \"col_rate\":\n\t\t\tcase \"col_growth\":\n\t\t\t\treturn new Set(\n\t\t\t\t\tdefinition.rows.map((row) =>\n\t\t\t\t\t\tthis.toMatrixCellRef(row, formula.target),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t}\n\t}\n\n\tprivate validateMatrixFormulaDependencies(\n\t\tformula: ParsedMatrixFormula,\n\t\tdefinition: MatrixDefinition,\n\t\tavailable: Set<MatrixCellRef>,\n\t\tfutureProduced: Set<MatrixCellRef>,\n\t): void {\n\t\tswitch (formula.type) {\n\t\t\tcase \"col_sum\":\n\t\t\t\tthis.assertMatrixRefsAvailable(\n\t\t\t\t\tformula.raw,\n\t\t\t\t\tdefinition.sourceRows.flatMap((row) =>\n\t\t\t\t\t\tformula.args.map((column) => this.toMatrixCellRef(row, column)),\n\t\t\t\t\t),\n\t\t\t\t\tavailable,\n\t\t\t\t\tfutureProduced,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\tcase \"row_sum\":\n\t\t\t\tthis.assertMatrixRefsAvailable(\n\t\t\t\t\tformula.raw,\n\t\t\t\t\tdefinition.sourceColumns.flatMap((column) =>\n\t\t\t\t\t\tformula.args.map((row) => this.toMatrixCellRef(row, column)),\n\t\t\t\t\t),\n\t\t\t\t\tavailable,\n\t\t\t\t\tfutureProduced,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\tcase \"row_share\": {\n\t\t\t\tconst [sourceRow, baseColumn] = formula.args;\n\t\t\t\tconst baseIndex = definition.columns.indexOf(baseColumn);\n\t\t\t\tif (baseIndex === -1 || !definition.rows.includes(sourceRow)) {\n\t\t\t\t\tthrow new Error(`Invalid share formula: ${formula.raw}`);\n\t\t\t\t}\n\t\t\t\tthis.assertMatrixRefsAvailable(\n\t\t\t\t\tformula.raw,\n\t\t\t\t\tdefinition.columns\n\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t(column, index) =>\n\t\t\t\t\t\t\t\tindex <= baseIndex &&\n\t\t\t\t\t\t\t\t!definition.comparisonColumnTargets.has(column),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.map((column) => this.toMatrixCellRef(sourceRow, column)),\n\t\t\t\t\tavailable,\n\t\t\t\t\tfutureProduced,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"row_ratio\":\n\t\t\tcase \"row_delta\":\n\t\t\tcase \"row_rate\":\n\t\t\tcase \"row_growth\": {\n\t\t\t\tconst [leftRow, rightRow] = formula.args;\n\t\t\t\tthis.assertMatrixRefsAvailable(\n\t\t\t\t\tformula.raw,\n\t\t\t\t\tdefinition.columns\n\t\t\t\t\t\t.filter((column) => !definition.comparisonColumnTargets.has(column))\n\t\t\t\t\t\t.flatMap((column) => [\n\t\t\t\t\t\t\tthis.toMatrixCellRef(leftRow, column),\n\t\t\t\t\t\t\tthis.toMatrixCellRef(rightRow, column),\n\t\t\t\t\t\t]),\n\t\t\t\t\tavailable,\n\t\t\t\t\tfutureProduced,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"col_share\": {\n\t\t\t\tconst [sourceColumn, baseRow] = formula.args;\n\t\t\t\tconst baseIndex = definition.rows.indexOf(baseRow);\n\t\t\t\tif (baseIndex === -1 || !definition.columns.includes(sourceColumn)) {\n\t\t\t\t\tthrow new Error(`Invalid col_share formula: ${formula.raw}`);\n\t\t\t\t}\n\t\t\t\tthis.assertMatrixRefsAvailable(\n\t\t\t\t\tformula.raw,\n\t\t\t\t\tdefinition.rows\n\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t(row, index) =>\n\t\t\t\t\t\t\t\tindex <= baseIndex && !definition.comparisonRowTargets.has(row),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.map((row) => this.toMatrixCellRef(row, sourceColumn)),\n\t\t\t\t\tavailable,\n\t\t\t\t\tfutureProduced,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"col_ratio\": {\n\t\t\t\tconst [numeratorColumn, denominatorColumn] = formula.args;\n\t\t\t\tthis.assertMatrixRefsAvailable(\n\t\t\t\t\tformula.raw,\n\t\t\t\t\tdefinition.rows\n\t\t\t\t\t\t.filter((row) => !definition.comparisonRowTargets.has(row))\n\t\t\t\t\t\t.flatMap((row) => [\n\t\t\t\t\t\t\tthis.toMatrixCellRef(row, numeratorColumn),\n\t\t\t\t\t\t\tthis.toMatrixCellRef(row, denominatorColumn),\n\t\t\t\t\t\t]),\n\t\t\t\t\tavailable,\n\t\t\t\t\tfutureProduced,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"col_delta\":\n\t\t\tcase \"col_rate\":\n\t\t\tcase \"col_growth\": {\n\t\t\t\tconst [leftColumn, rightColumn] = formula.args;\n\t\t\t\tconst blockingRefs = definition.rows.flatMap((row) =>\n\t\t\t\t\t[leftColumn, rightColumn]\n\t\t\t\t\t\t.map((column) => this.toMatrixCellRef(row, column))\n\t\t\t\t\t\t.filter((ref) => !available.has(ref) && futureProduced.has(ref)),\n\t\t\t\t);\n\t\t\t\tif (blockingRefs.length > 0) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Matrix formula \"${formula.raw}\" depends on values from later formulas: ${blockingRefs.join(\", \")}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate assertMatrixRefsAvailable(\n\t\tformula: string,\n\t\trefs: MatrixCellRef[],\n\t\tavailable: Set<MatrixCellRef>,\n\t\tfutureProduced: Set<MatrixCellRef>,\n\t): void {\n\t\tconst laterRefs = refs.filter(\n\t\t\t(ref) => !available.has(ref) && futureProduced.has(ref),\n\t\t);\n\t\tif (laterRefs.length > 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`Matrix formula \"${formula}\" depends on values from later formulas: ${laterRefs.join(\", \")}`,\n\t\t\t);\n\t\t}\n\n\t\tconst missingRefs = refs.filter(\n\t\t\t(ref) => !available.has(ref) && !futureProduced.has(ref),\n\t\t);\n\t\tif (missingRefs.length > 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`Matrix formula \"${formula}\" references values that are never produced: ${missingRefs.join(\", \")}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate toMatrixCellRef(row: string, column: string): MatrixCellRef {\n\t\treturn `${row}::${column}`;\n\t}\n\n\tprivate applyMatrixFormula(\n\t\tformula: ParsedMatrixFormula,\n\t\tdefinition: MatrixDefinition,\n\t\tmatrix: MatrixTable,\n\t\twarnings: string[],\n\t): void {\n\t\tswitch (formula.type) {\n\t\t\tcase \"col_sum\":\n\t\t\t\tfor (const row of definition.rows) {\n\t\t\t\t\tconst values = formula.args.map(\n\t\t\t\t\t\t(column) => matrix[row]?.[column] ?? null,\n\t\t\t\t\t);\n\t\t\t\t\tif (values.every((value) => value === null)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tmatrix[row][formula.target] = values.reduce<number>(\n\t\t\t\t\t\t(sum, value) => sum + (value ?? 0),\n\t\t\t\t\t\t0,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase \"row_sum\":\n\t\t\t\tfor (const column of definition.columns) {\n\t\t\t\t\tif (definition.comparisonColumnTargets.has(column)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst isSourceColumn = definition.sourceColumns.includes(column);\n\t\t\t\t\tconst values = formula.args.map(\n\t\t\t\t\t\t(row) => matrix[row]?.[column] ?? null,\n\t\t\t\t\t);\n\t\t\t\t\tif (!isSourceColumn && values.every((value) => value === null)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tmatrix[formula.target][column] = values.reduce<number>(\n\t\t\t\t\t\t(sum, value) => sum + (value ?? 0),\n\t\t\t\t\t\t0,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase \"row_share\":\n\t\t\t\tthis.applyMatrixShareFormula(formula, definition, matrix, warnings);\n\t\t\t\treturn;\n\t\t\tcase \"row_ratio\":\n\t\t\t\tthis.applyMatrixRatioFormula(formula, definition, matrix, warnings);\n\t\t\t\treturn;\n\t\t\tcase \"row_delta\":\n\t\t\tcase \"row_rate\":\n\t\t\tcase \"row_growth\":\n\t\t\t\tthis.applyMatrixRowComparisonFormula(\n\t\t\t\t\tformula,\n\t\t\t\t\tdefinition,\n\t\t\t\t\tmatrix,\n\t\t\t\t\twarnings,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\tcase \"col_share\":\n\t\t\t\tthis.applyMatrixColShareFormula(formula, definition, matrix, warnings);\n\t\t\t\treturn;\n\t\t\tcase \"col_ratio\":\n\t\t\t\tthis.applyMatrixColRatioFormula(formula, definition, matrix, warnings);\n\t\t\t\treturn;\n\t\t\tcase \"col_delta\":\n\t\t\tcase \"col_rate\":\n\t\t\tcase \"col_growth\":\n\t\t\t\tthis.applyMatrixColumnComparisonFormula(\n\t\t\t\t\tformula,\n\t\t\t\t\tdefinition,\n\t\t\t\t\tmatrix,\n\t\t\t\t\twarnings,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate applyMatrixShareFormula(\n\t\tformula: Extract<ParsedMatrixFormula, { type: \"row_share\" }>,\n\t\tdefinition: MatrixDefinition,\n\t\tmatrix: MatrixTable,\n\t\twarnings: string[],\n\t): void {\n\t\tconst [sourceRow, baseColumn] = formula.args;\n\t\tconst baseIndex = definition.columns.indexOf(baseColumn);\n\t\tif (baseIndex === -1 || !matrix[sourceRow]) {\n\t\t\tthrow new Error(`Invalid share formula: ${formula.raw}`);\n\t\t}\n\n\t\tconst baseValue = matrix[sourceRow][baseColumn];\n\t\tfor (const [index, column] of definition.columns.entries()) {\n\t\t\tif (index > baseIndex || definition.comparisonColumnTargets.has(column)) {\n\t\t\t\tmatrix[formula.target][column] = null;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tmatrix[formula.target][column] = safeDivide(\n\t\t\t\tmatrix[sourceRow][column],\n\t\t\t\tbaseValue,\n\t\t\t\twarnings,\n\t\t\t\t`${formula.target}.${column}`,\n\t\t\t\t100,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate applyMatrixRatioFormula(\n\t\tformula: Extract<ParsedMatrixFormula, { type: \"row_ratio\" }>,\n\t\tdefinition: MatrixDefinition,\n\t\tmatrix: MatrixTable,\n\t\twarnings: string[],\n\t): void {\n\t\tconst [numeratorRow, denominatorRow] = formula.args;\n\t\tif (!matrix[numeratorRow] || !matrix[denominatorRow]) {\n\t\t\tthrow new Error(`Invalid ratio formula: ${formula.raw}`);\n\t\t}\n\n\t\tfor (const column of definition.columns) {\n\t\t\tif (definition.comparisonColumnTargets.has(column)) {\n\t\t\t\tmatrix[formula.target][column] = null;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tmatrix[formula.target][column] = safeDivide(\n\t\t\t\tmatrix[numeratorRow][column],\n\t\t\t\tmatrix[denominatorRow][column],\n\t\t\t\twarnings,\n\t\t\t\t`${formula.target}.${column}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate applyMatrixColShareFormula(\n\t\tformula: Extract<ParsedMatrixFormula, { type: \"col_share\" }>,\n\t\tdefinition: MatrixDefinition,\n\t\tmatrix: MatrixTable,\n\t\twarnings: string[],\n\t): void {\n\t\tconst [sourceColumn, baseRow] = formula.args;\n\t\tconst baseIndex = definition.rows.indexOf(baseRow);\n\t\tif (baseIndex === -1 || !matrix[baseRow]) {\n\t\t\tthrow new Error(`Invalid col_share formula: ${formula.raw}`);\n\t\t}\n\n\t\tconst baseValue = matrix[baseRow][sourceColumn];\n\t\tfor (const [index, row] of definition.rows.entries()) {\n\t\t\tif (index > baseIndex || definition.comparisonRowTargets.has(row)) {\n\t\t\t\tmatrix[row][formula.target] = null;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tmatrix[row][formula.target] = safeDivide(\n\t\t\t\tmatrix[row]?.[sourceColumn] ?? null,\n\t\t\t\tbaseValue,\n\t\t\t\twarnings,\n\t\t\t\t`${formula.target}.${row}`,\n\t\t\t\t100,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate applyMatrixColRatioFormula(\n\t\tformula: Extract<ParsedMatrixFormula, { type: \"col_ratio\" }>,\n\t\tdefinition: MatrixDefinition,\n\t\tmatrix: MatrixTable,\n\t\twarnings: string[],\n\t): void {\n\t\tconst [numeratorColumn, denominatorColumn] = formula.args;\n\t\tfor (const row of definition.rows) {\n\t\t\tif (definition.comparisonRowTargets.has(row)) {\n\t\t\t\tmatrix[row][formula.target] = null;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tmatrix[row][formula.target] = safeDivide(\n\t\t\t\tmatrix[row]?.[numeratorColumn] ?? null,\n\t\t\t\tmatrix[row]?.[denominatorColumn] ?? null,\n\t\t\t\twarnings,\n\t\t\t\t`${formula.target}.${row}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate applyMatrixRowComparisonFormula(\n\t\tformula: Extract<\n\t\t\tParsedMatrixFormula,\n\t\t\t{ type: \"row_delta\" | \"row_rate\" | \"row_growth\" }\n\t\t>,\n\t\tdefinition: MatrixDefinition,\n\t\tmatrix: MatrixTable,\n\t\twarnings: string[],\n\t): void {\n\t\tconst [leftRow, rightRow] = formula.args;\n\t\tif (!matrix[leftRow] || !matrix[rightRow]) {\n\t\t\tthrow new Error(`Invalid ${formula.type} formula: ${formula.raw}`);\n\t\t}\n\n\t\tfor (const column of definition.columns) {\n\t\t\tif (definition.comparisonColumnTargets.has(column)) {\n\t\t\t\tmatrix[formula.target][column] = null;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst leftValue = matrix[leftRow][column];\n\t\t\tconst rightValue = matrix[rightRow][column];\n\n\t\t\tswitch (formula.type) {\n\t\t\t\tcase \"row_delta\":\n\t\t\t\t\tmatrix[formula.target][column] =\n\t\t\t\t\t\tleftValue === null || rightValue === null\n\t\t\t\t\t\t\t? null\n\t\t\t\t\t\t\t: leftValue - rightValue;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"row_rate\":\n\t\t\t\t\tmatrix[formula.target][column] = safeDivide(\n\t\t\t\t\t\tleftValue,\n\t\t\t\t\t\trightValue,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t\t`${formula.target}.${column}`,\n\t\t\t\t\t\t100,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"row_growth\":\n\t\t\t\t\tif (leftValue === null || rightValue === null) {\n\t\t\t\t\t\tmatrix[formula.target][column] = null;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tmatrix[formula.target][column] = safeDivide(\n\t\t\t\t\t\tleftValue - rightValue,\n\t\t\t\t\t\trightValue,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t\t`${formula.target}.${column}`,\n\t\t\t\t\t\t100,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate applyMatrixColumnComparisonFormula(\n\t\tformula: Extract<\n\t\t\tParsedMatrixFormula,\n\t\t\t{ type: \"col_delta\" | \"col_rate\" | \"col_growth\" }\n\t\t>,\n\t\tdefinition: MatrixDefinition,\n\t\tmatrix: MatrixTable,\n\t\twarnings: string[],\n\t): void {\n\t\tconst [leftColumn, rightColumn] = formula.args;\n\t\tfor (const row of definition.rows) {\n\t\t\tconst leftValue = matrix[row]?.[leftColumn] ?? null;\n\t\t\tconst rightValue = matrix[row]?.[rightColumn] ?? null;\n\n\t\t\tswitch (formula.type) {\n\t\t\t\tcase \"col_delta\":\n\t\t\t\t\tmatrix[row][formula.target] =\n\t\t\t\t\t\tleftValue === null || rightValue === null\n\t\t\t\t\t\t\t? null\n\t\t\t\t\t\t\t: leftValue - rightValue;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"col_rate\":\n\t\t\t\t\tmatrix[row][formula.target] = safeDivide(\n\t\t\t\t\t\tleftValue,\n\t\t\t\t\t\trightValue,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t\t`${row}.${formula.target}`,\n\t\t\t\t\t\t100,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"col_growth\":\n\t\t\t\t\tif (leftValue === null || rightValue === null) {\n\t\t\t\t\t\tmatrix[row][formula.target] = null;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tmatrix[row][formula.target] = safeDivide(\n\t\t\t\t\t\tleftValue - rightValue,\n\t\t\t\t\t\trightValue,\n\t\t\t\t\t\twarnings,\n\t\t\t\t\t\t`${row}.${formula.target}`,\n\t\t\t\t\t\t100,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate applyRecordExpressionFormula(\n\t\tformula: Extract<ParsedRecordFormula, { type: \"expression\" }>,\n\t\trows: RecordTableRow[],\n\t\twarnings: string[],\n\t): void {\n\t\tfor (const row of rows) {\n\t\t\tconst values = formula.operands.map((operand) =>\n\t\t\t\toperand.kind === \"column\"\n\t\t\t\t\t? parseNumberish(row[operand.name])\n\t\t\t\t\t: operand.value,\n\t\t\t);\n\t\t\trow[formula.target] = this.evaluateRecordExpression(\n\t\t\t\tvalues,\n\t\t\t\tformula.operators,\n\t\t\t\twarnings,\n\t\t\t\tformula.target,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate evaluateRecordExpression(\n\t\tvalues: Array<number | null>,\n\t\toperators: Array<\"+\" | \"-\" | \"*\" | \"/\">,\n\t\twarnings: string[],\n\t\tcontext: string,\n\t): number | null {\n\t\tconst valueStack: Array<number | null> = [values[0] ?? null];\n\t\tconst operatorStack: Array<\"+\" | \"-\" | \"*\" | \"/\"> = [];\n\n\t\tfor (const [index, operator] of operators.entries()) {\n\t\t\twhile (\n\t\t\t\toperatorStack.length > 0 &&\n\t\t\t\tthis.getRecordOperatorPrecedence(\n\t\t\t\t\toperatorStack[operatorStack.length - 1],\n\t\t\t\t) >= this.getRecordOperatorPrecedence(operator)\n\t\t\t) {\n\t\t\t\tthis.reduceRecordExpression(\n\t\t\t\t\tvalueStack,\n\t\t\t\t\toperatorStack,\n\t\t\t\t\twarnings,\n\t\t\t\t\tcontext,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\toperatorStack.push(operator);\n\t\t\tvalueStack.push(values[index + 1] ?? null);\n\t\t}\n\n\t\twhile (operatorStack.length > 0) {\n\t\t\tthis.reduceRecordExpression(valueStack, operatorStack, warnings, context);\n\t\t}\n\n\t\treturn valueStack[0] ?? null;\n\t}\n\n\tprivate reduceRecordExpression(\n\t\tvalueStack: Array<number | null>,\n\t\toperatorStack: Array<\"+\" | \"-\" | \"*\" | \"/\">,\n\t\twarnings: string[],\n\t\tcontext: string,\n\t): void {\n\t\tconst operator = operatorStack.pop();\n\t\tconst right = valueStack.pop();\n\t\tconst left = valueStack.pop();\n\t\tif (operator === undefined || left === undefined || right === undefined) {\n\t\t\tthrow new Error(\"Invalid record formula evaluation state.\");\n\t\t}\n\n\t\tswitch (operator) {\n\t\t\tcase \"+\":\n\t\t\t\tvalueStack.push(left === null || right === null ? null : left + right);\n\t\t\t\treturn;\n\t\t\tcase \"-\":\n\t\t\t\tvalueStack.push(left === null || right === null ? null : left - right);\n\t\t\t\treturn;\n\t\t\tcase \"*\":\n\t\t\t\tvalueStack.push(left === null || right === null ? null : left * right);\n\t\t\t\treturn;\n\t\t\tcase \"/\":\n\t\t\t\tvalueStack.push(safeDivide(left, right, warnings, context));\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate getRecordOperatorPrecedence(operator: \"+\" | \"-\" | \"*\" | \"/\"): number {\n\t\treturn operator === \"*\" || operator === \"/\" ? 2 : 1;\n\t}\n\n\tprivate applyRecordSumFormula(\n\t\tformula: Extract<ParsedRecordFormula, { type: \"sum\" }>,\n\t\trows: RecordTableRow[],\n\t): void {\n\t\tfor (const row of rows) {\n\t\t\tconst values = formula.columns.map((column) =>\n\t\t\t\tparseNumberish(row[column]),\n\t\t\t);\n\t\t\trow[formula.target] = values.every((value) => value === null)\n\t\t\t\t? null\n\t\t\t\t: values.reduce<number>((sum, value) => sum + (value ?? 0), 0);\n\t\t}\n\t}\n\n\tprivate buildRecordTotalRow(\n\t\tdefinition: RecordDefinition,\n\t\trows: RecordTableRow[],\n\t): RecordTableRow {\n\t\tconst totalRow = Object.fromEntries(\n\t\t\tdefinition.columns.map((column) => [column, null]),\n\t\t) as RecordTableRow;\n\n\t\tif (definition.columns.length > 0) {\n\t\t\ttotalRow[definition.columns[0]] = \"Total\";\n\t\t}\n\n\t\tfor (const column of definition.totalFormula?.columns || []) {\n\t\t\tconst numericValues = rows\n\t\t\t\t.map((row) => parseNumberish(row[column]))\n\t\t\t\t.filter((value): value is number => value !== null);\n\t\t\ttotalRow[column] =\n\t\t\t\tnumericValues.length === 0\n\t\t\t\t\t? null\n\t\t\t\t\t: numericValues.reduce((sum, value) => sum + value, 0);\n\t\t}\n\n\t\treturn totalRow;\n\t}\n}\n"]}