@cj-tech-master/excelts 9.2.1 → 9.3.0
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/README.md +25 -2
- package/README_zh.md +29 -6
- package/dist/browser/index.browser.d.ts +1 -1
- package/dist/browser/index.browser.js +4 -0
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.js +4 -0
- package/dist/browser/modules/excel/cell.d.ts +17 -3
- package/dist/browser/modules/excel/cell.js +170 -22
- package/dist/browser/modules/excel/defined-names.d.ts +96 -1
- package/dist/browser/modules/excel/defined-names.js +411 -21
- package/dist/browser/modules/excel/image.d.ts +11 -0
- package/dist/browser/modules/excel/image.js +24 -1
- package/dist/browser/modules/excel/stream/workbook-reader.browser.d.ts +9 -3
- package/dist/browser/modules/excel/stream/workbook-reader.browser.js +14 -0
- package/dist/browser/modules/excel/stream/workbook-reader.d.ts +2 -1
- package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +39 -5
- package/dist/browser/modules/excel/stream/workbook-writer.browser.js +48 -1
- package/dist/browser/modules/excel/stream/workbook-writer.d.ts +3 -2
- package/dist/browser/modules/excel/stream/worksheet-reader.js +17 -1
- package/dist/browser/modules/excel/stream/worksheet-writer.d.ts +39 -6
- package/dist/browser/modules/excel/stream/worksheet-writer.js +45 -5
- package/dist/browser/modules/excel/table.js +15 -2
- package/dist/browser/modules/excel/types.d.ts +133 -2
- package/dist/browser/modules/excel/utils/col-cache.d.ts +1 -0
- package/dist/browser/modules/excel/utils/col-cache.js +15 -0
- package/dist/browser/modules/excel/utils/drawing-utils.d.ts +3 -3
- package/dist/browser/modules/excel/utils/drawing-utils.js +4 -0
- package/dist/browser/modules/excel/utils/external-link-formula.d.ts +76 -0
- package/dist/browser/modules/excel/utils/external-link-formula.js +208 -0
- package/dist/browser/modules/excel/utils/iterate-stream.d.ts +9 -3
- package/dist/browser/modules/excel/utils/iterate-stream.js +3 -1
- package/dist/browser/modules/excel/utils/ooxml-paths.d.ts +19 -0
- package/dist/browser/modules/excel/utils/ooxml-paths.js +37 -2
- package/dist/browser/modules/excel/utils/shared-strings.d.ts +8 -3
- package/dist/browser/modules/excel/utils/shared-strings.js +21 -2
- package/dist/browser/modules/excel/utils/workbook-protection.d.ts +30 -0
- package/dist/browser/modules/excel/utils/workbook-protection.js +30 -0
- package/dist/browser/modules/excel/workbook.browser.d.ts +257 -6
- package/dist/browser/modules/excel/workbook.browser.js +318 -34
- package/dist/browser/modules/excel/workbook.d.ts +1 -1
- package/dist/browser/modules/excel/worksheet.d.ts +3 -1
- package/dist/browser/modules/excel/worksheet.js +21 -2
- package/dist/browser/modules/excel/xlsx/rel-type.d.ts +15 -0
- package/dist/browser/modules/excel/xlsx/rel-type.js +16 -1
- package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +6 -5
- package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
- package/dist/browser/modules/excel/xlsx/xform/book/external-link-xform.d.ts +84 -0
- package/dist/browser/modules/excel/xlsx/xform/book/external-link-xform.js +330 -0
- package/dist/browser/modules/excel/xlsx/xform/book/external-reference-xform.d.ts +17 -0
- package/dist/browser/modules/excel/xlsx/xform/book/external-reference-xform.js +24 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.d.ts +3 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-protection-xform.d.ts +20 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-protection-xform.js +66 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +19 -1
- package/dist/browser/modules/excel/xlsx/xform/core/metadata-xform.d.ts +56 -0
- package/dist/browser/modules/excel/xlsx/xform/core/metadata-xform.js +158 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.d.ts +26 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +105 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
- package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.d.ts +1 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
- package/dist/browser/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/ignored-errors-xform.d.ts +21 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +80 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
- package/dist/browser/modules/excel/xlsx/xform/style/border-xform.js +4 -1
- package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +172 -13
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +410 -20
- package/dist/browser/modules/excel/xlsx/xlsx.d.ts +7 -4
- package/dist/browser/modules/excel/xlsx/xlsx.js +4 -5
- package/dist/browser/modules/formula/compile/address-utils.d.ts +62 -0
- package/dist/browser/modules/formula/compile/address-utils.js +83 -0
- package/dist/browser/modules/formula/compile/binder.d.ts +42 -0
- package/dist/browser/modules/formula/compile/binder.js +487 -0
- package/dist/browser/modules/formula/compile/bound-ast.d.ts +230 -0
- package/dist/browser/modules/formula/compile/bound-ast.js +80 -0
- package/dist/browser/modules/formula/compile/compiled-formula.d.ts +137 -0
- package/dist/browser/modules/formula/compile/compiled-formula.js +383 -0
- package/dist/browser/modules/formula/compile/dependency-analysis.d.ts +93 -0
- package/dist/browser/modules/formula/compile/dependency-analysis.js +432 -0
- package/dist/browser/modules/formula/compile/structured-ref-utils.d.ts +93 -0
- package/dist/browser/modules/formula/compile/structured-ref-utils.js +136 -0
- package/dist/browser/modules/formula/default-syntax-probe.d.ts +79 -0
- package/dist/browser/modules/formula/default-syntax-probe.js +83 -0
- package/dist/browser/modules/formula/functions/_date-context.d.ts +4 -0
- package/dist/browser/modules/formula/functions/_date-context.js +29 -0
- package/dist/browser/modules/formula/functions/_shared.d.ts +121 -0
- package/dist/browser/modules/formula/functions/_shared.js +381 -0
- package/dist/browser/modules/formula/functions/conditional.d.ts +27 -0
- package/dist/browser/modules/formula/functions/conditional.js +343 -0
- package/dist/browser/modules/formula/functions/database.d.ts +37 -0
- package/dist/browser/modules/formula/functions/database.js +274 -0
- package/dist/browser/modules/formula/functions/date.d.ts +61 -0
- package/dist/browser/modules/formula/functions/date.js +855 -0
- package/dist/browser/modules/formula/functions/dynamic-array.d.ts +23 -0
- package/dist/browser/modules/formula/functions/dynamic-array.js +860 -0
- package/dist/browser/modules/formula/functions/engineering.d.ts +57 -0
- package/dist/browser/modules/formula/functions/engineering.js +1128 -0
- package/dist/browser/modules/formula/functions/financial.d.ts +202 -0
- package/dist/browser/modules/formula/functions/financial.js +2296 -0
- package/dist/browser/modules/formula/functions/lookup.d.ts +18 -0
- package/dist/browser/modules/formula/functions/lookup.js +886 -0
- package/dist/browser/modules/formula/functions/math.d.ts +114 -0
- package/dist/browser/modules/formula/functions/math.js +1406 -0
- package/dist/browser/modules/formula/functions/statistical.d.ts +193 -0
- package/dist/browser/modules/formula/functions/statistical.js +3390 -0
- package/dist/browser/modules/formula/functions/text.d.ts +86 -0
- package/dist/browser/modules/formula/functions/text.js +1845 -0
- package/dist/browser/modules/formula/host-registry.d.ts +53 -0
- package/dist/browser/modules/formula/host-registry.js +69 -0
- package/dist/browser/modules/formula/index.d.ts +39 -0
- package/dist/browser/modules/formula/index.js +49 -0
- package/dist/browser/modules/formula/install.d.ts +62 -0
- package/dist/browser/modules/formula/install.js +88 -0
- package/dist/browser/modules/formula/integration/apply-writeback-plan.d.ts +26 -0
- package/dist/browser/modules/formula/integration/apply-writeback-plan.js +210 -0
- package/dist/browser/modules/formula/integration/calculate-formulas-impl.d.ts +30 -0
- package/dist/browser/modules/formula/integration/calculate-formulas-impl.js +616 -0
- package/dist/browser/modules/formula/integration/calculate-formulas.d.ts +67 -0
- package/dist/browser/modules/formula/integration/calculate-formulas.js +68 -0
- package/dist/browser/modules/formula/integration/formula-instance.d.ts +64 -0
- package/dist/browser/modules/formula/integration/formula-instance.js +79 -0
- package/dist/browser/modules/formula/integration/workbook-adapter.d.ts +26 -0
- package/dist/browser/modules/formula/integration/workbook-adapter.js +324 -0
- package/dist/browser/modules/formula/integration/workbook-snapshot.d.ts +267 -0
- package/dist/browser/modules/formula/integration/workbook-snapshot.js +77 -0
- package/dist/browser/modules/formula/materialize/build-writeback-plan.d.ts +34 -0
- package/dist/browser/modules/formula/materialize/build-writeback-plan.js +473 -0
- package/dist/browser/modules/formula/materialize/spill-engine.d.ts +9 -0
- package/dist/browser/modules/formula/materialize/spill-engine.js +38 -0
- package/dist/browser/modules/formula/materialize/types.d.ts +179 -0
- package/dist/browser/modules/formula/materialize/types.js +29 -0
- package/dist/browser/modules/formula/materialize/writeback-plan.d.ts +167 -0
- package/dist/browser/modules/formula/materialize/writeback-plan.js +27 -0
- package/dist/browser/modules/formula/runtime/evaluator.d.ts +151 -0
- package/dist/browser/modules/formula/runtime/evaluator.js +2291 -0
- package/dist/browser/modules/formula/runtime/function-registry.d.ts +47 -0
- package/dist/browser/modules/formula/runtime/function-registry.js +840 -0
- package/dist/browser/modules/formula/runtime/values.d.ts +211 -0
- package/dist/browser/modules/formula/runtime/values.js +385 -0
- package/dist/browser/modules/formula/syntax/ast.d.ts +129 -0
- package/dist/browser/modules/formula/syntax/ast.js +28 -0
- package/dist/browser/modules/formula/syntax/parser.d.ts +18 -0
- package/dist/browser/modules/formula/syntax/parser.js +439 -0
- package/dist/browser/modules/formula/syntax/token-types.d.ts +153 -0
- package/dist/browser/modules/formula/syntax/token-types.js +59 -0
- package/dist/browser/modules/formula/syntax/tokenizer.d.ts +10 -0
- package/dist/browser/modules/formula/syntax/tokenizer.js +1074 -0
- package/dist/browser/modules/pdf/excel-bridge.js +9 -0
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/modules/excel/cell.js +170 -22
- package/dist/cjs/modules/excel/defined-names.js +411 -21
- package/dist/cjs/modules/excel/image.js +24 -1
- package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +14 -0
- package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +48 -1
- package/dist/cjs/modules/excel/stream/worksheet-reader.js +17 -1
- package/dist/cjs/modules/excel/stream/worksheet-writer.js +45 -5
- package/dist/cjs/modules/excel/table.js +15 -2
- package/dist/cjs/modules/excel/utils/col-cache.js +15 -0
- package/dist/cjs/modules/excel/utils/drawing-utils.js +4 -0
- package/dist/cjs/modules/excel/utils/external-link-formula.js +212 -0
- package/dist/cjs/modules/excel/utils/iterate-stream.js +3 -1
- package/dist/cjs/modules/excel/utils/ooxml-paths.js +42 -2
- package/dist/cjs/modules/excel/utils/shared-strings.js +21 -2
- package/dist/cjs/modules/excel/utils/workbook-protection.js +33 -0
- package/dist/cjs/modules/excel/workbook.browser.js +318 -34
- package/dist/cjs/modules/excel/worksheet.js +20 -1
- package/dist/cjs/modules/excel/xlsx/rel-type.js +16 -1
- package/dist/cjs/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
- package/dist/cjs/modules/excel/xlsx/xform/book/external-link-xform.js +333 -0
- package/dist/cjs/modules/excel/xlsx/xform/book/external-reference-xform.js +27 -0
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-protection-xform.js +69 -0
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +18 -0
- package/dist/cjs/modules/excel/xlsx/xform/core/metadata-xform.js +161 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +108 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
- package/dist/cjs/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
- package/dist/cjs/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +83 -0
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
- package/dist/cjs/modules/excel/xlsx/xform/style/border-xform.js +4 -1
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +408 -18
- package/dist/cjs/modules/excel/xlsx/xlsx.js +4 -5
- package/dist/cjs/modules/formula/compile/address-utils.js +89 -0
- package/dist/cjs/modules/formula/compile/binder.js +489 -0
- package/dist/cjs/modules/formula/compile/bound-ast.js +68 -0
- package/dist/cjs/modules/formula/compile/compiled-formula.js +387 -0
- package/dist/cjs/modules/formula/compile/dependency-analysis.js +437 -0
- package/dist/cjs/modules/formula/compile/structured-ref-utils.js +141 -0
- package/dist/cjs/modules/formula/default-syntax-probe.js +87 -0
- package/dist/cjs/modules/formula/functions/_date-context.js +33 -0
- package/dist/cjs/modules/formula/functions/_shared.js +396 -0
- package/dist/cjs/modules/formula/functions/conditional.js +354 -0
- package/dist/cjs/modules/formula/functions/database.js +288 -0
- package/dist/cjs/modules/formula/functions/date.js +883 -0
- package/dist/cjs/modules/formula/functions/dynamic-array.js +881 -0
- package/dist/cjs/modules/formula/functions/engineering.js +1183 -0
- package/dist/cjs/modules/formula/functions/financial.js +2348 -0
- package/dist/cjs/modules/formula/functions/lookup.js +902 -0
- package/dist/cjs/modules/formula/functions/math.js +1487 -0
- package/dist/cjs/modules/formula/functions/statistical.js +3488 -0
- package/dist/cjs/modules/formula/functions/text.js +1889 -0
- package/dist/cjs/modules/formula/host-registry.js +75 -0
- package/dist/cjs/modules/formula/index.js +58 -0
- package/dist/cjs/modules/formula/install.js +93 -0
- package/dist/cjs/modules/formula/integration/apply-writeback-plan.js +213 -0
- package/dist/cjs/modules/formula/integration/calculate-formulas-impl.js +619 -0
- package/dist/cjs/modules/formula/integration/calculate-formulas.js +71 -0
- package/dist/cjs/modules/formula/integration/formula-instance.js +82 -0
- package/dist/cjs/modules/formula/integration/workbook-adapter.js +327 -0
- package/dist/cjs/modules/formula/integration/workbook-snapshot.js +84 -0
- package/dist/cjs/modules/formula/materialize/build-writeback-plan.js +475 -0
- package/dist/cjs/modules/formula/materialize/spill-engine.js +42 -0
- package/dist/cjs/modules/formula/materialize/types.js +32 -0
- package/dist/cjs/modules/formula/materialize/writeback-plan.js +28 -0
- package/dist/cjs/modules/formula/runtime/evaluator.js +2298 -0
- package/dist/cjs/modules/formula/runtime/function-registry.js +846 -0
- package/dist/cjs/modules/formula/runtime/values.js +385 -0
- package/dist/cjs/modules/formula/syntax/ast.js +8 -0
- package/dist/cjs/modules/formula/syntax/parser.js +440 -0
- package/dist/cjs/modules/formula/syntax/token-types.js +32 -0
- package/dist/cjs/modules/formula/syntax/tokenizer.js +1076 -0
- package/dist/cjs/modules/pdf/excel-bridge.js +9 -0
- package/dist/esm/index.browser.js +4 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/modules/excel/cell.js +170 -22
- package/dist/esm/modules/excel/defined-names.js +411 -21
- package/dist/esm/modules/excel/image.js +24 -1
- package/dist/esm/modules/excel/stream/workbook-reader.browser.js +14 -0
- package/dist/esm/modules/excel/stream/workbook-writer.browser.js +48 -1
- package/dist/esm/modules/excel/stream/worksheet-reader.js +17 -1
- package/dist/esm/modules/excel/stream/worksheet-writer.js +45 -5
- package/dist/esm/modules/excel/table.js +15 -2
- package/dist/esm/modules/excel/utils/col-cache.js +15 -0
- package/dist/esm/modules/excel/utils/drawing-utils.js +4 -0
- package/dist/esm/modules/excel/utils/external-link-formula.js +208 -0
- package/dist/esm/modules/excel/utils/iterate-stream.js +3 -1
- package/dist/esm/modules/excel/utils/ooxml-paths.js +37 -2
- package/dist/esm/modules/excel/utils/shared-strings.js +21 -2
- package/dist/esm/modules/excel/utils/workbook-protection.js +30 -0
- package/dist/esm/modules/excel/workbook.browser.js +318 -34
- package/dist/esm/modules/excel/worksheet.js +21 -2
- package/dist/esm/modules/excel/xlsx/rel-type.js +16 -1
- package/dist/esm/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
- package/dist/esm/modules/excel/xlsx/xform/book/external-link-xform.js +330 -0
- package/dist/esm/modules/excel/xlsx/xform/book/external-reference-xform.js +24 -0
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-protection-xform.js +66 -0
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +19 -1
- package/dist/esm/modules/excel/xlsx/xform/core/metadata-xform.js +158 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +105 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
- package/dist/esm/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
- package/dist/esm/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +80 -0
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
- package/dist/esm/modules/excel/xlsx/xform/style/border-xform.js +4 -1
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +410 -20
- package/dist/esm/modules/excel/xlsx/xlsx.js +4 -5
- package/dist/esm/modules/formula/compile/address-utils.js +83 -0
- package/dist/esm/modules/formula/compile/binder.js +487 -0
- package/dist/esm/modules/formula/compile/bound-ast.js +80 -0
- package/dist/esm/modules/formula/compile/compiled-formula.js +383 -0
- package/dist/esm/modules/formula/compile/dependency-analysis.js +432 -0
- package/dist/esm/modules/formula/compile/structured-ref-utils.js +136 -0
- package/dist/esm/modules/formula/default-syntax-probe.js +83 -0
- package/dist/esm/modules/formula/functions/_date-context.js +29 -0
- package/dist/esm/modules/formula/functions/_shared.js +381 -0
- package/dist/esm/modules/formula/functions/conditional.js +343 -0
- package/dist/esm/modules/formula/functions/database.js +274 -0
- package/dist/esm/modules/formula/functions/date.js +855 -0
- package/dist/esm/modules/formula/functions/dynamic-array.js +860 -0
- package/dist/esm/modules/formula/functions/engineering.js +1128 -0
- package/dist/esm/modules/formula/functions/financial.js +2296 -0
- package/dist/esm/modules/formula/functions/lookup.js +886 -0
- package/dist/esm/modules/formula/functions/math.js +1406 -0
- package/dist/esm/modules/formula/functions/statistical.js +3390 -0
- package/dist/esm/modules/formula/functions/text.js +1845 -0
- package/dist/esm/modules/formula/host-registry.js +69 -0
- package/dist/esm/modules/formula/index.js +49 -0
- package/dist/esm/modules/formula/install.js +88 -0
- package/dist/esm/modules/formula/integration/apply-writeback-plan.js +210 -0
- package/dist/esm/modules/formula/integration/calculate-formulas-impl.js +616 -0
- package/dist/esm/modules/formula/integration/calculate-formulas.js +68 -0
- package/dist/esm/modules/formula/integration/formula-instance.js +79 -0
- package/dist/esm/modules/formula/integration/workbook-adapter.js +324 -0
- package/dist/esm/modules/formula/integration/workbook-snapshot.js +77 -0
- package/dist/esm/modules/formula/materialize/build-writeback-plan.js +473 -0
- package/dist/esm/modules/formula/materialize/spill-engine.js +38 -0
- package/dist/esm/modules/formula/materialize/types.js +29 -0
- package/dist/esm/modules/formula/materialize/writeback-plan.js +27 -0
- package/dist/esm/modules/formula/runtime/evaluator.js +2291 -0
- package/dist/esm/modules/formula/runtime/function-registry.js +840 -0
- package/dist/esm/modules/formula/runtime/values.js +385 -0
- package/dist/esm/modules/formula/syntax/ast.js +28 -0
- package/dist/esm/modules/formula/syntax/parser.js +439 -0
- package/dist/esm/modules/formula/syntax/token-types.js +59 -0
- package/dist/esm/modules/formula/syntax/tokenizer.js +1074 -0
- package/dist/esm/modules/pdf/excel-bridge.js +9 -0
- package/dist/iife/excelts.iife.js +2302 -373
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +34 -34
- package/dist/types/index.browser.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/modules/excel/cell.d.ts +17 -3
- package/dist/types/modules/excel/defined-names.d.ts +96 -1
- package/dist/types/modules/excel/image.d.ts +11 -0
- package/dist/types/modules/excel/stream/workbook-reader.browser.d.ts +9 -3
- package/dist/types/modules/excel/stream/workbook-reader.d.ts +2 -1
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +39 -5
- package/dist/types/modules/excel/stream/workbook-writer.d.ts +3 -2
- package/dist/types/modules/excel/stream/worksheet-writer.d.ts +39 -6
- package/dist/types/modules/excel/types.d.ts +133 -2
- package/dist/types/modules/excel/utils/col-cache.d.ts +1 -0
- package/dist/types/modules/excel/utils/drawing-utils.d.ts +3 -3
- package/dist/types/modules/excel/utils/external-link-formula.d.ts +76 -0
- package/dist/types/modules/excel/utils/iterate-stream.d.ts +9 -3
- package/dist/types/modules/excel/utils/ooxml-paths.d.ts +19 -0
- package/dist/types/modules/excel/utils/shared-strings.d.ts +8 -3
- package/dist/types/modules/excel/utils/workbook-protection.d.ts +30 -0
- package/dist/types/modules/excel/workbook.browser.d.ts +257 -6
- package/dist/types/modules/excel/workbook.d.ts +1 -1
- package/dist/types/modules/excel/worksheet.d.ts +3 -1
- package/dist/types/modules/excel/xlsx/rel-type.d.ts +15 -0
- package/dist/types/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +6 -5
- package/dist/types/modules/excel/xlsx/xform/book/external-link-xform.d.ts +84 -0
- package/dist/types/modules/excel/xlsx/xform/book/external-reference-xform.d.ts +17 -0
- package/dist/types/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.d.ts +3 -0
- package/dist/types/modules/excel/xlsx/xform/book/workbook-protection-xform.d.ts +20 -0
- package/dist/types/modules/excel/xlsx/xform/core/metadata-xform.d.ts +56 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.d.ts +26 -0
- package/dist/types/modules/excel/xlsx/xform/sheet/cell-xform.d.ts +1 -1
- package/dist/types/modules/excel/xlsx/xform/sheet/ignored-errors-xform.d.ts +21 -0
- package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +172 -13
- package/dist/types/modules/excel/xlsx/xlsx.d.ts +7 -4
- package/dist/types/modules/formula/compile/address-utils.d.ts +62 -0
- package/dist/types/modules/formula/compile/binder.d.ts +42 -0
- package/dist/types/modules/formula/compile/bound-ast.d.ts +230 -0
- package/dist/types/modules/formula/compile/compiled-formula.d.ts +137 -0
- package/dist/types/modules/formula/compile/dependency-analysis.d.ts +93 -0
- package/dist/types/modules/formula/compile/structured-ref-utils.d.ts +93 -0
- package/dist/types/modules/formula/default-syntax-probe.d.ts +79 -0
- package/dist/types/modules/formula/functions/_date-context.d.ts +4 -0
- package/dist/types/modules/formula/functions/_shared.d.ts +121 -0
- package/dist/types/modules/formula/functions/conditional.d.ts +27 -0
- package/dist/types/modules/formula/functions/database.d.ts +37 -0
- package/dist/types/modules/formula/functions/date.d.ts +61 -0
- package/dist/types/modules/formula/functions/dynamic-array.d.ts +23 -0
- package/dist/types/modules/formula/functions/engineering.d.ts +57 -0
- package/dist/types/modules/formula/functions/financial.d.ts +202 -0
- package/dist/types/modules/formula/functions/lookup.d.ts +18 -0
- package/dist/types/modules/formula/functions/math.d.ts +114 -0
- package/dist/types/modules/formula/functions/statistical.d.ts +193 -0
- package/dist/types/modules/formula/functions/text.d.ts +86 -0
- package/dist/types/modules/formula/host-registry.d.ts +53 -0
- package/dist/types/modules/formula/index.d.ts +39 -0
- package/dist/types/modules/formula/install.d.ts +62 -0
- package/dist/types/modules/formula/integration/apply-writeback-plan.d.ts +26 -0
- package/dist/types/modules/formula/integration/calculate-formulas-impl.d.ts +30 -0
- package/dist/types/modules/formula/integration/calculate-formulas.d.ts +67 -0
- package/dist/types/modules/formula/integration/formula-instance.d.ts +64 -0
- package/dist/types/modules/formula/integration/workbook-adapter.d.ts +26 -0
- package/dist/types/modules/formula/integration/workbook-snapshot.d.ts +267 -0
- package/dist/types/modules/formula/materialize/build-writeback-plan.d.ts +34 -0
- package/dist/types/modules/formula/materialize/spill-engine.d.ts +9 -0
- package/dist/types/modules/formula/materialize/types.d.ts +179 -0
- package/dist/types/modules/formula/materialize/writeback-plan.d.ts +167 -0
- package/dist/types/modules/formula/runtime/evaluator.d.ts +151 -0
- package/dist/types/modules/formula/runtime/function-registry.d.ts +47 -0
- package/dist/types/modules/formula/runtime/values.d.ts +211 -0
- package/dist/types/modules/formula/syntax/ast.d.ts +129 -0
- package/dist/types/modules/formula/syntax/parser.d.ts +18 -0
- package/dist/types/modules/formula/syntax/token-types.d.ts +153 -0
- package/dist/types/modules/formula/syntax/tokenizer.d.ts +10 -0
- package/package.json +28 -28
|
@@ -0,0 +1,1406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Math / Aggregate Functions — Native RuntimeValue implementation.
|
|
3
|
+
*/
|
|
4
|
+
import { RVKind, ERRORS, rvNumber, rvString, rvArray, toNumberRV, toStringRV, topLeft, isError, isArray } from "../runtime/values.js";
|
|
5
|
+
import { argToNumber, flattenAll, flattenNumbers, firstError } from "./_shared.js";
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Internal Helpers
|
|
8
|
+
// ============================================================================
|
|
9
|
+
/**
|
|
10
|
+
* Apply a rounding operation using an integer factor, avoiding the
|
|
11
|
+
* floating-point drift that `Math.pow(10, d) / Math.pow(10, d)` introduces
|
|
12
|
+
* when `d` is negative (e.g. `1/0.1` is not exactly `10`).
|
|
13
|
+
*
|
|
14
|
+
* For `digits >= 0` we scale up by `10^digits`, round, and scale back down.
|
|
15
|
+
* For `digits < 0` we divide by `10^|digits|`, round, and multiply back up.
|
|
16
|
+
* In both branches the scaling factor is an integer, so only one rounding
|
|
17
|
+
* error is introduced.
|
|
18
|
+
*/
|
|
19
|
+
function applyRounding(value, digits, round) {
|
|
20
|
+
const d = Math.trunc(digits);
|
|
21
|
+
// Clamp digits to Excel's documented range [-127, 127]. Without this
|
|
22
|
+
// guard, extreme inputs blow `Math.pow(10, d)` up to Infinity; the
|
|
23
|
+
// resulting `round(value * Infinity) / Infinity` is NaN, which then
|
|
24
|
+
// leaks through the whole rounding family (ROUND / ROUNDDOWN /
|
|
25
|
+
// ROUNDUP / TRUNC / MROUND) and persists to the worksheet. Return
|
|
26
|
+
// `value` itself for well-behaved "nothing to do" edge cases (d very
|
|
27
|
+
// large is effectively "no rounding"; d very negative rounds to 0).
|
|
28
|
+
if (!Number.isFinite(value)) {
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
if (d >= 308) {
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
if (d <= -308) {
|
|
35
|
+
return round(0);
|
|
36
|
+
}
|
|
37
|
+
if (d >= 0) {
|
|
38
|
+
const factor = Math.pow(10, d);
|
|
39
|
+
if (!Number.isFinite(factor) || factor === 0) {
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
return round(value * factor) / factor;
|
|
43
|
+
}
|
|
44
|
+
const factor = Math.pow(10, -d);
|
|
45
|
+
if (!Number.isFinite(factor) || factor === 0) {
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
return round(value / factor) * factor;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Round half away from zero, matching Excel's ROUND semantics.
|
|
52
|
+
*
|
|
53
|
+
* JavaScript's `Math.round` rounds half toward +∞ (so `Math.round(-0.5)`
|
|
54
|
+
* returns `-0` instead of `-1`), whereas Excel rounds the magnitude and
|
|
55
|
+
* preserves the sign: `ROUND(-0.5, 0) = -1`. This helper implements the
|
|
56
|
+
* Excel rule by operating on the absolute value and restoring the sign.
|
|
57
|
+
*/
|
|
58
|
+
function roundHalfAwayFromZero(n) {
|
|
59
|
+
return n < 0 ? -Math.round(-n) : Math.round(n);
|
|
60
|
+
}
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Trigonometric Functions
|
|
63
|
+
// ============================================================================
|
|
64
|
+
export const fnSIN = args => {
|
|
65
|
+
const n = argToNumber(args[0]);
|
|
66
|
+
return isError(n) ? n : rvNumber(Math.sin(n.value));
|
|
67
|
+
};
|
|
68
|
+
export const fnCOS = args => {
|
|
69
|
+
const n = argToNumber(args[0]);
|
|
70
|
+
return isError(n) ? n : rvNumber(Math.cos(n.value));
|
|
71
|
+
};
|
|
72
|
+
export const fnTAN = args => {
|
|
73
|
+
const n = argToNumber(args[0]);
|
|
74
|
+
return isError(n) ? n : rvNumber(Math.tan(n.value));
|
|
75
|
+
};
|
|
76
|
+
export const fnASIN = args => {
|
|
77
|
+
const n = argToNumber(args[0]);
|
|
78
|
+
if (isError(n)) {
|
|
79
|
+
return n;
|
|
80
|
+
}
|
|
81
|
+
if (n.value < -1 || n.value > 1) {
|
|
82
|
+
return ERRORS.NUM;
|
|
83
|
+
}
|
|
84
|
+
return rvNumber(Math.asin(n.value));
|
|
85
|
+
};
|
|
86
|
+
export const fnACOS = args => {
|
|
87
|
+
const n = argToNumber(args[0]);
|
|
88
|
+
if (isError(n)) {
|
|
89
|
+
return n;
|
|
90
|
+
}
|
|
91
|
+
if (n.value < -1 || n.value > 1) {
|
|
92
|
+
return ERRORS.NUM;
|
|
93
|
+
}
|
|
94
|
+
return rvNumber(Math.acos(n.value));
|
|
95
|
+
};
|
|
96
|
+
export const fnATAN = args => {
|
|
97
|
+
const n = argToNumber(args[0]);
|
|
98
|
+
return isError(n) ? n : rvNumber(Math.atan(n.value));
|
|
99
|
+
};
|
|
100
|
+
export const fnATAN2 = args => {
|
|
101
|
+
const x = argToNumber(args[0]);
|
|
102
|
+
if (isError(x)) {
|
|
103
|
+
return x;
|
|
104
|
+
}
|
|
105
|
+
const y = argToNumber(args[1]);
|
|
106
|
+
if (isError(y)) {
|
|
107
|
+
return y;
|
|
108
|
+
}
|
|
109
|
+
if (x.value === 0 && y.value === 0) {
|
|
110
|
+
return ERRORS.DIV0;
|
|
111
|
+
}
|
|
112
|
+
return rvNumber(Math.atan2(y.value, x.value));
|
|
113
|
+
};
|
|
114
|
+
export const fnSINH = args => {
|
|
115
|
+
const n = argToNumber(args[0]);
|
|
116
|
+
if (isError(n)) {
|
|
117
|
+
return n;
|
|
118
|
+
}
|
|
119
|
+
// sinh(x) overflows double for |x| > ~710. Excel returns #NUM! rather
|
|
120
|
+
// than a silent Infinity (which would poison downstream arithmetic).
|
|
121
|
+
const r = Math.sinh(n.value);
|
|
122
|
+
return isFinite(r) ? rvNumber(r) : ERRORS.NUM;
|
|
123
|
+
};
|
|
124
|
+
export const fnCOSH = args => {
|
|
125
|
+
const n = argToNumber(args[0]);
|
|
126
|
+
if (isError(n)) {
|
|
127
|
+
return n;
|
|
128
|
+
}
|
|
129
|
+
const r = Math.cosh(n.value);
|
|
130
|
+
return isFinite(r) ? rvNumber(r) : ERRORS.NUM;
|
|
131
|
+
};
|
|
132
|
+
export const fnTANH = args => {
|
|
133
|
+
const n = argToNumber(args[0]);
|
|
134
|
+
return isError(n) ? n : rvNumber(Math.tanh(n.value));
|
|
135
|
+
};
|
|
136
|
+
export const fnASINH = args => {
|
|
137
|
+
const n = argToNumber(args[0]);
|
|
138
|
+
return isError(n) ? n : rvNumber(Math.asinh(n.value));
|
|
139
|
+
};
|
|
140
|
+
export const fnACOSH = args => {
|
|
141
|
+
const n = argToNumber(args[0]);
|
|
142
|
+
if (isError(n)) {
|
|
143
|
+
return n;
|
|
144
|
+
}
|
|
145
|
+
if (n.value < 1) {
|
|
146
|
+
return ERRORS.NUM;
|
|
147
|
+
}
|
|
148
|
+
return rvNumber(Math.acosh(n.value));
|
|
149
|
+
};
|
|
150
|
+
export const fnATANH = args => {
|
|
151
|
+
const n = argToNumber(args[0]);
|
|
152
|
+
if (isError(n)) {
|
|
153
|
+
return n;
|
|
154
|
+
}
|
|
155
|
+
if (n.value <= -1 || n.value >= 1) {
|
|
156
|
+
return ERRORS.NUM;
|
|
157
|
+
}
|
|
158
|
+
return rvNumber(Math.atanh(n.value));
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Secondary trigonometric family (SEC / CSC / COT and hyperbolic /
|
|
162
|
+
* inverse variants). None of these exist on the JavaScript Math object
|
|
163
|
+
* so we derive them from the standard sin / cos / tan primitives, with
|
|
164
|
+
* explicit guards at the discontinuities (π/2 for SEC, multiples of π
|
|
165
|
+
* for CSC / COT, zero for the H variants).
|
|
166
|
+
*/
|
|
167
|
+
export const fnSEC = args => {
|
|
168
|
+
const n = argToNumber(args[0]);
|
|
169
|
+
if (isError(n)) {
|
|
170
|
+
return n;
|
|
171
|
+
}
|
|
172
|
+
const c = Math.cos(n.value);
|
|
173
|
+
return c === 0 ? ERRORS.DIV0 : rvNumber(1 / c);
|
|
174
|
+
};
|
|
175
|
+
export const fnCSC = args => {
|
|
176
|
+
const n = argToNumber(args[0]);
|
|
177
|
+
if (isError(n)) {
|
|
178
|
+
return n;
|
|
179
|
+
}
|
|
180
|
+
const s = Math.sin(n.value);
|
|
181
|
+
return s === 0 ? ERRORS.DIV0 : rvNumber(1 / s);
|
|
182
|
+
};
|
|
183
|
+
export const fnCOT = args => {
|
|
184
|
+
const n = argToNumber(args[0]);
|
|
185
|
+
if (isError(n)) {
|
|
186
|
+
return n;
|
|
187
|
+
}
|
|
188
|
+
const s = Math.sin(n.value);
|
|
189
|
+
if (s === 0) {
|
|
190
|
+
return ERRORS.DIV0;
|
|
191
|
+
}
|
|
192
|
+
return rvNumber(Math.cos(n.value) / s);
|
|
193
|
+
};
|
|
194
|
+
export const fnSECH = args => {
|
|
195
|
+
const n = argToNumber(args[0]);
|
|
196
|
+
if (isError(n)) {
|
|
197
|
+
return n;
|
|
198
|
+
}
|
|
199
|
+
// sech(x) = 1/cosh(x); cosh is never zero for real x, but it does
|
|
200
|
+
// overflow to Infinity for |x| > ~710 — return 0 (the mathematical
|
|
201
|
+
// limit) rather than dividing and producing NaN.
|
|
202
|
+
const c = Math.cosh(n.value);
|
|
203
|
+
return Number.isFinite(c) ? rvNumber(1 / c) : rvNumber(0);
|
|
204
|
+
};
|
|
205
|
+
export const fnCSCH = args => {
|
|
206
|
+
const n = argToNumber(args[0]);
|
|
207
|
+
if (isError(n)) {
|
|
208
|
+
return n;
|
|
209
|
+
}
|
|
210
|
+
if (n.value === 0) {
|
|
211
|
+
return ERRORS.DIV0;
|
|
212
|
+
}
|
|
213
|
+
const s = Math.sinh(n.value);
|
|
214
|
+
if (!Number.isFinite(s)) {
|
|
215
|
+
return rvNumber(0);
|
|
216
|
+
}
|
|
217
|
+
return rvNumber(1 / s);
|
|
218
|
+
};
|
|
219
|
+
export const fnCOTH = args => {
|
|
220
|
+
const n = argToNumber(args[0]);
|
|
221
|
+
if (isError(n)) {
|
|
222
|
+
return n;
|
|
223
|
+
}
|
|
224
|
+
if (n.value === 0) {
|
|
225
|
+
return ERRORS.DIV0;
|
|
226
|
+
}
|
|
227
|
+
return rvNumber(1 / Math.tanh(n.value));
|
|
228
|
+
};
|
|
229
|
+
export const fnACOT = args => {
|
|
230
|
+
const n = argToNumber(args[0]);
|
|
231
|
+
if (isError(n)) {
|
|
232
|
+
return n;
|
|
233
|
+
}
|
|
234
|
+
// Excel's ACOT returns values in (0, π), not (-π/2, π/2) like
|
|
235
|
+
// Math.atan. Map: ACOT(x) = π/2 − atan(x).
|
|
236
|
+
return rvNumber(Math.PI / 2 - Math.atan(n.value));
|
|
237
|
+
};
|
|
238
|
+
export const fnACOTH = args => {
|
|
239
|
+
const n = argToNumber(args[0]);
|
|
240
|
+
if (isError(n)) {
|
|
241
|
+
return n;
|
|
242
|
+
}
|
|
243
|
+
// Defined only for |x| > 1.
|
|
244
|
+
if (n.value >= -1 && n.value <= 1) {
|
|
245
|
+
return ERRORS.NUM;
|
|
246
|
+
}
|
|
247
|
+
return rvNumber(0.5 * Math.log((n.value + 1) / (n.value - 1)));
|
|
248
|
+
};
|
|
249
|
+
// ============================================================================
|
|
250
|
+
// Math / Aggregate Functions
|
|
251
|
+
// ============================================================================
|
|
252
|
+
export const fnSUM = args => {
|
|
253
|
+
const nums = flattenNumbers(args);
|
|
254
|
+
const err = firstError(nums);
|
|
255
|
+
if (err) {
|
|
256
|
+
return err;
|
|
257
|
+
}
|
|
258
|
+
let sum = 0;
|
|
259
|
+
for (const n of nums) {
|
|
260
|
+
sum += n.value;
|
|
261
|
+
}
|
|
262
|
+
// Fail fast on overflow to Infinity; otherwise the result leaks into
|
|
263
|
+
// any formula that aggregates it (AVERAGE, STDEV, etc.) and those
|
|
264
|
+
// downstream callers would then fan #NUM! out across the graph.
|
|
265
|
+
return Number.isFinite(sum) ? rvNumber(sum) : ERRORS.NUM;
|
|
266
|
+
};
|
|
267
|
+
export const fnAVERAGE = args => {
|
|
268
|
+
const nums = flattenNumbers(args);
|
|
269
|
+
const err = firstError(nums);
|
|
270
|
+
if (err) {
|
|
271
|
+
return err;
|
|
272
|
+
}
|
|
273
|
+
if (nums.length === 0) {
|
|
274
|
+
return ERRORS.DIV0;
|
|
275
|
+
}
|
|
276
|
+
let sum = 0;
|
|
277
|
+
for (const n of nums) {
|
|
278
|
+
sum += n.value;
|
|
279
|
+
}
|
|
280
|
+
const avg = sum / nums.length;
|
|
281
|
+
return Number.isFinite(avg) ? rvNumber(avg) : ERRORS.NUM;
|
|
282
|
+
};
|
|
283
|
+
export const fnMIN = args => {
|
|
284
|
+
const nums = flattenNumbers(args);
|
|
285
|
+
const err = firstError(nums);
|
|
286
|
+
if (err) {
|
|
287
|
+
return err;
|
|
288
|
+
}
|
|
289
|
+
if (nums.length === 0) {
|
|
290
|
+
return rvNumber(0);
|
|
291
|
+
}
|
|
292
|
+
let min = Infinity;
|
|
293
|
+
for (const n of nums) {
|
|
294
|
+
if (n.value < min) {
|
|
295
|
+
min = n.value;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return rvNumber(min);
|
|
299
|
+
};
|
|
300
|
+
export const fnMAX = args => {
|
|
301
|
+
const nums = flattenNumbers(args);
|
|
302
|
+
const err = firstError(nums);
|
|
303
|
+
if (err) {
|
|
304
|
+
return err;
|
|
305
|
+
}
|
|
306
|
+
if (nums.length === 0) {
|
|
307
|
+
return rvNumber(0);
|
|
308
|
+
}
|
|
309
|
+
let max = -Infinity;
|
|
310
|
+
for (const n of nums) {
|
|
311
|
+
if (n.value > max) {
|
|
312
|
+
max = n.value;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return rvNumber(max);
|
|
316
|
+
};
|
|
317
|
+
export const fnCOUNT = args => {
|
|
318
|
+
let count = 0;
|
|
319
|
+
const all = flattenAll(args);
|
|
320
|
+
for (const v of all) {
|
|
321
|
+
if (v.kind === RVKind.Number) {
|
|
322
|
+
count++;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return rvNumber(count);
|
|
326
|
+
};
|
|
327
|
+
export const fnCOUNTA = args => {
|
|
328
|
+
let count = 0;
|
|
329
|
+
const all = flattenAll(args);
|
|
330
|
+
for (const v of all) {
|
|
331
|
+
// Count everything that is not blank and not empty string
|
|
332
|
+
if (v.kind !== RVKind.Blank && !(v.kind === RVKind.String && v.value === "")) {
|
|
333
|
+
count++;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return rvNumber(count);
|
|
337
|
+
};
|
|
338
|
+
export const fnCOUNTBLANK = args => {
|
|
339
|
+
let count = 0;
|
|
340
|
+
const all = flattenAll(args);
|
|
341
|
+
for (const v of all) {
|
|
342
|
+
if (v.kind === RVKind.Blank || (v.kind === RVKind.String && v.value === "")) {
|
|
343
|
+
count++;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return rvNumber(count);
|
|
347
|
+
};
|
|
348
|
+
export const fnPRODUCT = args => {
|
|
349
|
+
const nums = flattenNumbers(args);
|
|
350
|
+
const err = firstError(nums);
|
|
351
|
+
if (err) {
|
|
352
|
+
return err;
|
|
353
|
+
}
|
|
354
|
+
if (nums.length === 0) {
|
|
355
|
+
return rvNumber(0);
|
|
356
|
+
}
|
|
357
|
+
let product = 1;
|
|
358
|
+
for (const n of nums) {
|
|
359
|
+
product *= n.value;
|
|
360
|
+
}
|
|
361
|
+
// Excel surfaces an overflow as #NUM! rather than letting Infinity
|
|
362
|
+
// propagate into subsequent arithmetic.
|
|
363
|
+
return isFinite(product) ? rvNumber(product) : ERRORS.NUM;
|
|
364
|
+
};
|
|
365
|
+
export const fnSUMPRODUCT = args => {
|
|
366
|
+
if (args.length === 0) {
|
|
367
|
+
return ERRORS.VALUE;
|
|
368
|
+
}
|
|
369
|
+
// Promote scalar args to 1x1 arrays. Excel allows a scalar (or 1x1 array)
|
|
370
|
+
// to broadcast to the surrounding array's shape, e.g. SUMPRODUCT(1, A1:A10)
|
|
371
|
+
// is equivalent to SUMPRODUCT(A1:A10). All non-broadcast arrays must share
|
|
372
|
+
// the same height/width — only 1x1 may be broadcast.
|
|
373
|
+
const arrays = [];
|
|
374
|
+
for (const a of args) {
|
|
375
|
+
if (isArray(a)) {
|
|
376
|
+
arrays.push(a);
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
arrays.push(rvArray([[topLeft(a)]]));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// Find the target dimensions: the max height/width across all non-1x1 arrays.
|
|
383
|
+
// 1x1 arrays are eligible to broadcast to whatever size is chosen.
|
|
384
|
+
let rows = 1;
|
|
385
|
+
let cols = 1;
|
|
386
|
+
for (const arr of arrays) {
|
|
387
|
+
if (arr.height !== 1 || arr.width !== 1) {
|
|
388
|
+
if (rows === 1 && cols === 1) {
|
|
389
|
+
rows = arr.height;
|
|
390
|
+
cols = arr.width;
|
|
391
|
+
}
|
|
392
|
+
else if (arr.height !== rows || arr.width !== cols) {
|
|
393
|
+
// Two different non-1x1 shapes — incompatible.
|
|
394
|
+
return ERRORS.VALUE;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
let sum = 0;
|
|
399
|
+
for (let r = 0; r < rows; r++) {
|
|
400
|
+
for (let c = 0; c < cols; c++) {
|
|
401
|
+
let product = 1;
|
|
402
|
+
for (const arr of arrays) {
|
|
403
|
+
// Broadcast 1x1 arrays to the target cell position.
|
|
404
|
+
const val = arr.height === 1 && arr.width === 1 ? arr.rows[0][0] : arr.rows[r][c];
|
|
405
|
+
if (val.kind === RVKind.Error) {
|
|
406
|
+
return val;
|
|
407
|
+
}
|
|
408
|
+
const n = val.kind === RVKind.Number
|
|
409
|
+
? val.value
|
|
410
|
+
: val.kind === RVKind.Boolean
|
|
411
|
+
? val.value
|
|
412
|
+
? 1
|
|
413
|
+
: 0
|
|
414
|
+
: 0;
|
|
415
|
+
product *= n;
|
|
416
|
+
}
|
|
417
|
+
sum += product;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
if (!Number.isFinite(sum)) {
|
|
421
|
+
return ERRORS.NUM;
|
|
422
|
+
}
|
|
423
|
+
return rvNumber(sum);
|
|
424
|
+
};
|
|
425
|
+
// ============================================================================
|
|
426
|
+
// Math Functions
|
|
427
|
+
// ============================================================================
|
|
428
|
+
export const fnABS = args => {
|
|
429
|
+
const n = argToNumber(args[0]);
|
|
430
|
+
return isError(n) ? n : rvNumber(Math.abs(n.value));
|
|
431
|
+
};
|
|
432
|
+
export const fnCEILING = args => {
|
|
433
|
+
const num = argToNumber(args[0]);
|
|
434
|
+
if (isError(num)) {
|
|
435
|
+
return num;
|
|
436
|
+
}
|
|
437
|
+
const sigRV = args.length > 1 ? argToNumber(args[1]) : rvNumber(1);
|
|
438
|
+
if (isError(sigRV)) {
|
|
439
|
+
return sigRV;
|
|
440
|
+
}
|
|
441
|
+
const sig = sigRV.value;
|
|
442
|
+
if (sig === 0) {
|
|
443
|
+
return rvNumber(0);
|
|
444
|
+
}
|
|
445
|
+
// CEILING (the non-MATH variant) requires number and significance to share
|
|
446
|
+
// a sign. `CEILING(2, -1)` in Excel is #NUM!; only `CEILING.MATH` tolerates
|
|
447
|
+
// a negative significance with a positive number.
|
|
448
|
+
if (num.value !== 0 && Math.sign(num.value) !== Math.sign(sig)) {
|
|
449
|
+
return ERRORS.NUM;
|
|
450
|
+
}
|
|
451
|
+
return rvNumber(Math.ceil(num.value / sig) * sig);
|
|
452
|
+
};
|
|
453
|
+
export const fnFLOOR = args => {
|
|
454
|
+
const num = argToNumber(args[0]);
|
|
455
|
+
if (isError(num)) {
|
|
456
|
+
return num;
|
|
457
|
+
}
|
|
458
|
+
const sigRV = args.length > 1 ? argToNumber(args[1]) : rvNumber(1);
|
|
459
|
+
if (isError(sigRV)) {
|
|
460
|
+
return sigRV;
|
|
461
|
+
}
|
|
462
|
+
const sig = sigRV.value;
|
|
463
|
+
if (sig === 0) {
|
|
464
|
+
return ERRORS.DIV0;
|
|
465
|
+
}
|
|
466
|
+
if (num.value !== 0 && Math.sign(num.value) !== Math.sign(sig)) {
|
|
467
|
+
return ERRORS.NUM;
|
|
468
|
+
}
|
|
469
|
+
return rvNumber(Math.floor(num.value / sig) * sig);
|
|
470
|
+
};
|
|
471
|
+
export const fnINT = args => {
|
|
472
|
+
const n = argToNumber(args[0]);
|
|
473
|
+
return isError(n) ? n : rvNumber(Math.floor(n.value));
|
|
474
|
+
};
|
|
475
|
+
export const fnMOD = args => {
|
|
476
|
+
const num = argToNumber(args[0]);
|
|
477
|
+
if (isError(num)) {
|
|
478
|
+
return num;
|
|
479
|
+
}
|
|
480
|
+
const div = argToNumber(args[1]);
|
|
481
|
+
if (isError(div)) {
|
|
482
|
+
return div;
|
|
483
|
+
}
|
|
484
|
+
if (div.value === 0) {
|
|
485
|
+
return ERRORS.DIV0;
|
|
486
|
+
}
|
|
487
|
+
return rvNumber(num.value - div.value * Math.floor(num.value / div.value));
|
|
488
|
+
};
|
|
489
|
+
export const fnPOWER = args => {
|
|
490
|
+
const base = argToNumber(args[0]);
|
|
491
|
+
if (isError(base)) {
|
|
492
|
+
return base;
|
|
493
|
+
}
|
|
494
|
+
const exp = argToNumber(args[1]);
|
|
495
|
+
if (isError(exp)) {
|
|
496
|
+
return exp;
|
|
497
|
+
}
|
|
498
|
+
// Distinguish the two degenerate-base cases Excel handles separately:
|
|
499
|
+
// POWER(0, 0) → 1 (by convention, matches Excel)
|
|
500
|
+
// POWER(0, <0) → #DIV/0!
|
|
501
|
+
// POWER(<0, non-int) → #NUM! (complex result; Math.pow returns NaN)
|
|
502
|
+
if (base.value === 0) {
|
|
503
|
+
if (exp.value < 0) {
|
|
504
|
+
return ERRORS.DIV0;
|
|
505
|
+
}
|
|
506
|
+
if (exp.value === 0) {
|
|
507
|
+
return rvNumber(1);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
const result = Math.pow(base.value, exp.value);
|
|
511
|
+
if (Number.isNaN(result)) {
|
|
512
|
+
return ERRORS.NUM;
|
|
513
|
+
}
|
|
514
|
+
return !isFinite(result) ? ERRORS.NUM : rvNumber(result);
|
|
515
|
+
};
|
|
516
|
+
export const fnROUND = args => {
|
|
517
|
+
const num = argToNumber(args[0]);
|
|
518
|
+
if (isError(num)) {
|
|
519
|
+
return num;
|
|
520
|
+
}
|
|
521
|
+
const digitsRV = args.length > 1 ? argToNumber(args[1]) : rvNumber(0);
|
|
522
|
+
if (isError(digitsRV)) {
|
|
523
|
+
return digitsRV;
|
|
524
|
+
}
|
|
525
|
+
return rvNumber(applyRounding(num.value, digitsRV.value, roundHalfAwayFromZero));
|
|
526
|
+
};
|
|
527
|
+
export const fnROUNDDOWN = args => {
|
|
528
|
+
const num = argToNumber(args[0]);
|
|
529
|
+
if (isError(num)) {
|
|
530
|
+
return num;
|
|
531
|
+
}
|
|
532
|
+
const digitsRV = args.length > 1 ? argToNumber(args[1]) : rvNumber(0);
|
|
533
|
+
if (isError(digitsRV)) {
|
|
534
|
+
return digitsRV;
|
|
535
|
+
}
|
|
536
|
+
return rvNumber(applyRounding(num.value, digitsRV.value, Math.trunc));
|
|
537
|
+
};
|
|
538
|
+
export const fnROUNDUP = args => {
|
|
539
|
+
const num = argToNumber(args[0]);
|
|
540
|
+
if (isError(num)) {
|
|
541
|
+
return num;
|
|
542
|
+
}
|
|
543
|
+
const digitsRV = args.length > 1 ? argToNumber(args[1]) : rvNumber(0);
|
|
544
|
+
if (isError(digitsRV)) {
|
|
545
|
+
return digitsRV;
|
|
546
|
+
}
|
|
547
|
+
// ROUNDUP rounds away from zero for the fractional part at the requested
|
|
548
|
+
// precision. We emulate this by ceiling the scaled absolute value and
|
|
549
|
+
// restoring the sign.
|
|
550
|
+
const sign = num.value >= 0 ? 1 : -1;
|
|
551
|
+
const rounded = applyRounding(Math.abs(num.value), digitsRV.value, Math.ceil);
|
|
552
|
+
return rvNumber(sign * rounded);
|
|
553
|
+
};
|
|
554
|
+
export const fnSQRT = args => {
|
|
555
|
+
const n = argToNumber(args[0]);
|
|
556
|
+
if (isError(n)) {
|
|
557
|
+
return n;
|
|
558
|
+
}
|
|
559
|
+
if (n.value < 0) {
|
|
560
|
+
return ERRORS.NUM;
|
|
561
|
+
}
|
|
562
|
+
return rvNumber(Math.sqrt(n.value));
|
|
563
|
+
};
|
|
564
|
+
/**
|
|
565
|
+
* SQRTPI(number) — returns the square root of (number × π). Useful in
|
|
566
|
+
* statistical formulas and Gauss integrals.
|
|
567
|
+
*/
|
|
568
|
+
export const fnSQRTPI = args => {
|
|
569
|
+
const n = argToNumber(args[0]);
|
|
570
|
+
if (isError(n)) {
|
|
571
|
+
return n;
|
|
572
|
+
}
|
|
573
|
+
if (n.value < 0) {
|
|
574
|
+
return ERRORS.NUM;
|
|
575
|
+
}
|
|
576
|
+
return rvNumber(Math.sqrt(n.value * Math.PI));
|
|
577
|
+
};
|
|
578
|
+
export const fnLN = args => {
|
|
579
|
+
const n = argToNumber(args[0]);
|
|
580
|
+
if (isError(n)) {
|
|
581
|
+
return n;
|
|
582
|
+
}
|
|
583
|
+
if (n.value <= 0) {
|
|
584
|
+
return ERRORS.NUM;
|
|
585
|
+
}
|
|
586
|
+
return rvNumber(Math.log(n.value));
|
|
587
|
+
};
|
|
588
|
+
export const fnLOG = args => {
|
|
589
|
+
const n = argToNumber(args[0]);
|
|
590
|
+
if (isError(n)) {
|
|
591
|
+
return n;
|
|
592
|
+
}
|
|
593
|
+
if (n.value <= 0) {
|
|
594
|
+
return ERRORS.NUM;
|
|
595
|
+
}
|
|
596
|
+
const baseRV = args.length > 1 ? argToNumber(args[1]) : rvNumber(10);
|
|
597
|
+
if (isError(baseRV)) {
|
|
598
|
+
return baseRV;
|
|
599
|
+
}
|
|
600
|
+
if (baseRV.value <= 0 || baseRV.value === 1) {
|
|
601
|
+
return ERRORS.NUM;
|
|
602
|
+
}
|
|
603
|
+
return rvNumber(Math.log(n.value) / Math.log(baseRV.value));
|
|
604
|
+
};
|
|
605
|
+
export const fnLOG10 = args => {
|
|
606
|
+
const n = argToNumber(args[0]);
|
|
607
|
+
if (isError(n)) {
|
|
608
|
+
return n;
|
|
609
|
+
}
|
|
610
|
+
if (n.value <= 0) {
|
|
611
|
+
return ERRORS.NUM;
|
|
612
|
+
}
|
|
613
|
+
return rvNumber(Math.log10(n.value));
|
|
614
|
+
};
|
|
615
|
+
export const fnEXP = args => {
|
|
616
|
+
const n = argToNumber(args[0]);
|
|
617
|
+
if (isError(n)) {
|
|
618
|
+
return n;
|
|
619
|
+
}
|
|
620
|
+
// EXP(~710) overflows double to Infinity. Excel returns #NUM! in that
|
|
621
|
+
// regime rather than letting the non-finite result propagate.
|
|
622
|
+
const r = Math.exp(n.value);
|
|
623
|
+
return isFinite(r) ? rvNumber(r) : ERRORS.NUM;
|
|
624
|
+
};
|
|
625
|
+
export const fnPI = () => rvNumber(Math.PI);
|
|
626
|
+
export const fnRAND = () => rvNumber(Math.random());
|
|
627
|
+
export const fnRANDBETWEEN = args => {
|
|
628
|
+
const bottom = argToNumber(args[0]);
|
|
629
|
+
if (isError(bottom)) {
|
|
630
|
+
return bottom;
|
|
631
|
+
}
|
|
632
|
+
const top = argToNumber(args[1]);
|
|
633
|
+
if (isError(top)) {
|
|
634
|
+
return top;
|
|
635
|
+
}
|
|
636
|
+
const lo = Math.ceil(bottom.value);
|
|
637
|
+
const hi = Math.floor(top.value);
|
|
638
|
+
// Excel returns #NUM! when bottom > top; otherwise the formula below would
|
|
639
|
+
// produce a garbage integer from a negative range.
|
|
640
|
+
if (lo > hi) {
|
|
641
|
+
return ERRORS.NUM;
|
|
642
|
+
}
|
|
643
|
+
return rvNumber(Math.floor(Math.random() * (hi - lo + 1)) + lo);
|
|
644
|
+
};
|
|
645
|
+
export const fnSIGN = args => {
|
|
646
|
+
const n = argToNumber(args[0]);
|
|
647
|
+
if (isError(n)) {
|
|
648
|
+
return n;
|
|
649
|
+
}
|
|
650
|
+
// `Math.sign(-0)` returns `-0` (preserving IEEE-754 sign bit). Excel's
|
|
651
|
+
// SIGN normalises zero to +0 (since the documented result for a zero
|
|
652
|
+
// input is 0, not distinguishing signed zero). The `|| 0` collapses
|
|
653
|
+
// both ±0 into `+0` while leaving ±1 untouched.
|
|
654
|
+
return rvNumber(Math.sign(n.value) || 0);
|
|
655
|
+
};
|
|
656
|
+
// ============================================================================
|
|
657
|
+
// Additional Math Functions
|
|
658
|
+
// ============================================================================
|
|
659
|
+
export const fnTRUNC = args => {
|
|
660
|
+
const num = argToNumber(args[0]);
|
|
661
|
+
if (isError(num)) {
|
|
662
|
+
return num;
|
|
663
|
+
}
|
|
664
|
+
const digitsRV = args.length > 1 ? argToNumber(args[1]) : rvNumber(0);
|
|
665
|
+
if (isError(digitsRV)) {
|
|
666
|
+
return digitsRV;
|
|
667
|
+
}
|
|
668
|
+
return rvNumber(applyRounding(num.value, digitsRV.value, Math.trunc));
|
|
669
|
+
};
|
|
670
|
+
export const fnSUMSQ = args => {
|
|
671
|
+
const nums = flattenNumbers(args);
|
|
672
|
+
const err = firstError(nums);
|
|
673
|
+
if (err) {
|
|
674
|
+
return err;
|
|
675
|
+
}
|
|
676
|
+
let sum = 0;
|
|
677
|
+
for (const n of nums) {
|
|
678
|
+
sum += n.value ** 2;
|
|
679
|
+
}
|
|
680
|
+
return isFinite(sum) ? rvNumber(sum) : ERRORS.NUM;
|
|
681
|
+
};
|
|
682
|
+
export const fnGCD = args => {
|
|
683
|
+
const nums = flattenNumbers(args);
|
|
684
|
+
const err = firstError(nums);
|
|
685
|
+
if (err) {
|
|
686
|
+
return err;
|
|
687
|
+
}
|
|
688
|
+
if (nums.length === 0) {
|
|
689
|
+
return rvNumber(0);
|
|
690
|
+
}
|
|
691
|
+
// Excel rejects any negative argument with #NUM!; truncate toward zero
|
|
692
|
+
// for non-integer positives (previous `Math.floor(-5.5) = -6` then
|
|
693
|
+
// `Math.abs` produced 6 instead of #NUM!).
|
|
694
|
+
const coerce = (v) => {
|
|
695
|
+
if (v < 0) {
|
|
696
|
+
return ERRORS.NUM;
|
|
697
|
+
}
|
|
698
|
+
return Math.trunc(v);
|
|
699
|
+
};
|
|
700
|
+
const first = coerce(nums[0].value);
|
|
701
|
+
if (typeof first !== "number") {
|
|
702
|
+
return first;
|
|
703
|
+
}
|
|
704
|
+
let result = first;
|
|
705
|
+
for (let i = 1; i < nums.length; i++) {
|
|
706
|
+
const bi = coerce(nums[i].value);
|
|
707
|
+
if (typeof bi !== "number") {
|
|
708
|
+
return bi;
|
|
709
|
+
}
|
|
710
|
+
let b = bi;
|
|
711
|
+
while (b) {
|
|
712
|
+
const t = b;
|
|
713
|
+
b = result % b;
|
|
714
|
+
result = t;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return rvNumber(result);
|
|
718
|
+
};
|
|
719
|
+
export const fnLCM = args => {
|
|
720
|
+
const nums = flattenNumbers(args);
|
|
721
|
+
const err = firstError(nums);
|
|
722
|
+
if (err) {
|
|
723
|
+
return err;
|
|
724
|
+
}
|
|
725
|
+
if (nums.length === 0) {
|
|
726
|
+
return rvNumber(0);
|
|
727
|
+
}
|
|
728
|
+
const coerce = (v) => {
|
|
729
|
+
if (v < 0) {
|
|
730
|
+
return ERRORS.NUM;
|
|
731
|
+
}
|
|
732
|
+
return Math.trunc(v);
|
|
733
|
+
};
|
|
734
|
+
const first = coerce(nums[0].value);
|
|
735
|
+
if (typeof first !== "number") {
|
|
736
|
+
return first;
|
|
737
|
+
}
|
|
738
|
+
let result = first;
|
|
739
|
+
for (let i = 1; i < nums.length; i++) {
|
|
740
|
+
const bi = coerce(nums[i].value);
|
|
741
|
+
if (typeof bi !== "number") {
|
|
742
|
+
return bi;
|
|
743
|
+
}
|
|
744
|
+
const b = bi;
|
|
745
|
+
if (result === 0 && b === 0) {
|
|
746
|
+
result = 0;
|
|
747
|
+
}
|
|
748
|
+
else {
|
|
749
|
+
let g = result;
|
|
750
|
+
let t = b;
|
|
751
|
+
while (t) {
|
|
752
|
+
const tmp = t;
|
|
753
|
+
t = g % t;
|
|
754
|
+
g = tmp;
|
|
755
|
+
}
|
|
756
|
+
result = (result * b) / g;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return rvNumber(result);
|
|
760
|
+
};
|
|
761
|
+
// ============================================================================
|
|
762
|
+
// More Math Functions
|
|
763
|
+
// ============================================================================
|
|
764
|
+
export const fnEVEN = args => {
|
|
765
|
+
const n = argToNumber(args[0]);
|
|
766
|
+
if (isError(n)) {
|
|
767
|
+
return n;
|
|
768
|
+
}
|
|
769
|
+
const sign = n.value >= 0 ? 1 : -1;
|
|
770
|
+
const abs = Math.abs(n.value);
|
|
771
|
+
const ceil = Math.ceil(abs);
|
|
772
|
+
return rvNumber(sign * (ceil % 2 === 0 ? ceil : ceil + 1));
|
|
773
|
+
};
|
|
774
|
+
export const fnODD = args => {
|
|
775
|
+
const n = argToNumber(args[0]);
|
|
776
|
+
if (isError(n)) {
|
|
777
|
+
return n;
|
|
778
|
+
}
|
|
779
|
+
if (n.value === 0) {
|
|
780
|
+
return rvNumber(1);
|
|
781
|
+
}
|
|
782
|
+
const sign = n.value >= 0 ? 1 : -1;
|
|
783
|
+
const abs = Math.abs(n.value);
|
|
784
|
+
const ceil = Math.ceil(abs);
|
|
785
|
+
return rvNumber(sign * (ceil % 2 === 1 ? ceil : ceil + 1));
|
|
786
|
+
};
|
|
787
|
+
export const fnMROUND = args => {
|
|
788
|
+
const num = argToNumber(args[0]);
|
|
789
|
+
if (isError(num)) {
|
|
790
|
+
return num;
|
|
791
|
+
}
|
|
792
|
+
const multiple = argToNumber(args[1]);
|
|
793
|
+
if (isError(multiple)) {
|
|
794
|
+
return multiple;
|
|
795
|
+
}
|
|
796
|
+
if (multiple.value === 0) {
|
|
797
|
+
return rvNumber(0);
|
|
798
|
+
}
|
|
799
|
+
if ((num.value > 0 && multiple.value < 0) || (num.value < 0 && multiple.value > 0)) {
|
|
800
|
+
return ERRORS.NUM;
|
|
801
|
+
}
|
|
802
|
+
return rvNumber(roundHalfAwayFromZero(num.value / multiple.value) * multiple.value);
|
|
803
|
+
};
|
|
804
|
+
export const fnQUOTIENT = args => {
|
|
805
|
+
const num = argToNumber(args[0]);
|
|
806
|
+
if (isError(num)) {
|
|
807
|
+
return num;
|
|
808
|
+
}
|
|
809
|
+
const den = argToNumber(args[1]);
|
|
810
|
+
if (isError(den)) {
|
|
811
|
+
return den;
|
|
812
|
+
}
|
|
813
|
+
if (den.value === 0) {
|
|
814
|
+
return ERRORS.DIV0;
|
|
815
|
+
}
|
|
816
|
+
return rvNumber(Math.trunc(num.value / den.value));
|
|
817
|
+
};
|
|
818
|
+
export const fnBASE = args => {
|
|
819
|
+
const num = argToNumber(args[0]);
|
|
820
|
+
if (isError(num)) {
|
|
821
|
+
return num;
|
|
822
|
+
}
|
|
823
|
+
const radix = argToNumber(args[1]);
|
|
824
|
+
if (isError(radix)) {
|
|
825
|
+
return radix;
|
|
826
|
+
}
|
|
827
|
+
if (radix.value < 2 || radix.value > 36) {
|
|
828
|
+
return ERRORS.NUM;
|
|
829
|
+
}
|
|
830
|
+
const minLenRV = args.length > 2 ? argToNumber(args[2]) : rvNumber(0);
|
|
831
|
+
if (isError(minLenRV)) {
|
|
832
|
+
return minLenRV;
|
|
833
|
+
}
|
|
834
|
+
const result = Math.floor(num.value).toString(Math.floor(radix.value)).toUpperCase();
|
|
835
|
+
return rvString(minLenRV.value > 0 ? result.padStart(minLenRV.value, "0") : result);
|
|
836
|
+
};
|
|
837
|
+
export const fnDECIMAL = args => {
|
|
838
|
+
const e = topLeft(args[0]);
|
|
839
|
+
if (e.kind === RVKind.Error) {
|
|
840
|
+
return e;
|
|
841
|
+
}
|
|
842
|
+
const text = toStringRV(e);
|
|
843
|
+
const radix = argToNumber(args[1]);
|
|
844
|
+
if (isError(radix)) {
|
|
845
|
+
return radix;
|
|
846
|
+
}
|
|
847
|
+
if (radix.value < 2 || radix.value > 36) {
|
|
848
|
+
return ERRORS.NUM;
|
|
849
|
+
}
|
|
850
|
+
// `parseInt("1G", 16)` returns 1 because JavaScript silently stops at
|
|
851
|
+
// the first invalid digit. Excel's DECIMAL requires every character in
|
|
852
|
+
// the input to be a valid digit for the given radix, so we validate
|
|
853
|
+
// strictly before delegating.
|
|
854
|
+
const base = Math.floor(radix.value);
|
|
855
|
+
const trimmed = text.trim();
|
|
856
|
+
if (trimmed === "") {
|
|
857
|
+
return ERRORS.NUM;
|
|
858
|
+
}
|
|
859
|
+
// Digit alphabet up to base 36: 0-9, A-Z (case-insensitive).
|
|
860
|
+
const match = /^[+-]?([0-9A-Za-z]+)$/.exec(trimmed);
|
|
861
|
+
if (!match) {
|
|
862
|
+
return ERRORS.NUM;
|
|
863
|
+
}
|
|
864
|
+
const digits = match[1].toUpperCase();
|
|
865
|
+
for (const ch of digits) {
|
|
866
|
+
const d = ch >= "0" && ch <= "9" ? ch.charCodeAt(0) - 48 : ch.charCodeAt(0) - 55;
|
|
867
|
+
if (d < 0 || d >= base) {
|
|
868
|
+
return ERRORS.NUM;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
const result = parseInt(trimmed, base);
|
|
872
|
+
if (isNaN(result)) {
|
|
873
|
+
return ERRORS.NUM;
|
|
874
|
+
}
|
|
875
|
+
return rvNumber(result);
|
|
876
|
+
};
|
|
877
|
+
export const fnROMAN = args => {
|
|
878
|
+
const num = argToNumber(args[0]);
|
|
879
|
+
if (isError(num)) {
|
|
880
|
+
return num;
|
|
881
|
+
}
|
|
882
|
+
let n = Math.floor(num.value);
|
|
883
|
+
if (n < 0 || n > 3999) {
|
|
884
|
+
return ERRORS.VALUE;
|
|
885
|
+
}
|
|
886
|
+
if (n === 0) {
|
|
887
|
+
return rvString("");
|
|
888
|
+
}
|
|
889
|
+
// The optional `form` argument (0=classic through 4=simplified) controls
|
|
890
|
+
// how far Excel will collapse repeated characters into subtractive pairs.
|
|
891
|
+
// Higher forms introduce additional patterns beyond the classic IV/IX/
|
|
892
|
+
// XL/XC/CD/CM pairs — e.g. form 1 allows LM for 950, form 4 uses the
|
|
893
|
+
// maximally short forms. A value of TRUE (1) / FALSE (0) maps to form 0
|
|
894
|
+
// / 4 the way Excel does.
|
|
895
|
+
let form = 0;
|
|
896
|
+
if (args.length > 1 && args[1].kind !== RVKind.Blank) {
|
|
897
|
+
const f = argToNumber(args[1]);
|
|
898
|
+
if (isError(f)) {
|
|
899
|
+
return f;
|
|
900
|
+
}
|
|
901
|
+
if (f.value === 1 && f.value === 1) {
|
|
902
|
+
// Boolean inputs flow through argToNumber as 0/1 already.
|
|
903
|
+
}
|
|
904
|
+
form = Math.floor(f.value);
|
|
905
|
+
if (form < 0 || form > 4) {
|
|
906
|
+
return ERRORS.VALUE;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
// Classic table (form 0) — subtractive pairs IV, IX, XL, XC, CD, CM.
|
|
910
|
+
const vals = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
|
|
911
|
+
const syms = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"];
|
|
912
|
+
// Forms 1-4 progressively introduce aggressive subtractive pairs
|
|
913
|
+
// (LM=950, VC=95, IC=99, etc.). Each extra must only trigger at its
|
|
914
|
+
// `minForm` or higher — and must NOT duplicate a value the classic
|
|
915
|
+
// table already covers, or we'd emit the same subtractive pair twice
|
|
916
|
+
// per occurrence (e.g. 1999 would become "CMCMCXCIX" instead of
|
|
917
|
+
// "MCMXCIX").
|
|
918
|
+
const extraByForm = [
|
|
919
|
+
{ value: 995, sym: "VM", minForm: 4 },
|
|
920
|
+
{ value: 990, sym: "XM", minForm: 3 },
|
|
921
|
+
{ value: 950, sym: "LM", minForm: 1 },
|
|
922
|
+
{ value: 495, sym: "VD", minForm: 4 },
|
|
923
|
+
{ value: 490, sym: "XD", minForm: 3 },
|
|
924
|
+
{ value: 450, sym: "LD", minForm: 1 },
|
|
925
|
+
{ value: 99, sym: "IC", minForm: 2 },
|
|
926
|
+
{ value: 95, sym: "VC", minForm: 1 },
|
|
927
|
+
{ value: 49, sym: "IL", minForm: 2 },
|
|
928
|
+
{ value: 45, sym: "VL", minForm: 1 }
|
|
929
|
+
];
|
|
930
|
+
let result = "";
|
|
931
|
+
// Merge: walk from largest remainder, trying any applicable extra then
|
|
932
|
+
// the classic table entry.
|
|
933
|
+
while (n > 0) {
|
|
934
|
+
let matched = false;
|
|
935
|
+
for (const ex of extraByForm) {
|
|
936
|
+
if (form >= ex.minForm && n >= ex.value) {
|
|
937
|
+
result += ex.sym;
|
|
938
|
+
n -= ex.value;
|
|
939
|
+
matched = true;
|
|
940
|
+
break;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
if (matched) {
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
946
|
+
for (let i = 0; i < vals.length; i++) {
|
|
947
|
+
if (n >= vals[i]) {
|
|
948
|
+
result += syms[i];
|
|
949
|
+
n -= vals[i];
|
|
950
|
+
matched = true;
|
|
951
|
+
break;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
if (!matched) {
|
|
955
|
+
// Safety net — should not happen since the classic table covers 1.
|
|
956
|
+
break;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return rvString(result);
|
|
960
|
+
};
|
|
961
|
+
export const fnARABIC = args => {
|
|
962
|
+
const s = topLeft(args[0]);
|
|
963
|
+
if (s.kind === RVKind.Error) {
|
|
964
|
+
return s;
|
|
965
|
+
}
|
|
966
|
+
const text = toStringRV(s).toUpperCase().trim();
|
|
967
|
+
if (text === "") {
|
|
968
|
+
return rvNumber(0);
|
|
969
|
+
}
|
|
970
|
+
const romanMap = { I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000 };
|
|
971
|
+
let result = 0;
|
|
972
|
+
for (let i = 0; i < text.length; i++) {
|
|
973
|
+
const current = romanMap[text[i]];
|
|
974
|
+
const next = romanMap[text[i + 1]];
|
|
975
|
+
if (current === undefined) {
|
|
976
|
+
return ERRORS.VALUE;
|
|
977
|
+
}
|
|
978
|
+
if (next && current < next) {
|
|
979
|
+
result -= current;
|
|
980
|
+
}
|
|
981
|
+
else {
|
|
982
|
+
result += current;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
return rvNumber(result);
|
|
986
|
+
};
|
|
987
|
+
export const fnDEGREES = args => {
|
|
988
|
+
const n = argToNumber(args[0]);
|
|
989
|
+
return isError(n) ? n : rvNumber((n.value * 180) / Math.PI);
|
|
990
|
+
};
|
|
991
|
+
export const fnRADIANS = args => {
|
|
992
|
+
const n = argToNumber(args[0]);
|
|
993
|
+
return isError(n) ? n : rvNumber((n.value * Math.PI) / 180);
|
|
994
|
+
};
|
|
995
|
+
/**
|
|
996
|
+
* Shared driver for SUMX2MY2 / SUMX2PY2 / SUMXMY2. Walks two arrays in
|
|
997
|
+
* lock-step and reduces `combine(x, y)` across matching numeric cells.
|
|
998
|
+
* Non-numeric cells are skipped (Excel behaviour) and errors propagate.
|
|
999
|
+
*/
|
|
1000
|
+
function sumPairedArrays(args, combine) {
|
|
1001
|
+
const a0 = args[0];
|
|
1002
|
+
const a1 = args[1];
|
|
1003
|
+
if (a0.kind !== RVKind.Array || a1.kind !== RVKind.Array) {
|
|
1004
|
+
return ERRORS.VALUE;
|
|
1005
|
+
}
|
|
1006
|
+
const h = Math.min(a0.height, a1.height);
|
|
1007
|
+
const w = Math.min(a0.width, a1.width);
|
|
1008
|
+
let sum = 0;
|
|
1009
|
+
for (let r = 0; r < h; r++) {
|
|
1010
|
+
for (let c = 0; c < w; c++) {
|
|
1011
|
+
const x = a0.rows[r][c];
|
|
1012
|
+
const y = a1.rows[r][c];
|
|
1013
|
+
if (x.kind === RVKind.Error) {
|
|
1014
|
+
return x;
|
|
1015
|
+
}
|
|
1016
|
+
if (y.kind === RVKind.Error) {
|
|
1017
|
+
return y;
|
|
1018
|
+
}
|
|
1019
|
+
if (x.kind !== RVKind.Number || y.kind !== RVKind.Number) {
|
|
1020
|
+
continue;
|
|
1021
|
+
}
|
|
1022
|
+
sum += combine(x.value, y.value);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
return rvNumber(sum);
|
|
1026
|
+
}
|
|
1027
|
+
export const fnSUMX2MY2 = args => sumPairedArrays(args, (x, y) => x * x - y * y);
|
|
1028
|
+
export const fnSUMX2PY2 = args => sumPairedArrays(args, (x, y) => x * x + y * y);
|
|
1029
|
+
export const fnSUMXMY2 = args => sumPairedArrays(args, (x, y) => (x - y) ** 2);
|
|
1030
|
+
export const fnMULTINOMIAL = args => {
|
|
1031
|
+
const nums = flattenNumbers(args);
|
|
1032
|
+
const err = firstError(nums);
|
|
1033
|
+
if (err) {
|
|
1034
|
+
return err;
|
|
1035
|
+
}
|
|
1036
|
+
// Work in log space so the numerator (sum! ≈ up to ~1e307 around sum = 170)
|
|
1037
|
+
// doesn't overflow before we divide. Summing logs avoids the NaN case
|
|
1038
|
+
// `Infinity / Infinity` that the direct product formulation would hit
|
|
1039
|
+
// for large inputs.
|
|
1040
|
+
let sum = 0;
|
|
1041
|
+
let lnDenom = 0;
|
|
1042
|
+
for (const n of nums) {
|
|
1043
|
+
const ni = Math.floor(n.value);
|
|
1044
|
+
if (ni < 0) {
|
|
1045
|
+
return ERRORS.NUM;
|
|
1046
|
+
}
|
|
1047
|
+
sum += ni;
|
|
1048
|
+
for (let i = 2; i <= ni; i++) {
|
|
1049
|
+
lnDenom += Math.log(i);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
let lnNumer = 0;
|
|
1053
|
+
for (let i = 2; i <= sum; i++) {
|
|
1054
|
+
lnNumer += Math.log(i);
|
|
1055
|
+
}
|
|
1056
|
+
const result = Math.exp(lnNumer - lnDenom);
|
|
1057
|
+
if (!isFinite(result)) {
|
|
1058
|
+
return ERRORS.NUM;
|
|
1059
|
+
}
|
|
1060
|
+
// Round to the nearest integer — the log-exp round-trip introduces sub-ulp
|
|
1061
|
+
// noise that would otherwise leave us with things like `20.0000000001`.
|
|
1062
|
+
return rvNumber(Math.round(result));
|
|
1063
|
+
};
|
|
1064
|
+
export const fnFACT = args => {
|
|
1065
|
+
const n = argToNumber(args[0]);
|
|
1066
|
+
if (isError(n)) {
|
|
1067
|
+
return n;
|
|
1068
|
+
}
|
|
1069
|
+
const num = Math.floor(n.value);
|
|
1070
|
+
if (num < 0) {
|
|
1071
|
+
return ERRORS.NUM;
|
|
1072
|
+
}
|
|
1073
|
+
if (num > 170) {
|
|
1074
|
+
return ERRORS.NUM;
|
|
1075
|
+
}
|
|
1076
|
+
let result = 1;
|
|
1077
|
+
for (let i = 2; i <= num; i++) {
|
|
1078
|
+
result *= i;
|
|
1079
|
+
}
|
|
1080
|
+
return rvNumber(result);
|
|
1081
|
+
};
|
|
1082
|
+
export const fnFACTDOUBLE = args => {
|
|
1083
|
+
const n = argToNumber(args[0]);
|
|
1084
|
+
if (isError(n)) {
|
|
1085
|
+
return n;
|
|
1086
|
+
}
|
|
1087
|
+
const num = Math.floor(n.value);
|
|
1088
|
+
if (num < -1) {
|
|
1089
|
+
return ERRORS.NUM;
|
|
1090
|
+
}
|
|
1091
|
+
if (num <= 0) {
|
|
1092
|
+
return rvNumber(1);
|
|
1093
|
+
}
|
|
1094
|
+
let result = 1;
|
|
1095
|
+
for (let i = num; i > 0; i -= 2) {
|
|
1096
|
+
result *= i;
|
|
1097
|
+
if (!isFinite(result)) {
|
|
1098
|
+
return ERRORS.NUM;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
return rvNumber(result);
|
|
1102
|
+
};
|
|
1103
|
+
export const fnCOMBIN = args => {
|
|
1104
|
+
const nRV = argToNumber(args[0]);
|
|
1105
|
+
if (isError(nRV)) {
|
|
1106
|
+
return nRV;
|
|
1107
|
+
}
|
|
1108
|
+
const kRV = argToNumber(args[1]);
|
|
1109
|
+
if (isError(kRV)) {
|
|
1110
|
+
return kRV;
|
|
1111
|
+
}
|
|
1112
|
+
const ni = Math.floor(nRV.value);
|
|
1113
|
+
const ki = Math.floor(kRV.value);
|
|
1114
|
+
if (ni < 0 || ki < 0 || ki > ni) {
|
|
1115
|
+
return ERRORS.NUM;
|
|
1116
|
+
}
|
|
1117
|
+
// Use the `C(n, k) = C(n, k-1) * (n-k+1)/k` recurrence and pick the
|
|
1118
|
+
// smaller of k / (n-k) so the loop runs at most n/2 iterations. The
|
|
1119
|
+
// interleaved divide keeps the running value bounded and preserves
|
|
1120
|
+
// near-full double precision until the final product overflows
|
|
1121
|
+
// magnitude ~1e308 (at which point we surface #NUM!).
|
|
1122
|
+
const kEff = Math.min(ki, ni - ki);
|
|
1123
|
+
let result = 1;
|
|
1124
|
+
for (let i = 0; i < kEff; i++) {
|
|
1125
|
+
result = (result * (ni - i)) / (i + 1);
|
|
1126
|
+
if (!isFinite(result)) {
|
|
1127
|
+
return ERRORS.NUM;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
// Excel returns an integer for COMBIN when the value fits in a double
|
|
1131
|
+
// exactly (≤ 2^53); beyond that (e.g. COMBIN(100, 50) ≈ 1e29) the
|
|
1132
|
+
// result is fundamentally approximate, so rounding only makes sense
|
|
1133
|
+
// below 2^53.
|
|
1134
|
+
return rvNumber(result < 9.007199254740992e15 ? Math.round(result) : result);
|
|
1135
|
+
};
|
|
1136
|
+
export const fnCOMBINA = args => {
|
|
1137
|
+
const nRV = argToNumber(args[0]);
|
|
1138
|
+
if (isError(nRV)) {
|
|
1139
|
+
return nRV;
|
|
1140
|
+
}
|
|
1141
|
+
const kRV = argToNumber(args[1]);
|
|
1142
|
+
if (isError(kRV)) {
|
|
1143
|
+
return kRV;
|
|
1144
|
+
}
|
|
1145
|
+
// Special case: Excel's COMBINA(0, 0) is 1 even though the delegated
|
|
1146
|
+
// `COMBIN(-1, 0)` would flag #NUM! under our generic validation.
|
|
1147
|
+
if (nRV.value === 0 && kRV.value === 0) {
|
|
1148
|
+
return rvNumber(1);
|
|
1149
|
+
}
|
|
1150
|
+
return fnCOMBIN([rvNumber(nRV.value + kRV.value - 1), kRV]);
|
|
1151
|
+
};
|
|
1152
|
+
export const fnPERMUT = args => {
|
|
1153
|
+
const nRV = argToNumber(args[0]);
|
|
1154
|
+
if (isError(nRV)) {
|
|
1155
|
+
return nRV;
|
|
1156
|
+
}
|
|
1157
|
+
const kRV = argToNumber(args[1]);
|
|
1158
|
+
if (isError(kRV)) {
|
|
1159
|
+
return kRV;
|
|
1160
|
+
}
|
|
1161
|
+
const ni = Math.floor(nRV.value);
|
|
1162
|
+
const ki = Math.floor(kRV.value);
|
|
1163
|
+
if (ni < 0 || ki < 0 || ki > ni) {
|
|
1164
|
+
return ERRORS.NUM;
|
|
1165
|
+
}
|
|
1166
|
+
let result = 1;
|
|
1167
|
+
for (let i = 0; i < ki; i++) {
|
|
1168
|
+
result *= ni - i;
|
|
1169
|
+
}
|
|
1170
|
+
return rvNumber(result);
|
|
1171
|
+
};
|
|
1172
|
+
// ============================================================================
|
|
1173
|
+
// Matrix functions: MMULT, MDETERM, MINVERSE, MUNIT
|
|
1174
|
+
// ============================================================================
|
|
1175
|
+
/**
|
|
1176
|
+
* Extract a numeric matrix from an ArrayValue. Returns #VALUE! when any
|
|
1177
|
+
* cell is non-numeric or an error propagates.
|
|
1178
|
+
*/
|
|
1179
|
+
function asNumericMatrix(v) {
|
|
1180
|
+
if (!isArray(v)) {
|
|
1181
|
+
return ERRORS.VALUE;
|
|
1182
|
+
}
|
|
1183
|
+
const arr = v;
|
|
1184
|
+
const out = [];
|
|
1185
|
+
for (const row of arr.rows) {
|
|
1186
|
+
const r = [];
|
|
1187
|
+
for (const cell of row) {
|
|
1188
|
+
if (cell.kind === RVKind.Error) {
|
|
1189
|
+
return cell;
|
|
1190
|
+
}
|
|
1191
|
+
if (cell.kind !== RVKind.Number) {
|
|
1192
|
+
return ERRORS.VALUE;
|
|
1193
|
+
}
|
|
1194
|
+
r.push(cell.value);
|
|
1195
|
+
}
|
|
1196
|
+
out.push(r);
|
|
1197
|
+
}
|
|
1198
|
+
return out;
|
|
1199
|
+
}
|
|
1200
|
+
/**
|
|
1201
|
+
* MMULT(array1, array2) — matrix product. Dimensions must be
|
|
1202
|
+
* (m×k) × (k×n) = (m×n); mismatched sizes return #VALUE!.
|
|
1203
|
+
*/
|
|
1204
|
+
export const fnMMULT = args => {
|
|
1205
|
+
const a = asNumericMatrix(args[0]);
|
|
1206
|
+
if ("kind" in a) {
|
|
1207
|
+
return a;
|
|
1208
|
+
}
|
|
1209
|
+
const b = asNumericMatrix(args[1]);
|
|
1210
|
+
if ("kind" in b) {
|
|
1211
|
+
return b;
|
|
1212
|
+
}
|
|
1213
|
+
const m = a.length;
|
|
1214
|
+
const k = a[0]?.length ?? 0;
|
|
1215
|
+
const k2 = b.length;
|
|
1216
|
+
const n = b[0]?.length ?? 0;
|
|
1217
|
+
if (m === 0 || k === 0 || n === 0 || k !== k2) {
|
|
1218
|
+
return ERRORS.VALUE;
|
|
1219
|
+
}
|
|
1220
|
+
const rows = [];
|
|
1221
|
+
for (let i = 0; i < m; i++) {
|
|
1222
|
+
const row = [];
|
|
1223
|
+
for (let j = 0; j < n; j++) {
|
|
1224
|
+
let sum = 0;
|
|
1225
|
+
for (let p = 0; p < k; p++) {
|
|
1226
|
+
sum += a[i][p] * b[p][j];
|
|
1227
|
+
}
|
|
1228
|
+
row.push(rvNumber(sum));
|
|
1229
|
+
}
|
|
1230
|
+
rows.push(row);
|
|
1231
|
+
}
|
|
1232
|
+
return rvArray(rows);
|
|
1233
|
+
};
|
|
1234
|
+
/**
|
|
1235
|
+
* MDETERM(array) — determinant of a square matrix via Gaussian
|
|
1236
|
+
* elimination with partial pivoting. Non-square or non-numeric input
|
|
1237
|
+
* returns #VALUE!.
|
|
1238
|
+
*/
|
|
1239
|
+
export const fnMDETERM = args => {
|
|
1240
|
+
const mat = asNumericMatrix(args[0]);
|
|
1241
|
+
if ("kind" in mat) {
|
|
1242
|
+
return mat;
|
|
1243
|
+
}
|
|
1244
|
+
const n = mat.length;
|
|
1245
|
+
if (n === 0 || mat[0].length !== n) {
|
|
1246
|
+
return ERRORS.VALUE;
|
|
1247
|
+
}
|
|
1248
|
+
// Copy rows so we don't mutate the caller's array.
|
|
1249
|
+
const a = mat.map(r => r.slice());
|
|
1250
|
+
let det = 1;
|
|
1251
|
+
for (let i = 0; i < n; i++) {
|
|
1252
|
+
// Partial pivot: find row with largest |a[r][i]| for r in [i, n).
|
|
1253
|
+
let pivot = i;
|
|
1254
|
+
for (let r = i + 1; r < n; r++) {
|
|
1255
|
+
if (Math.abs(a[r][i]) > Math.abs(a[pivot][i])) {
|
|
1256
|
+
pivot = r;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
if (Math.abs(a[pivot][i]) < 1e-14) {
|
|
1260
|
+
return rvNumber(0);
|
|
1261
|
+
}
|
|
1262
|
+
if (pivot !== i) {
|
|
1263
|
+
const tmp = a[i];
|
|
1264
|
+
a[i] = a[pivot];
|
|
1265
|
+
a[pivot] = tmp;
|
|
1266
|
+
det = -det;
|
|
1267
|
+
}
|
|
1268
|
+
det *= a[i][i];
|
|
1269
|
+
for (let r = i + 1; r < n; r++) {
|
|
1270
|
+
const factor = a[r][i] / a[i][i];
|
|
1271
|
+
for (let c = i; c < n; c++) {
|
|
1272
|
+
a[r][c] -= factor * a[i][c];
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
return rvNumber(det);
|
|
1277
|
+
};
|
|
1278
|
+
/**
|
|
1279
|
+
* MINVERSE(array) — inverse of a square matrix via Gauss-Jordan
|
|
1280
|
+
* elimination. Singular matrices return #NUM!; non-square return
|
|
1281
|
+
* #VALUE!.
|
|
1282
|
+
*/
|
|
1283
|
+
export const fnMINVERSE = args => {
|
|
1284
|
+
const mat = asNumericMatrix(args[0]);
|
|
1285
|
+
if ("kind" in mat) {
|
|
1286
|
+
return mat;
|
|
1287
|
+
}
|
|
1288
|
+
const n = mat.length;
|
|
1289
|
+
if (n === 0 || mat[0].length !== n) {
|
|
1290
|
+
return ERRORS.VALUE;
|
|
1291
|
+
}
|
|
1292
|
+
// Build augmented matrix [A | I].
|
|
1293
|
+
const aug = [];
|
|
1294
|
+
for (let i = 0; i < n; i++) {
|
|
1295
|
+
const row = new Array(2 * n).fill(0);
|
|
1296
|
+
for (let j = 0; j < n; j++) {
|
|
1297
|
+
row[j] = mat[i][j];
|
|
1298
|
+
}
|
|
1299
|
+
row[n + i] = 1;
|
|
1300
|
+
aug.push(row);
|
|
1301
|
+
}
|
|
1302
|
+
// Gauss-Jordan elimination with partial pivoting.
|
|
1303
|
+
for (let i = 0; i < n; i++) {
|
|
1304
|
+
let pivot = i;
|
|
1305
|
+
for (let r = i + 1; r < n; r++) {
|
|
1306
|
+
if (Math.abs(aug[r][i]) > Math.abs(aug[pivot][i])) {
|
|
1307
|
+
pivot = r;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
if (Math.abs(aug[pivot][i]) < 1e-14) {
|
|
1311
|
+
return ERRORS.NUM; // singular
|
|
1312
|
+
}
|
|
1313
|
+
if (pivot !== i) {
|
|
1314
|
+
const tmp = aug[i];
|
|
1315
|
+
aug[i] = aug[pivot];
|
|
1316
|
+
aug[pivot] = tmp;
|
|
1317
|
+
}
|
|
1318
|
+
const diag = aug[i][i];
|
|
1319
|
+
for (let c = 0; c < 2 * n; c++) {
|
|
1320
|
+
aug[i][c] /= diag;
|
|
1321
|
+
}
|
|
1322
|
+
for (let r = 0; r < n; r++) {
|
|
1323
|
+
if (r === i) {
|
|
1324
|
+
continue;
|
|
1325
|
+
}
|
|
1326
|
+
const factor = aug[r][i];
|
|
1327
|
+
if (factor === 0) {
|
|
1328
|
+
continue;
|
|
1329
|
+
}
|
|
1330
|
+
for (let c = 0; c < 2 * n; c++) {
|
|
1331
|
+
aug[r][c] -= factor * aug[i][c];
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
// Extract inverse from right half.
|
|
1336
|
+
const rows = [];
|
|
1337
|
+
for (let i = 0; i < n; i++) {
|
|
1338
|
+
const row = [];
|
|
1339
|
+
for (let j = 0; j < n; j++) {
|
|
1340
|
+
row.push(rvNumber(aug[i][n + j]));
|
|
1341
|
+
}
|
|
1342
|
+
rows.push(row);
|
|
1343
|
+
}
|
|
1344
|
+
return rvArray(rows);
|
|
1345
|
+
};
|
|
1346
|
+
/**
|
|
1347
|
+
* MUNIT(dimension) — n×n identity matrix.
|
|
1348
|
+
*/
|
|
1349
|
+
export const fnMUNIT = args => {
|
|
1350
|
+
const nV = toNumberRV(topLeft(args[0]));
|
|
1351
|
+
if (isError(nV)) {
|
|
1352
|
+
return nV;
|
|
1353
|
+
}
|
|
1354
|
+
const n = Math.trunc(nV.value);
|
|
1355
|
+
if (n < 1) {
|
|
1356
|
+
return ERRORS.VALUE;
|
|
1357
|
+
}
|
|
1358
|
+
const rows = [];
|
|
1359
|
+
for (let i = 0; i < n; i++) {
|
|
1360
|
+
const row = new Array(n);
|
|
1361
|
+
for (let j = 0; j < n; j++) {
|
|
1362
|
+
row[j] = rvNumber(i === j ? 1 : 0);
|
|
1363
|
+
}
|
|
1364
|
+
rows.push(row);
|
|
1365
|
+
}
|
|
1366
|
+
return rvArray(rows);
|
|
1367
|
+
};
|
|
1368
|
+
// ============================================================================
|
|
1369
|
+
// SERIESSUM
|
|
1370
|
+
// ============================================================================
|
|
1371
|
+
/**
|
|
1372
|
+
* SERIESSUM(x, n, m, coefficients) — returns the sum of a power series
|
|
1373
|
+
* x^n * coef[0] + x^(n+m) * coef[1] + x^(n+2m) * coef[2] + …
|
|
1374
|
+
*/
|
|
1375
|
+
export const fnSERIESSUM = args => {
|
|
1376
|
+
const xV = toNumberRV(topLeft(args[0]));
|
|
1377
|
+
if (isError(xV)) {
|
|
1378
|
+
return xV;
|
|
1379
|
+
}
|
|
1380
|
+
const nV = toNumberRV(topLeft(args[1]));
|
|
1381
|
+
if (isError(nV)) {
|
|
1382
|
+
return nV;
|
|
1383
|
+
}
|
|
1384
|
+
const mV = toNumberRV(topLeft(args[2]));
|
|
1385
|
+
if (isError(mV)) {
|
|
1386
|
+
return mV;
|
|
1387
|
+
}
|
|
1388
|
+
const coeffs = flattenNumbers([args[3]]);
|
|
1389
|
+
const err = firstError(coeffs);
|
|
1390
|
+
if (err) {
|
|
1391
|
+
return err;
|
|
1392
|
+
}
|
|
1393
|
+
if (coeffs.length === 0) {
|
|
1394
|
+
return ERRORS.VALUE;
|
|
1395
|
+
}
|
|
1396
|
+
let total = 0;
|
|
1397
|
+
let exponent = nV.value;
|
|
1398
|
+
for (const c of coeffs) {
|
|
1399
|
+
total += c.value * Math.pow(xV.value, exponent);
|
|
1400
|
+
exponent += mV.value;
|
|
1401
|
+
}
|
|
1402
|
+
if (!isFinite(total)) {
|
|
1403
|
+
return ERRORS.NUM;
|
|
1404
|
+
}
|
|
1405
|
+
return rvNumber(total);
|
|
1406
|
+
};
|