@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,3488 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Statistical Functions
|
|
4
|
+
*
|
|
5
|
+
* Native RuntimeValue implementations.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.fnNEGBINOM_DIST = exports.fnHYPGEOM_DIST = exports.fnBINOM_INV = exports.fnBINOM_DIST_RANGE = exports.fnBINOM_DIST = exports.fnPOISSON_DIST = exports.fnMINA = exports.fnMAXA = exports.fnAVERAGEA = exports.fnFISHERINV = exports.fnFISHER = exports.fnRANK_AVG = exports.fnCOVARIANCE_S = exports.fnCOVARIANCE_P = exports.fnCONFIDENCE_T = exports.fnCONFIDENCENORM = exports.fnAVEDEV = exports.fnDEVSQ = exports.fnTRIMMEAN = exports.fnHARMEAN = exports.fnGEOMEAN = exports.fnPERMUT = exports.fnCOMBINA = exports.fnCOMBIN = exports.fnFACTDOUBLE = exports.fnFACT = exports.fnFORECAST = exports.fnSTEYX = exports.fnRSQ = exports.fnINTERCEPT = exports.fnSLOPE = exports.fnCORREL = exports.fnMODE_MULT = exports.fnMODE = exports.fnQUARTILEEXC = exports.fnQUARTILE = exports.fnPERCENTILEEXC = exports.fnPERCENTILE = exports.fnNORMINV = exports.fnNORMSINV = exports.fnNORMDIST = exports.fnNORMSDIST = exports.fnVARP = exports.fnVAR = exports.fnSTDEVP = exports.fnSTDEV = exports.fnRANK = exports.fnSMALL = exports.fnLARGE = exports.fnMEDIAN = void 0;
|
|
9
|
+
exports.fnCHISQ_TEST = exports.fnT_TEST = exports.fnF_TEST = exports.fnZ_TEST = exports.fnPROB = exports.fnPERCENTRANK_EXC = exports.fnPERCENTRANK_INC = exports.fnPERCENTRANK = exports.fnKURT = exports.fnSKEW_P = exports.fnSKEW = exports.fnF_INV_RT = exports.fnF_DIST_RT = exports.fnLOGEST = exports.fnLINEST = exports.fnTREND = exports.fnGROWTH = exports.fnFREQUENCY = exports.fnSTANDARDIZE = exports.fnERFC = exports.fnERF = exports.fnGAUSS = exports.fnPHI = exports.fnLOGNORM_INV = exports.fnLOGNORM_DIST = exports.fnWEIBULL_DIST = exports.fnEXPON_DIST = exports.fnGAMMA_INV = exports.fnGAMMA_DIST = exports.fnGAMMALN = exports.fnGAMMA = exports.fnBETA_INV = exports.fnBETA_DIST = exports.fnT_INV_2T = exports.fnT_DIST_RT = exports.fnT_DIST_2T = exports.fnT_INV = exports.fnT_DIST = exports.fnF_INV = exports.fnF_DIST = exports.fnCHISQ_INV_RT = exports.fnCHISQ_DIST_RT = exports.fnCHISQ_INV = exports.fnCHISQ_DIST = void 0;
|
|
10
|
+
const values_1 = require("../runtime/values");
|
|
11
|
+
const _shared_1 = require("./_shared");
|
|
12
|
+
/**
|
|
13
|
+
* Extract a boolean from a single arg. Returns the boolean or ErrorValue.
|
|
14
|
+
*/
|
|
15
|
+
function boolArg(args, idx) {
|
|
16
|
+
const rv = (0, values_1.toBooleanRV)((0, values_1.topLeft)(args[idx]));
|
|
17
|
+
if (rv.kind === 4 /* RVKind.Error */) {
|
|
18
|
+
return { ok: false, error: rv };
|
|
19
|
+
}
|
|
20
|
+
return { ok: true, value: rv.value };
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Check if an arg is an array.
|
|
24
|
+
*/
|
|
25
|
+
function isArrayArg(arg) {
|
|
26
|
+
return arg.kind === 5 /* RVKind.Array */;
|
|
27
|
+
}
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// MEDIAN, LARGE, SMALL, RANK
|
|
30
|
+
// ============================================================================
|
|
31
|
+
/**
|
|
32
|
+
* Quickselect: returns the k-th smallest element (0-indexed) of `arr`
|
|
33
|
+
* in-place, in expected O(n) time.
|
|
34
|
+
*
|
|
35
|
+
* Uses Hoare partitioning with a "median of three" pivot choice for
|
|
36
|
+
* resilience against already-sorted and adversarial inputs. The input
|
|
37
|
+
* array is reorganised around the pivot — callers that need the original
|
|
38
|
+
* order must pass a copy.
|
|
39
|
+
*/
|
|
40
|
+
function quickselect(arr, k) {
|
|
41
|
+
let lo = 0;
|
|
42
|
+
let hi = arr.length - 1;
|
|
43
|
+
while (lo < hi) {
|
|
44
|
+
// median-of-three pivot
|
|
45
|
+
const mid = (lo + hi) >> 1;
|
|
46
|
+
const a = arr[lo];
|
|
47
|
+
const b = arr[mid];
|
|
48
|
+
const c = arr[hi];
|
|
49
|
+
const pivot = a < b ? (b < c ? b : a < c ? c : a) : a < c ? a : b < c ? c : b;
|
|
50
|
+
let i = lo;
|
|
51
|
+
let j = hi;
|
|
52
|
+
while (i <= j) {
|
|
53
|
+
while (arr[i] < pivot) {
|
|
54
|
+
i++;
|
|
55
|
+
}
|
|
56
|
+
while (arr[j] > pivot) {
|
|
57
|
+
j--;
|
|
58
|
+
}
|
|
59
|
+
if (i <= j) {
|
|
60
|
+
const t = arr[i];
|
|
61
|
+
arr[i] = arr[j];
|
|
62
|
+
arr[j] = t;
|
|
63
|
+
i++;
|
|
64
|
+
j--;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (k <= j) {
|
|
68
|
+
hi = j;
|
|
69
|
+
}
|
|
70
|
+
else if (k >= i) {
|
|
71
|
+
lo = i;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// pivot settled at k
|
|
75
|
+
return arr[k];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return arr[k];
|
|
79
|
+
}
|
|
80
|
+
const fnMEDIAN = args => {
|
|
81
|
+
const nums = (0, _shared_1.flattenNumbers)(args);
|
|
82
|
+
const err = (0, _shared_1.firstError)(nums);
|
|
83
|
+
if (err) {
|
|
84
|
+
return err;
|
|
85
|
+
}
|
|
86
|
+
if (nums.length === 0) {
|
|
87
|
+
return values_1.ERRORS.NUM;
|
|
88
|
+
}
|
|
89
|
+
const values = nums.map(n => n.value);
|
|
90
|
+
const n = values.length;
|
|
91
|
+
const mid = n >> 1;
|
|
92
|
+
if (n % 2 !== 0) {
|
|
93
|
+
return (0, values_1.rvNumber)(quickselect(values, mid));
|
|
94
|
+
}
|
|
95
|
+
// Even count: average of (n/2 − 1)-th and (n/2)-th smallest. Use
|
|
96
|
+
// quickselect twice — but the second search can be limited to the upper
|
|
97
|
+
// half produced by the first call, since quickselect leaves that region
|
|
98
|
+
// sorted w.r.t. the pivot.
|
|
99
|
+
const hi = quickselect(values, mid);
|
|
100
|
+
// After selecting index `mid`, every element at position < mid is ≤ hi.
|
|
101
|
+
// The lower of the two middle values is the max of that prefix.
|
|
102
|
+
let lo = Number.NEGATIVE_INFINITY;
|
|
103
|
+
for (let i = 0; i < mid; i++) {
|
|
104
|
+
if (values[i] > lo) {
|
|
105
|
+
lo = values[i];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return (0, values_1.rvNumber)((lo + hi) / 2);
|
|
109
|
+
};
|
|
110
|
+
exports.fnMEDIAN = fnMEDIAN;
|
|
111
|
+
const fnLARGE = args => {
|
|
112
|
+
const nums = (0, _shared_1.flattenNumbers)([args[0]]);
|
|
113
|
+
const err = (0, _shared_1.firstError)(nums);
|
|
114
|
+
if (err) {
|
|
115
|
+
return err;
|
|
116
|
+
}
|
|
117
|
+
const values = nums.map(n => n.value);
|
|
118
|
+
// k can be an array (Excel broadcasts); when it is, return an array
|
|
119
|
+
// with the same shape where each cell holds LARGE at that k.
|
|
120
|
+
if (args[1].kind === 5 /* RVKind.Array */) {
|
|
121
|
+
const kArr = args[1];
|
|
122
|
+
const outRows = [];
|
|
123
|
+
for (const row of kArr.rows) {
|
|
124
|
+
const outRow = [];
|
|
125
|
+
for (const cell of row) {
|
|
126
|
+
if (cell.kind === 4 /* RVKind.Error */) {
|
|
127
|
+
outRow.push(cell);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const kn = (0, values_1.toNumberRV)(cell);
|
|
131
|
+
if (kn.kind === 4 /* RVKind.Error */) {
|
|
132
|
+
outRow.push(kn);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const kInt = Math.floor(kn.value);
|
|
136
|
+
if (kInt < 1 || kInt > values.length) {
|
|
137
|
+
outRow.push(values_1.ERRORS.NUM);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
// k-th largest == (n − k)-th smallest. Use a copy — quickselect
|
|
141
|
+
// mutates the array it works on, and we need a fresh view for
|
|
142
|
+
// every cell in the output.
|
|
143
|
+
outRow.push((0, values_1.rvNumber)(quickselect(values.slice(), values.length - kInt)));
|
|
144
|
+
}
|
|
145
|
+
outRows.push(outRow);
|
|
146
|
+
}
|
|
147
|
+
return (0, values_1.rvArray)(outRows);
|
|
148
|
+
}
|
|
149
|
+
const k = (0, _shared_1.argToNumber)(args[1]);
|
|
150
|
+
if (k.kind === 4 /* RVKind.Error */) {
|
|
151
|
+
return k;
|
|
152
|
+
}
|
|
153
|
+
const kInt = Math.floor(k.value);
|
|
154
|
+
if (kInt < 1 || kInt > values.length) {
|
|
155
|
+
return values_1.ERRORS.NUM;
|
|
156
|
+
}
|
|
157
|
+
return (0, values_1.rvNumber)(quickselect(values, values.length - kInt));
|
|
158
|
+
};
|
|
159
|
+
exports.fnLARGE = fnLARGE;
|
|
160
|
+
const fnSMALL = args => {
|
|
161
|
+
const nums = (0, _shared_1.flattenNumbers)([args[0]]);
|
|
162
|
+
const err = (0, _shared_1.firstError)(nums);
|
|
163
|
+
if (err) {
|
|
164
|
+
return err;
|
|
165
|
+
}
|
|
166
|
+
const values = nums.map(n => n.value);
|
|
167
|
+
if (args[1].kind === 5 /* RVKind.Array */) {
|
|
168
|
+
const kArr = args[1];
|
|
169
|
+
const outRows = [];
|
|
170
|
+
for (const row of kArr.rows) {
|
|
171
|
+
const outRow = [];
|
|
172
|
+
for (const cell of row) {
|
|
173
|
+
if (cell.kind === 4 /* RVKind.Error */) {
|
|
174
|
+
outRow.push(cell);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const kn = (0, values_1.toNumberRV)(cell);
|
|
178
|
+
if (kn.kind === 4 /* RVKind.Error */) {
|
|
179
|
+
outRow.push(kn);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const kInt = Math.floor(kn.value);
|
|
183
|
+
if (kInt < 1 || kInt > values.length) {
|
|
184
|
+
outRow.push(values_1.ERRORS.NUM);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
outRow.push((0, values_1.rvNumber)(quickselect(values.slice(), kInt - 1)));
|
|
188
|
+
}
|
|
189
|
+
outRows.push(outRow);
|
|
190
|
+
}
|
|
191
|
+
return (0, values_1.rvArray)(outRows);
|
|
192
|
+
}
|
|
193
|
+
const k = (0, _shared_1.argToNumber)(args[1]);
|
|
194
|
+
if (k.kind === 4 /* RVKind.Error */) {
|
|
195
|
+
return k;
|
|
196
|
+
}
|
|
197
|
+
const kInt = Math.floor(k.value);
|
|
198
|
+
if (kInt < 1 || kInt > values.length) {
|
|
199
|
+
return values_1.ERRORS.NUM;
|
|
200
|
+
}
|
|
201
|
+
return (0, values_1.rvNumber)(quickselect(values, kInt - 1));
|
|
202
|
+
};
|
|
203
|
+
exports.fnSMALL = fnSMALL;
|
|
204
|
+
const fnRANK = args => {
|
|
205
|
+
const num = (0, _shared_1.argToNumber)(args[0]);
|
|
206
|
+
if (num.kind === 4 /* RVKind.Error */) {
|
|
207
|
+
return num;
|
|
208
|
+
}
|
|
209
|
+
const nums = (0, _shared_1.flattenNumbers)([args[1]]);
|
|
210
|
+
const err = (0, _shared_1.firstError)(nums);
|
|
211
|
+
if (err) {
|
|
212
|
+
return err;
|
|
213
|
+
}
|
|
214
|
+
const orderRV = args.length > 2 ? (0, _shared_1.argToNumber)(args[2]) : (0, values_1.rvNumber)(0);
|
|
215
|
+
if (orderRV.kind === 4 /* RVKind.Error */) {
|
|
216
|
+
return orderRV;
|
|
217
|
+
}
|
|
218
|
+
const order = orderRV.value;
|
|
219
|
+
const sorted = order === 0
|
|
220
|
+
? nums
|
|
221
|
+
.map(n => n.value)
|
|
222
|
+
.slice()
|
|
223
|
+
.sort((a, b) => b - a)
|
|
224
|
+
: nums
|
|
225
|
+
.map(n => n.value)
|
|
226
|
+
.slice()
|
|
227
|
+
.sort((a, b) => a - b);
|
|
228
|
+
const idx = sorted.indexOf(num.value);
|
|
229
|
+
return idx === -1 ? values_1.ERRORS.NA : (0, values_1.rvNumber)(idx + 1);
|
|
230
|
+
};
|
|
231
|
+
exports.fnRANK = fnRANK;
|
|
232
|
+
// ============================================================================
|
|
233
|
+
// STDEV, STDEVP, VAR, VARP
|
|
234
|
+
// ============================================================================
|
|
235
|
+
/**
|
|
236
|
+
* Compute mean and sum of squared deviations from mean. Used by the
|
|
237
|
+
* STDEV/STDEVP/VAR/VARP family to share a single pass through the data.
|
|
238
|
+
* Returns `null` when there is no data at all (callers decide whether that
|
|
239
|
+
* should be `#DIV/0!` or zero given the sample/population convention).
|
|
240
|
+
*/
|
|
241
|
+
function computeMeanAndSumSq(nums) {
|
|
242
|
+
const n = nums.length;
|
|
243
|
+
if (n === 0) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
let sum = 0;
|
|
247
|
+
for (const v of nums) {
|
|
248
|
+
sum += v;
|
|
249
|
+
}
|
|
250
|
+
const mean = sum / n;
|
|
251
|
+
let sumSq = 0;
|
|
252
|
+
for (const v of nums) {
|
|
253
|
+
sumSq += (v - mean) ** 2;
|
|
254
|
+
}
|
|
255
|
+
return { n, mean, sumSq };
|
|
256
|
+
}
|
|
257
|
+
/** Resolve {args} to `number[]` or an error. Shared by STDEV/VAR family. */
|
|
258
|
+
function toNumberArray(args) {
|
|
259
|
+
const rawNums = (0, _shared_1.flattenNumbers)(args);
|
|
260
|
+
const err = (0, _shared_1.firstError)(rawNums);
|
|
261
|
+
if (err) {
|
|
262
|
+
return err;
|
|
263
|
+
}
|
|
264
|
+
return rawNums.map(n => n.value);
|
|
265
|
+
}
|
|
266
|
+
const fnSTDEV = args => {
|
|
267
|
+
const nums = toNumberArray(args);
|
|
268
|
+
if (!Array.isArray(nums)) {
|
|
269
|
+
return nums;
|
|
270
|
+
}
|
|
271
|
+
const stats = computeMeanAndSumSq(nums);
|
|
272
|
+
if (!stats || stats.n < 2) {
|
|
273
|
+
return values_1.ERRORS.DIV0;
|
|
274
|
+
}
|
|
275
|
+
return (0, values_1.rvNumber)(Math.sqrt(stats.sumSq / (stats.n - 1)));
|
|
276
|
+
};
|
|
277
|
+
exports.fnSTDEV = fnSTDEV;
|
|
278
|
+
const fnSTDEVP = args => {
|
|
279
|
+
const nums = toNumberArray(args);
|
|
280
|
+
if (!Array.isArray(nums)) {
|
|
281
|
+
return nums;
|
|
282
|
+
}
|
|
283
|
+
const stats = computeMeanAndSumSq(nums);
|
|
284
|
+
if (!stats) {
|
|
285
|
+
return values_1.ERRORS.DIV0;
|
|
286
|
+
}
|
|
287
|
+
return (0, values_1.rvNumber)(Math.sqrt(stats.sumSq / stats.n));
|
|
288
|
+
};
|
|
289
|
+
exports.fnSTDEVP = fnSTDEVP;
|
|
290
|
+
const fnVAR = args => {
|
|
291
|
+
const nums = toNumberArray(args);
|
|
292
|
+
if (!Array.isArray(nums)) {
|
|
293
|
+
return nums;
|
|
294
|
+
}
|
|
295
|
+
const stats = computeMeanAndSumSq(nums);
|
|
296
|
+
if (!stats || stats.n < 2) {
|
|
297
|
+
return values_1.ERRORS.DIV0;
|
|
298
|
+
}
|
|
299
|
+
return (0, values_1.rvNumber)(stats.sumSq / (stats.n - 1));
|
|
300
|
+
};
|
|
301
|
+
exports.fnVAR = fnVAR;
|
|
302
|
+
const fnVARP = args => {
|
|
303
|
+
const nums = toNumberArray(args);
|
|
304
|
+
if (!Array.isArray(nums)) {
|
|
305
|
+
return nums;
|
|
306
|
+
}
|
|
307
|
+
const stats = computeMeanAndSumSq(nums);
|
|
308
|
+
if (!stats) {
|
|
309
|
+
return values_1.ERRORS.DIV0;
|
|
310
|
+
}
|
|
311
|
+
return (0, values_1.rvNumber)(stats.sumSq / stats.n);
|
|
312
|
+
};
|
|
313
|
+
exports.fnVARP = fnVARP;
|
|
314
|
+
// ============================================================================
|
|
315
|
+
// Advanced Statistical Functions — private math helpers
|
|
316
|
+
// ============================================================================
|
|
317
|
+
// Peter Acklam's rational approximation for the standard normal inverse CDF.
|
|
318
|
+
// Accuracy: |error| < 1.15e-9 across the full range (0, 1).
|
|
319
|
+
function normSInv(p) {
|
|
320
|
+
if (p <= 0 || p >= 1) {
|
|
321
|
+
return NaN;
|
|
322
|
+
}
|
|
323
|
+
if (p < 0.5) {
|
|
324
|
+
return -normSInv(1 - p);
|
|
325
|
+
}
|
|
326
|
+
// Coefficients for rational approximation
|
|
327
|
+
const a1 = -3.969683028665376e1;
|
|
328
|
+
const a2 = 2.209460984245205e2;
|
|
329
|
+
const a3 = -2.759285104469687e2;
|
|
330
|
+
const a4 = 1.38357751867269e2;
|
|
331
|
+
const a5 = -3.066479806614716e1;
|
|
332
|
+
const a6 = 2.506628277459239;
|
|
333
|
+
const b1 = -5.447609879822406e1;
|
|
334
|
+
const b2 = 1.615858368580409e2;
|
|
335
|
+
const b3 = -1.556989798598866e2;
|
|
336
|
+
const b4 = 6.680131188771972e1;
|
|
337
|
+
const b5 = -1.328068155288572e1;
|
|
338
|
+
const c1 = -7.784894002430293e-3;
|
|
339
|
+
const c2 = -3.223964580411365e-1;
|
|
340
|
+
const c3 = -2.400758277161838;
|
|
341
|
+
const c4 = -2.549732539343734;
|
|
342
|
+
const c5 = 4.374664141464968;
|
|
343
|
+
const c6 = 2.938163982698783;
|
|
344
|
+
const d1 = 7.784695709041462e-3;
|
|
345
|
+
const d2 = 3.224671290700398e-1;
|
|
346
|
+
const d3 = 2.445134137142996;
|
|
347
|
+
const d4 = 3.754408661907416;
|
|
348
|
+
const pLow = 0.02425;
|
|
349
|
+
const pHigh = 1 - pLow;
|
|
350
|
+
if (p < pLow) {
|
|
351
|
+
// Rational approximation for lower region
|
|
352
|
+
const q = Math.sqrt(-2 * Math.log(p));
|
|
353
|
+
return ((((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6) /
|
|
354
|
+
((((d1 * q + d2) * q + d3) * q + d4) * q + 1));
|
|
355
|
+
}
|
|
356
|
+
if (p <= pHigh) {
|
|
357
|
+
// Rational approximation for central region
|
|
358
|
+
const q = p - 0.5;
|
|
359
|
+
const r = q * q;
|
|
360
|
+
return (((((((a1 * r + a2) * r + a3) * r + a4) * r + a5) * r + a6) * q) /
|
|
361
|
+
(((((b1 * r + b2) * r + b3) * r + b4) * r + b5) * r + 1));
|
|
362
|
+
}
|
|
363
|
+
// Upper region — use symmetry
|
|
364
|
+
const q = Math.sqrt(-2 * Math.log(1 - p));
|
|
365
|
+
return -((((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6) /
|
|
366
|
+
((((d1 * q + d2) * q + d3) * q + d4) * q + 1));
|
|
367
|
+
}
|
|
368
|
+
// Standard normal CDF approximation (Abramowitz & Stegun 7.1.26). The
|
|
369
|
+
// approximation has max error ~7.5e-8 — good enough for GAUSS/NORM.S.DIST
|
|
370
|
+
// but it does NOT evaluate to exactly 0.5 at x = 0 (the erf kernel leaves
|
|
371
|
+
// a residual ≈ 5e-10). Excel-facing callers expect symmetry around 0, so
|
|
372
|
+
// we short-circuit that single point.
|
|
373
|
+
function normSDist(x) {
|
|
374
|
+
if (x === 0) {
|
|
375
|
+
return 0.5;
|
|
376
|
+
}
|
|
377
|
+
const a1 = 0.254829592;
|
|
378
|
+
const a2 = -0.284496736;
|
|
379
|
+
const a3 = 1.421413741;
|
|
380
|
+
const a4 = -1.453152027;
|
|
381
|
+
const a5 = 1.061405429;
|
|
382
|
+
const p = 0.3275911;
|
|
383
|
+
const sign = x < 0 ? -1 : 1;
|
|
384
|
+
x = Math.abs(x) / Math.SQRT2;
|
|
385
|
+
const t = 1.0 / (1.0 + p * x);
|
|
386
|
+
const y = 1.0 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);
|
|
387
|
+
return 0.5 * (1.0 + sign * y);
|
|
388
|
+
}
|
|
389
|
+
// Standard normal PDF
|
|
390
|
+
function normSPdf(x) {
|
|
391
|
+
return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI);
|
|
392
|
+
}
|
|
393
|
+
// ============================================================================
|
|
394
|
+
// Normal Distribution Functions
|
|
395
|
+
// ============================================================================
|
|
396
|
+
const fnNORMSDIST = args => {
|
|
397
|
+
const z = (0, _shared_1.argToNumber)(args[0]);
|
|
398
|
+
if (z.kind === 4 /* RVKind.Error */) {
|
|
399
|
+
return z;
|
|
400
|
+
}
|
|
401
|
+
// Legacy NORM.S.DIST compatibility: single arg = CDF
|
|
402
|
+
if (args.length > 1) {
|
|
403
|
+
const cum = boolArg(args, 1);
|
|
404
|
+
if (!cum.ok) {
|
|
405
|
+
return cum.error;
|
|
406
|
+
}
|
|
407
|
+
return (0, values_1.rvNumber)(cum.value ? normSDist(z.value) : normSPdf(z.value));
|
|
408
|
+
}
|
|
409
|
+
return (0, values_1.rvNumber)(normSDist(z.value));
|
|
410
|
+
};
|
|
411
|
+
exports.fnNORMSDIST = fnNORMSDIST;
|
|
412
|
+
const fnNORMDIST = args => {
|
|
413
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
414
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
415
|
+
return x;
|
|
416
|
+
}
|
|
417
|
+
const mean = (0, _shared_1.argToNumber)(args[1]);
|
|
418
|
+
if (mean.kind === 4 /* RVKind.Error */) {
|
|
419
|
+
return mean;
|
|
420
|
+
}
|
|
421
|
+
const stddev = (0, _shared_1.argToNumber)(args[2]);
|
|
422
|
+
if (stddev.kind === 4 /* RVKind.Error */) {
|
|
423
|
+
return stddev;
|
|
424
|
+
}
|
|
425
|
+
if (stddev.value <= 0) {
|
|
426
|
+
return values_1.ERRORS.NUM;
|
|
427
|
+
}
|
|
428
|
+
const cum = boolArg(args, 3);
|
|
429
|
+
if (!cum.ok) {
|
|
430
|
+
return cum.error;
|
|
431
|
+
}
|
|
432
|
+
const zVal = (x.value - mean.value) / stddev.value;
|
|
433
|
+
if (cum.value) {
|
|
434
|
+
return (0, values_1.rvNumber)(normSDist(zVal));
|
|
435
|
+
}
|
|
436
|
+
return (0, values_1.rvNumber)(normSPdf(zVal) / stddev.value);
|
|
437
|
+
};
|
|
438
|
+
exports.fnNORMDIST = fnNORMDIST;
|
|
439
|
+
const fnNORMSINV = args => {
|
|
440
|
+
const p = (0, _shared_1.argToNumber)(args[0]);
|
|
441
|
+
if (p.kind === 4 /* RVKind.Error */) {
|
|
442
|
+
return p;
|
|
443
|
+
}
|
|
444
|
+
if (p.value <= 0 || p.value >= 1) {
|
|
445
|
+
return values_1.ERRORS.NUM;
|
|
446
|
+
}
|
|
447
|
+
return (0, values_1.rvNumber)(normSInv(p.value));
|
|
448
|
+
};
|
|
449
|
+
exports.fnNORMSINV = fnNORMSINV;
|
|
450
|
+
const fnNORMINV = args => {
|
|
451
|
+
const p = (0, _shared_1.argToNumber)(args[0]);
|
|
452
|
+
if (p.kind === 4 /* RVKind.Error */) {
|
|
453
|
+
return p;
|
|
454
|
+
}
|
|
455
|
+
const mean = (0, _shared_1.argToNumber)(args[1]);
|
|
456
|
+
if (mean.kind === 4 /* RVKind.Error */) {
|
|
457
|
+
return mean;
|
|
458
|
+
}
|
|
459
|
+
const stddev = (0, _shared_1.argToNumber)(args[2]);
|
|
460
|
+
if (stddev.kind === 4 /* RVKind.Error */) {
|
|
461
|
+
return stddev;
|
|
462
|
+
}
|
|
463
|
+
if (p.value <= 0 || p.value >= 1 || stddev.value <= 0) {
|
|
464
|
+
return values_1.ERRORS.NUM;
|
|
465
|
+
}
|
|
466
|
+
return (0, values_1.rvNumber)(mean.value + stddev.value * normSInv(p.value));
|
|
467
|
+
};
|
|
468
|
+
exports.fnNORMINV = fnNORMINV;
|
|
469
|
+
// ============================================================================
|
|
470
|
+
// PERCENTILE, QUARTILE, MODE
|
|
471
|
+
// ============================================================================
|
|
472
|
+
const fnPERCENTILE = args => {
|
|
473
|
+
const nums = (0, _shared_1.flattenNumbers)([args[0]])
|
|
474
|
+
.filter((v) => v.kind === 1 /* RVKind.Number */)
|
|
475
|
+
.map(n => n.value);
|
|
476
|
+
const k = (0, _shared_1.argToNumber)(args[1]);
|
|
477
|
+
if (k.kind === 4 /* RVKind.Error */) {
|
|
478
|
+
return k;
|
|
479
|
+
}
|
|
480
|
+
if (k.value < 0 || k.value > 1 || nums.length === 0) {
|
|
481
|
+
return values_1.ERRORS.NUM;
|
|
482
|
+
}
|
|
483
|
+
nums.sort((a, b) => a - b);
|
|
484
|
+
const n = nums.length;
|
|
485
|
+
if (n === 1) {
|
|
486
|
+
return (0, values_1.rvNumber)(nums[0]);
|
|
487
|
+
}
|
|
488
|
+
const rank = k.value * (n - 1);
|
|
489
|
+
const lower = Math.floor(rank);
|
|
490
|
+
const upper = Math.ceil(rank);
|
|
491
|
+
const frac = rank - lower;
|
|
492
|
+
return (0, values_1.rvNumber)(nums[lower] + frac * (nums[upper] - nums[lower]));
|
|
493
|
+
};
|
|
494
|
+
exports.fnPERCENTILE = fnPERCENTILE;
|
|
495
|
+
const fnPERCENTILEEXC = args => {
|
|
496
|
+
const nums = (0, _shared_1.flattenNumbers)([args[0]])
|
|
497
|
+
.filter((v) => v.kind === 1 /* RVKind.Number */)
|
|
498
|
+
.map(n => n.value);
|
|
499
|
+
const k = (0, _shared_1.argToNumber)(args[1]);
|
|
500
|
+
if (k.kind === 4 /* RVKind.Error */) {
|
|
501
|
+
return k;
|
|
502
|
+
}
|
|
503
|
+
const n = nums.length;
|
|
504
|
+
if (k.value <= 0 || k.value >= 1 || n === 0) {
|
|
505
|
+
return values_1.ERRORS.NUM;
|
|
506
|
+
}
|
|
507
|
+
if (k.value < 1 / (n + 1) || k.value > n / (n + 1)) {
|
|
508
|
+
return values_1.ERRORS.NUM;
|
|
509
|
+
}
|
|
510
|
+
nums.sort((a, b) => a - b);
|
|
511
|
+
const rank = k.value * (n + 1) - 1;
|
|
512
|
+
const lower = Math.floor(rank);
|
|
513
|
+
const upper = Math.ceil(rank);
|
|
514
|
+
const frac = rank - lower;
|
|
515
|
+
return (0, values_1.rvNumber)(nums[Math.max(0, lower)] + frac * (nums[Math.min(n - 1, upper)] - nums[Math.max(0, lower)]));
|
|
516
|
+
};
|
|
517
|
+
exports.fnPERCENTILEEXC = fnPERCENTILEEXC;
|
|
518
|
+
const fnQUARTILE = args => {
|
|
519
|
+
const quart = (0, _shared_1.argToNumber)(args[1]);
|
|
520
|
+
if (quart.kind === 4 /* RVKind.Error */) {
|
|
521
|
+
return quart;
|
|
522
|
+
}
|
|
523
|
+
if (quart.value < 0 || quart.value > 4) {
|
|
524
|
+
return values_1.ERRORS.NUM;
|
|
525
|
+
}
|
|
526
|
+
return (0, exports.fnPERCENTILE)([args[0], (0, values_1.rvNumber)(quart.value / 4)]);
|
|
527
|
+
};
|
|
528
|
+
exports.fnQUARTILE = fnQUARTILE;
|
|
529
|
+
const fnQUARTILEEXC = args => {
|
|
530
|
+
const quart = (0, _shared_1.argToNumber)(args[1]);
|
|
531
|
+
if (quart.kind === 4 /* RVKind.Error */) {
|
|
532
|
+
return quart;
|
|
533
|
+
}
|
|
534
|
+
if (quart.value < 1 || quart.value > 3) {
|
|
535
|
+
return values_1.ERRORS.NUM;
|
|
536
|
+
}
|
|
537
|
+
return (0, exports.fnPERCENTILEEXC)([args[0], (0, values_1.rvNumber)(quart.value / 4)]);
|
|
538
|
+
};
|
|
539
|
+
exports.fnQUARTILEEXC = fnQUARTILEEXC;
|
|
540
|
+
const fnMODE = args => {
|
|
541
|
+
const all = (0, _shared_1.flattenNumbers)(args);
|
|
542
|
+
const err = (0, _shared_1.firstError)(all);
|
|
543
|
+
if (err) {
|
|
544
|
+
return err;
|
|
545
|
+
}
|
|
546
|
+
const nums = all.map(n => n.value);
|
|
547
|
+
if (nums.length === 0) {
|
|
548
|
+
return values_1.ERRORS.NA;
|
|
549
|
+
}
|
|
550
|
+
const counts = new Map();
|
|
551
|
+
let maxCount = 0;
|
|
552
|
+
let mode = nums[0];
|
|
553
|
+
for (const n of nums) {
|
|
554
|
+
const c = (counts.get(n) ?? 0) + 1;
|
|
555
|
+
counts.set(n, c);
|
|
556
|
+
if (c > maxCount) {
|
|
557
|
+
maxCount = c;
|
|
558
|
+
mode = n;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
return maxCount > 1 ? (0, values_1.rvNumber)(mode) : values_1.ERRORS.NA;
|
|
562
|
+
};
|
|
563
|
+
exports.fnMODE = fnMODE;
|
|
564
|
+
/**
|
|
565
|
+
* MODE.MULT — returns a vertical array of every mode (dynamic array).
|
|
566
|
+
* When the dataset is multimodal Excel spills all of them; for a single
|
|
567
|
+
* mode it behaves like MODE.SNGL.
|
|
568
|
+
*/
|
|
569
|
+
const fnMODE_MULT = args => {
|
|
570
|
+
const all = (0, _shared_1.flattenNumbers)(args);
|
|
571
|
+
const err = (0, _shared_1.firstError)(all);
|
|
572
|
+
if (err) {
|
|
573
|
+
return err;
|
|
574
|
+
}
|
|
575
|
+
const nums = all.map(n => n.value);
|
|
576
|
+
if (nums.length === 0) {
|
|
577
|
+
return values_1.ERRORS.NA;
|
|
578
|
+
}
|
|
579
|
+
const counts = new Map();
|
|
580
|
+
for (const n of nums) {
|
|
581
|
+
counts.set(n, (counts.get(n) ?? 0) + 1);
|
|
582
|
+
}
|
|
583
|
+
let maxCount = 0;
|
|
584
|
+
for (const c of counts.values()) {
|
|
585
|
+
if (c > maxCount) {
|
|
586
|
+
maxCount = c;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (maxCount < 2) {
|
|
590
|
+
return values_1.ERRORS.NA;
|
|
591
|
+
}
|
|
592
|
+
// Preserve first-occurrence order as Excel does (not sorted).
|
|
593
|
+
const seen = new Set();
|
|
594
|
+
const modes = [];
|
|
595
|
+
for (const n of nums) {
|
|
596
|
+
if (!seen.has(n) && counts.get(n) === maxCount) {
|
|
597
|
+
seen.add(n);
|
|
598
|
+
modes.push(n);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return (0, values_1.rvArray)(modes.map(m => [(0, values_1.rvNumber)(m)]));
|
|
602
|
+
};
|
|
603
|
+
exports.fnMODE_MULT = fnMODE_MULT;
|
|
604
|
+
// ============================================================================
|
|
605
|
+
// Paired-array functions: CORREL, SLOPE, INTERCEPT, RSQ, FORECAST
|
|
606
|
+
// ============================================================================
|
|
607
|
+
/**
|
|
608
|
+
* Extract matching pairs of numbers from two array arguments, filtering to
|
|
609
|
+
* numeric cells only (matching Excel's CORREL/SLOPE/INTERCEPT conventions).
|
|
610
|
+
* Returns the shorter prefix-length pair aligned by position.
|
|
611
|
+
*/
|
|
612
|
+
function pairedNumbers(args, aIdx, bIdx) {
|
|
613
|
+
const flatA = (0, _shared_1.flattenNumbers)([args[aIdx]]);
|
|
614
|
+
const errA = (0, _shared_1.firstError)(flatA);
|
|
615
|
+
if (errA) {
|
|
616
|
+
return errA;
|
|
617
|
+
}
|
|
618
|
+
const flatB = (0, _shared_1.flattenNumbers)([args[bIdx]]);
|
|
619
|
+
const errB = (0, _shared_1.firstError)(flatB);
|
|
620
|
+
if (errB) {
|
|
621
|
+
return errB;
|
|
622
|
+
}
|
|
623
|
+
const xs = flatA.filter((v) => v.kind === 1 /* RVKind.Number */).map(n => n.value);
|
|
624
|
+
const ys = flatB.filter((v) => v.kind === 1 /* RVKind.Number */).map(n => n.value);
|
|
625
|
+
const n = Math.min(xs.length, ys.length);
|
|
626
|
+
return { xs: xs.slice(0, n), ys: ys.slice(0, n) };
|
|
627
|
+
}
|
|
628
|
+
function pairedSums(xs, ys) {
|
|
629
|
+
const n = xs.length;
|
|
630
|
+
if (n === 0) {
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
let sumX = 0;
|
|
634
|
+
let sumY = 0;
|
|
635
|
+
for (let i = 0; i < n; i++) {
|
|
636
|
+
sumX += xs[i];
|
|
637
|
+
sumY += ys[i];
|
|
638
|
+
}
|
|
639
|
+
const meanX = sumX / n;
|
|
640
|
+
const meanY = sumY / n;
|
|
641
|
+
let sxy = 0;
|
|
642
|
+
let sxx = 0;
|
|
643
|
+
let syy = 0;
|
|
644
|
+
for (let i = 0; i < n; i++) {
|
|
645
|
+
const dx = xs[i] - meanX;
|
|
646
|
+
const dy = ys[i] - meanY;
|
|
647
|
+
sxy += dx * dy;
|
|
648
|
+
sxx += dx * dx;
|
|
649
|
+
syy += dy * dy;
|
|
650
|
+
}
|
|
651
|
+
return { n, meanX, meanY, sxy, sxx, syy };
|
|
652
|
+
}
|
|
653
|
+
const fnCORREL = args => {
|
|
654
|
+
const paired = pairedNumbers(args, 0, 1);
|
|
655
|
+
if ("code" in paired) {
|
|
656
|
+
return paired;
|
|
657
|
+
}
|
|
658
|
+
const { xs, ys } = paired;
|
|
659
|
+
const s = pairedSums(xs, ys);
|
|
660
|
+
if (!s || s.n < 2) {
|
|
661
|
+
return values_1.ERRORS.DIV0;
|
|
662
|
+
}
|
|
663
|
+
const denom = Math.sqrt(s.sxx * s.syy);
|
|
664
|
+
return denom === 0 ? values_1.ERRORS.DIV0 : (0, values_1.rvNumber)(s.sxy / denom);
|
|
665
|
+
};
|
|
666
|
+
exports.fnCORREL = fnCORREL;
|
|
667
|
+
const fnSLOPE = args => {
|
|
668
|
+
// SLOPE(known_y, known_x) — note the argument order (y first, x second).
|
|
669
|
+
const paired = pairedNumbers(args, 0, 1);
|
|
670
|
+
if ("code" in paired) {
|
|
671
|
+
return paired;
|
|
672
|
+
}
|
|
673
|
+
const { xs: ys, ys: xs } = paired;
|
|
674
|
+
const s = pairedSums(xs, ys);
|
|
675
|
+
if (!s || s.n < 2) {
|
|
676
|
+
return values_1.ERRORS.DIV0;
|
|
677
|
+
}
|
|
678
|
+
return s.sxx === 0 ? values_1.ERRORS.DIV0 : (0, values_1.rvNumber)(s.sxy / s.sxx);
|
|
679
|
+
};
|
|
680
|
+
exports.fnSLOPE = fnSLOPE;
|
|
681
|
+
const fnINTERCEPT = args => {
|
|
682
|
+
const paired = pairedNumbers(args, 0, 1);
|
|
683
|
+
if ("code" in paired) {
|
|
684
|
+
return paired;
|
|
685
|
+
}
|
|
686
|
+
const { xs: ys, ys: xs } = paired;
|
|
687
|
+
const s = pairedSums(xs, ys);
|
|
688
|
+
if (!s || s.n < 2) {
|
|
689
|
+
return values_1.ERRORS.DIV0;
|
|
690
|
+
}
|
|
691
|
+
if (s.sxx === 0) {
|
|
692
|
+
return values_1.ERRORS.DIV0;
|
|
693
|
+
}
|
|
694
|
+
const slope = s.sxy / s.sxx;
|
|
695
|
+
return (0, values_1.rvNumber)(s.meanY - slope * s.meanX);
|
|
696
|
+
};
|
|
697
|
+
exports.fnINTERCEPT = fnINTERCEPT;
|
|
698
|
+
const fnRSQ = args => {
|
|
699
|
+
const r = (0, exports.fnCORREL)(args);
|
|
700
|
+
if ((0, values_1.isError)(r)) {
|
|
701
|
+
return r;
|
|
702
|
+
}
|
|
703
|
+
return (0, values_1.rvNumber)(r.value ** 2);
|
|
704
|
+
};
|
|
705
|
+
exports.fnRSQ = fnRSQ;
|
|
706
|
+
/**
|
|
707
|
+
* STEYX(known_y, known_x) — standard error of the predicted y-value for
|
|
708
|
+
* each x in a regression. Matches Excel's definition:
|
|
709
|
+
* SE = sqrt((1/(n-2)) × (S_yy − S_xy² / S_xx))
|
|
710
|
+
* where S_xx, S_yy, S_xy are centred sums of squares / cross-product.
|
|
711
|
+
*/
|
|
712
|
+
const fnSTEYX = args => {
|
|
713
|
+
// STEYX(known_y, known_x) — y-first ordering, like SLOPE/INTERCEPT.
|
|
714
|
+
const paired = pairedNumbers(args, 0, 1);
|
|
715
|
+
if ("code" in paired) {
|
|
716
|
+
return paired;
|
|
717
|
+
}
|
|
718
|
+
const { xs: ys, ys: xs } = paired;
|
|
719
|
+
const s = pairedSums(xs, ys);
|
|
720
|
+
if (!s || s.n < 3) {
|
|
721
|
+
return values_1.ERRORS.DIV0;
|
|
722
|
+
}
|
|
723
|
+
if (s.sxx === 0) {
|
|
724
|
+
return values_1.ERRORS.DIV0;
|
|
725
|
+
}
|
|
726
|
+
const numer = s.syy - (s.sxy * s.sxy) / s.sxx;
|
|
727
|
+
if (numer < 0) {
|
|
728
|
+
// Floating-point noise when the regression is essentially perfect.
|
|
729
|
+
return (0, values_1.rvNumber)(0);
|
|
730
|
+
}
|
|
731
|
+
return (0, values_1.rvNumber)(Math.sqrt(numer / (s.n - 2)));
|
|
732
|
+
};
|
|
733
|
+
exports.fnSTEYX = fnSTEYX;
|
|
734
|
+
const fnFORECAST = args => {
|
|
735
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
736
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
737
|
+
return x;
|
|
738
|
+
}
|
|
739
|
+
// FORECAST(x, known_y, known_x) — same y-first argument order as SLOPE.
|
|
740
|
+
const paired = pairedNumbers(args, 1, 2);
|
|
741
|
+
if ("code" in paired) {
|
|
742
|
+
return paired;
|
|
743
|
+
}
|
|
744
|
+
const { xs: ys, ys: xs } = paired;
|
|
745
|
+
const s = pairedSums(xs, ys);
|
|
746
|
+
if (!s || s.n < 2) {
|
|
747
|
+
return values_1.ERRORS.DIV0;
|
|
748
|
+
}
|
|
749
|
+
if (s.sxx === 0) {
|
|
750
|
+
return values_1.ERRORS.DIV0;
|
|
751
|
+
}
|
|
752
|
+
const slope = s.sxy / s.sxx;
|
|
753
|
+
const intercept = s.meanY - slope * s.meanX;
|
|
754
|
+
return (0, values_1.rvNumber)(intercept + slope * x.value);
|
|
755
|
+
};
|
|
756
|
+
exports.fnFORECAST = fnFORECAST;
|
|
757
|
+
// ============================================================================
|
|
758
|
+
// FACT, FACTDOUBLE, COMBIN, COMBINA, PERMUT
|
|
759
|
+
// Re-exported from math.ts — canonical definitions live there.
|
|
760
|
+
// ============================================================================
|
|
761
|
+
var math_1 = require("./math");
|
|
762
|
+
Object.defineProperty(exports, "fnFACT", { enumerable: true, get: function () { return math_1.fnFACT; } });
|
|
763
|
+
Object.defineProperty(exports, "fnFACTDOUBLE", { enumerable: true, get: function () { return math_1.fnFACTDOUBLE; } });
|
|
764
|
+
Object.defineProperty(exports, "fnCOMBIN", { enumerable: true, get: function () { return math_1.fnCOMBIN; } });
|
|
765
|
+
Object.defineProperty(exports, "fnCOMBINA", { enumerable: true, get: function () { return math_1.fnCOMBINA; } });
|
|
766
|
+
Object.defineProperty(exports, "fnPERMUT", { enumerable: true, get: function () { return math_1.fnPERMUT; } });
|
|
767
|
+
// ============================================================================
|
|
768
|
+
// GEOMEAN, HARMEAN, TRIMMEAN, DEVSQ, AVEDEV
|
|
769
|
+
// ============================================================================
|
|
770
|
+
const fnGEOMEAN = args => {
|
|
771
|
+
const rawNums = (0, _shared_1.flattenNumbers)(args);
|
|
772
|
+
const err = (0, _shared_1.firstError)(rawNums);
|
|
773
|
+
if (err) {
|
|
774
|
+
return err;
|
|
775
|
+
}
|
|
776
|
+
const nums = rawNums;
|
|
777
|
+
if (nums.length === 0) {
|
|
778
|
+
return values_1.ERRORS.NUM;
|
|
779
|
+
}
|
|
780
|
+
let logSum = 0;
|
|
781
|
+
for (const n of nums) {
|
|
782
|
+
if (n.value <= 0) {
|
|
783
|
+
return values_1.ERRORS.NUM;
|
|
784
|
+
}
|
|
785
|
+
logSum += Math.log(n.value);
|
|
786
|
+
}
|
|
787
|
+
return (0, values_1.rvNumber)(Math.exp(logSum / nums.length));
|
|
788
|
+
};
|
|
789
|
+
exports.fnGEOMEAN = fnGEOMEAN;
|
|
790
|
+
const fnHARMEAN = args => {
|
|
791
|
+
const rawNums = (0, _shared_1.flattenNumbers)(args);
|
|
792
|
+
const err = (0, _shared_1.firstError)(rawNums);
|
|
793
|
+
if (err) {
|
|
794
|
+
return err;
|
|
795
|
+
}
|
|
796
|
+
const nums = rawNums;
|
|
797
|
+
if (nums.length === 0) {
|
|
798
|
+
return values_1.ERRORS.NUM;
|
|
799
|
+
}
|
|
800
|
+
let recipSum = 0;
|
|
801
|
+
for (const n of nums) {
|
|
802
|
+
if (n.value <= 0) {
|
|
803
|
+
return values_1.ERRORS.NUM;
|
|
804
|
+
}
|
|
805
|
+
recipSum += 1 / n.value;
|
|
806
|
+
}
|
|
807
|
+
return (0, values_1.rvNumber)(nums.length / recipSum);
|
|
808
|
+
};
|
|
809
|
+
exports.fnHARMEAN = fnHARMEAN;
|
|
810
|
+
const fnTRIMMEAN = args => {
|
|
811
|
+
const all = (0, _shared_1.flattenNumbers)([args[0]]);
|
|
812
|
+
const err = (0, _shared_1.firstError)(all);
|
|
813
|
+
if (err) {
|
|
814
|
+
return err;
|
|
815
|
+
}
|
|
816
|
+
const nums = all.map(n => n.value);
|
|
817
|
+
const pct = (0, _shared_1.argToNumber)(args[1]);
|
|
818
|
+
if (pct.kind === 4 /* RVKind.Error */) {
|
|
819
|
+
return pct;
|
|
820
|
+
}
|
|
821
|
+
if (pct.value < 0 || pct.value >= 1) {
|
|
822
|
+
return values_1.ERRORS.NUM;
|
|
823
|
+
}
|
|
824
|
+
nums.sort((a, b) => a - b);
|
|
825
|
+
const trimCount = Math.floor((nums.length * pct.value) / 2);
|
|
826
|
+
const trimmed = nums.slice(trimCount, nums.length - trimCount);
|
|
827
|
+
if (trimmed.length === 0) {
|
|
828
|
+
return values_1.ERRORS.DIV0;
|
|
829
|
+
}
|
|
830
|
+
return (0, values_1.rvNumber)(trimmed.reduce((a, b) => a + b, 0) / trimmed.length);
|
|
831
|
+
};
|
|
832
|
+
exports.fnTRIMMEAN = fnTRIMMEAN;
|
|
833
|
+
const fnDEVSQ = args => {
|
|
834
|
+
const rawNums = (0, _shared_1.flattenNumbers)(args);
|
|
835
|
+
const err = (0, _shared_1.firstError)(rawNums);
|
|
836
|
+
if (err) {
|
|
837
|
+
return err;
|
|
838
|
+
}
|
|
839
|
+
const nums = rawNums;
|
|
840
|
+
if (nums.length === 0) {
|
|
841
|
+
return (0, values_1.rvNumber)(0);
|
|
842
|
+
}
|
|
843
|
+
let sum = 0;
|
|
844
|
+
for (const n of nums) {
|
|
845
|
+
sum += n.value;
|
|
846
|
+
}
|
|
847
|
+
const mean = sum / nums.length;
|
|
848
|
+
let result = 0;
|
|
849
|
+
for (const n of nums) {
|
|
850
|
+
result += (n.value - mean) ** 2;
|
|
851
|
+
}
|
|
852
|
+
return (0, values_1.rvNumber)(result);
|
|
853
|
+
};
|
|
854
|
+
exports.fnDEVSQ = fnDEVSQ;
|
|
855
|
+
const fnAVEDEV = args => {
|
|
856
|
+
const rawNums = (0, _shared_1.flattenNumbers)(args);
|
|
857
|
+
const err = (0, _shared_1.firstError)(rawNums);
|
|
858
|
+
if (err) {
|
|
859
|
+
return err;
|
|
860
|
+
}
|
|
861
|
+
const nums = rawNums;
|
|
862
|
+
if (nums.length === 0) {
|
|
863
|
+
return values_1.ERRORS.NUM;
|
|
864
|
+
}
|
|
865
|
+
let sum = 0;
|
|
866
|
+
for (const n of nums) {
|
|
867
|
+
sum += n.value;
|
|
868
|
+
}
|
|
869
|
+
const mean = sum / nums.length;
|
|
870
|
+
let result = 0;
|
|
871
|
+
for (const n of nums) {
|
|
872
|
+
result += Math.abs(n.value - mean);
|
|
873
|
+
}
|
|
874
|
+
return (0, values_1.rvNumber)(result / nums.length);
|
|
875
|
+
};
|
|
876
|
+
exports.fnAVEDEV = fnAVEDEV;
|
|
877
|
+
// ============================================================================
|
|
878
|
+
// CONFIDENCE, FISHER, AVERAGEA, MAXA, MINA
|
|
879
|
+
// ============================================================================
|
|
880
|
+
const fnCONFIDENCENORM = args => {
|
|
881
|
+
const alpha = (0, _shared_1.argToNumber)(args[0]);
|
|
882
|
+
if (alpha.kind === 4 /* RVKind.Error */) {
|
|
883
|
+
return alpha;
|
|
884
|
+
}
|
|
885
|
+
const stddev = (0, _shared_1.argToNumber)(args[1]);
|
|
886
|
+
if (stddev.kind === 4 /* RVKind.Error */) {
|
|
887
|
+
return stddev;
|
|
888
|
+
}
|
|
889
|
+
const size = (0, _shared_1.argToNumber)(args[2]);
|
|
890
|
+
if (size.kind === 4 /* RVKind.Error */) {
|
|
891
|
+
return size;
|
|
892
|
+
}
|
|
893
|
+
if (alpha.value <= 0 || alpha.value >= 1 || stddev.value <= 0 || size.value < 1) {
|
|
894
|
+
return values_1.ERRORS.NUM;
|
|
895
|
+
}
|
|
896
|
+
return (0, values_1.rvNumber)((normSInv(1 - alpha.value / 2) * stddev.value) / Math.sqrt(size.value));
|
|
897
|
+
};
|
|
898
|
+
exports.fnCONFIDENCENORM = fnCONFIDENCENORM;
|
|
899
|
+
/**
|
|
900
|
+
* CONFIDENCE.T — confidence interval half-width for the mean using the
|
|
901
|
+
* Student's t distribution (small sample / unknown population variance).
|
|
902
|
+
*/
|
|
903
|
+
const fnCONFIDENCE_T = args => {
|
|
904
|
+
const alpha = (0, _shared_1.argToNumber)(args[0]);
|
|
905
|
+
if (alpha.kind === 4 /* RVKind.Error */) {
|
|
906
|
+
return alpha;
|
|
907
|
+
}
|
|
908
|
+
const stddev = (0, _shared_1.argToNumber)(args[1]);
|
|
909
|
+
if (stddev.kind === 4 /* RVKind.Error */) {
|
|
910
|
+
return stddev;
|
|
911
|
+
}
|
|
912
|
+
const size = (0, _shared_1.argToNumber)(args[2]);
|
|
913
|
+
if (size.kind === 4 /* RVKind.Error */) {
|
|
914
|
+
return size;
|
|
915
|
+
}
|
|
916
|
+
if (alpha.value <= 0 || alpha.value >= 1 || stddev.value <= 0 || size.value < 2) {
|
|
917
|
+
return values_1.ERRORS.NUM;
|
|
918
|
+
}
|
|
919
|
+
// Reuse the engine's existing T.INV.2T to pull the two-tailed critical
|
|
920
|
+
// value; avoids duplicating the Newton search. Excel uses df = n − 1.
|
|
921
|
+
const tCrit = (0, exports.fnT_INV_2T)([(0, values_1.rvNumber)(alpha.value), (0, values_1.rvNumber)(size.value - 1)]);
|
|
922
|
+
if ((0, values_1.isError)(tCrit)) {
|
|
923
|
+
return tCrit;
|
|
924
|
+
}
|
|
925
|
+
const t = tCrit.value;
|
|
926
|
+
return (0, values_1.rvNumber)((t * stddev.value) / Math.sqrt(size.value));
|
|
927
|
+
};
|
|
928
|
+
exports.fnCONFIDENCE_T = fnCONFIDENCE_T;
|
|
929
|
+
/**
|
|
930
|
+
* Shared helper: walk two numeric arrays element-wise, filtering to
|
|
931
|
+
* matching-position numeric pairs only (Excel skips rows where either
|
|
932
|
+
* side is non-numeric).
|
|
933
|
+
*/
|
|
934
|
+
function pairedNumericValues(a, b) {
|
|
935
|
+
const xsAll = (0, _shared_1.flattenNumbers)([a]);
|
|
936
|
+
const xsErr = (0, _shared_1.firstError)(xsAll);
|
|
937
|
+
if (xsErr) {
|
|
938
|
+
return xsErr;
|
|
939
|
+
}
|
|
940
|
+
const ysAll = (0, _shared_1.flattenNumbers)([b]);
|
|
941
|
+
const ysErr = (0, _shared_1.firstError)(ysAll);
|
|
942
|
+
if (ysErr) {
|
|
943
|
+
return ysErr;
|
|
944
|
+
}
|
|
945
|
+
const n = Math.min(xsAll.length, ysAll.length);
|
|
946
|
+
const xs = [];
|
|
947
|
+
const ys = [];
|
|
948
|
+
for (let i = 0; i < n; i++) {
|
|
949
|
+
const x = xsAll[i];
|
|
950
|
+
const y = ysAll[i];
|
|
951
|
+
if (x.kind === 1 /* RVKind.Number */ && y.kind === 1 /* RVKind.Number */) {
|
|
952
|
+
xs.push(x.value);
|
|
953
|
+
ys.push(y.value);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
return { xs, ys };
|
|
957
|
+
}
|
|
958
|
+
/** COVARIANCE.P — population covariance. */
|
|
959
|
+
const fnCOVARIANCE_P = args => {
|
|
960
|
+
const pairs = pairedNumericValues(args[0], args[1]);
|
|
961
|
+
if (pairs.kind === 4 /* RVKind.Error */) {
|
|
962
|
+
return pairs;
|
|
963
|
+
}
|
|
964
|
+
const { xs, ys } = pairs;
|
|
965
|
+
const n = xs.length;
|
|
966
|
+
if (n === 0) {
|
|
967
|
+
return values_1.ERRORS.DIV0;
|
|
968
|
+
}
|
|
969
|
+
let mx = 0;
|
|
970
|
+
let my = 0;
|
|
971
|
+
for (let i = 0; i < n; i++) {
|
|
972
|
+
mx += xs[i];
|
|
973
|
+
my += ys[i];
|
|
974
|
+
}
|
|
975
|
+
mx /= n;
|
|
976
|
+
my /= n;
|
|
977
|
+
let s = 0;
|
|
978
|
+
for (let i = 0; i < n; i++) {
|
|
979
|
+
s += (xs[i] - mx) * (ys[i] - my);
|
|
980
|
+
}
|
|
981
|
+
return (0, values_1.rvNumber)(s / n);
|
|
982
|
+
};
|
|
983
|
+
exports.fnCOVARIANCE_P = fnCOVARIANCE_P;
|
|
984
|
+
/** COVARIANCE.S — sample covariance (divide by n-1). */
|
|
985
|
+
const fnCOVARIANCE_S = args => {
|
|
986
|
+
const pairs = pairedNumericValues(args[0], args[1]);
|
|
987
|
+
if (pairs.kind === 4 /* RVKind.Error */) {
|
|
988
|
+
return pairs;
|
|
989
|
+
}
|
|
990
|
+
const { xs, ys } = pairs;
|
|
991
|
+
const n = xs.length;
|
|
992
|
+
if (n < 2) {
|
|
993
|
+
return values_1.ERRORS.DIV0;
|
|
994
|
+
}
|
|
995
|
+
let mx = 0;
|
|
996
|
+
let my = 0;
|
|
997
|
+
for (let i = 0; i < n; i++) {
|
|
998
|
+
mx += xs[i];
|
|
999
|
+
my += ys[i];
|
|
1000
|
+
}
|
|
1001
|
+
mx /= n;
|
|
1002
|
+
my /= n;
|
|
1003
|
+
let s = 0;
|
|
1004
|
+
for (let i = 0; i < n; i++) {
|
|
1005
|
+
s += (xs[i] - mx) * (ys[i] - my);
|
|
1006
|
+
}
|
|
1007
|
+
return (0, values_1.rvNumber)(s / (n - 1));
|
|
1008
|
+
};
|
|
1009
|
+
exports.fnCOVARIANCE_S = fnCOVARIANCE_S;
|
|
1010
|
+
/**
|
|
1011
|
+
* RANK.AVG — average-tie rank. Identical to RANK.EQ except that tied
|
|
1012
|
+
* positions return the average of the ranks they would otherwise span.
|
|
1013
|
+
*/
|
|
1014
|
+
const fnRANK_AVG = args => {
|
|
1015
|
+
const numberRV = (0, _shared_1.argToNumber)(args[0]);
|
|
1016
|
+
if (numberRV.kind === 4 /* RVKind.Error */) {
|
|
1017
|
+
return numberRV;
|
|
1018
|
+
}
|
|
1019
|
+
const rawArr = (0, _shared_1.flattenNumbers)([args[1]]);
|
|
1020
|
+
const arrErr = (0, _shared_1.firstError)(rawArr);
|
|
1021
|
+
if (arrErr) {
|
|
1022
|
+
return arrErr;
|
|
1023
|
+
}
|
|
1024
|
+
const nums = rawArr.map(n => n.value);
|
|
1025
|
+
const orderRV = args.length > 2 ? (0, _shared_1.argToNumber)(args[2]) : (0, values_1.rvNumber)(0);
|
|
1026
|
+
if (orderRV.kind === 4 /* RVKind.Error */) {
|
|
1027
|
+
return orderRV;
|
|
1028
|
+
}
|
|
1029
|
+
const ascending = orderRV.value !== 0;
|
|
1030
|
+
const sorted = nums.slice().sort((a, b) => (ascending ? a - b : b - a));
|
|
1031
|
+
// Find the range of indices that equal `number`; RANK.AVG returns the
|
|
1032
|
+
// average rank over that range.
|
|
1033
|
+
const target = numberRV.value;
|
|
1034
|
+
let first = -1;
|
|
1035
|
+
let last = -1;
|
|
1036
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
1037
|
+
if (sorted[i] === target) {
|
|
1038
|
+
if (first === -1) {
|
|
1039
|
+
first = i;
|
|
1040
|
+
}
|
|
1041
|
+
last = i;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
if (first === -1) {
|
|
1045
|
+
return values_1.ERRORS.NA;
|
|
1046
|
+
}
|
|
1047
|
+
// Ranks are 1-based; average of first+1 .. last+1.
|
|
1048
|
+
return (0, values_1.rvNumber)((first + last + 2) / 2);
|
|
1049
|
+
};
|
|
1050
|
+
exports.fnRANK_AVG = fnRANK_AVG;
|
|
1051
|
+
const fnFISHER = args => {
|
|
1052
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
1053
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
1054
|
+
return x;
|
|
1055
|
+
}
|
|
1056
|
+
if (x.value <= -1 || x.value >= 1) {
|
|
1057
|
+
return values_1.ERRORS.NUM;
|
|
1058
|
+
}
|
|
1059
|
+
return (0, values_1.rvNumber)(0.5 * Math.log((1 + x.value) / (1 - x.value)));
|
|
1060
|
+
};
|
|
1061
|
+
exports.fnFISHER = fnFISHER;
|
|
1062
|
+
const fnFISHERINV = args => {
|
|
1063
|
+
const y = (0, _shared_1.argToNumber)(args[0]);
|
|
1064
|
+
if (y.kind === 4 /* RVKind.Error */) {
|
|
1065
|
+
return y;
|
|
1066
|
+
}
|
|
1067
|
+
const e2y = Math.exp(2 * y.value);
|
|
1068
|
+
return (0, values_1.rvNumber)((e2y - 1) / (e2y + 1));
|
|
1069
|
+
};
|
|
1070
|
+
exports.fnFISHERINV = fnFISHERINV;
|
|
1071
|
+
const fnAVERAGEA = args => {
|
|
1072
|
+
const all = (0, _shared_1.flattenAll)(args);
|
|
1073
|
+
if (all.length === 0) {
|
|
1074
|
+
return values_1.ERRORS.DIV0;
|
|
1075
|
+
}
|
|
1076
|
+
let sum = 0;
|
|
1077
|
+
let count = 0;
|
|
1078
|
+
for (const v of all) {
|
|
1079
|
+
if (v.kind === 0 /* RVKind.Blank */) {
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
if (v.kind === 4 /* RVKind.Error */) {
|
|
1083
|
+
return v;
|
|
1084
|
+
}
|
|
1085
|
+
if (v.kind === 1 /* RVKind.Number */) {
|
|
1086
|
+
sum += v.value;
|
|
1087
|
+
}
|
|
1088
|
+
else if (v.kind === 3 /* RVKind.Boolean */) {
|
|
1089
|
+
sum += v.value ? 1 : 0;
|
|
1090
|
+
}
|
|
1091
|
+
// Text = 0 for AVERAGEA (no addition needed)
|
|
1092
|
+
count++;
|
|
1093
|
+
}
|
|
1094
|
+
return count === 0 ? values_1.ERRORS.DIV0 : (0, values_1.rvNumber)(sum / count);
|
|
1095
|
+
};
|
|
1096
|
+
exports.fnAVERAGEA = fnAVERAGEA;
|
|
1097
|
+
const fnMAXA = args => {
|
|
1098
|
+
const all = (0, _shared_1.flattenAll)(args);
|
|
1099
|
+
let max = -Infinity;
|
|
1100
|
+
let found = false;
|
|
1101
|
+
for (const v of all) {
|
|
1102
|
+
if (v.kind === 0 /* RVKind.Blank */) {
|
|
1103
|
+
continue;
|
|
1104
|
+
}
|
|
1105
|
+
if (v.kind === 4 /* RVKind.Error */) {
|
|
1106
|
+
return v;
|
|
1107
|
+
}
|
|
1108
|
+
let n;
|
|
1109
|
+
if (v.kind === 1 /* RVKind.Number */) {
|
|
1110
|
+
n = v.value;
|
|
1111
|
+
}
|
|
1112
|
+
else if (v.kind === 3 /* RVKind.Boolean */) {
|
|
1113
|
+
n = v.value ? 1 : 0;
|
|
1114
|
+
}
|
|
1115
|
+
else {
|
|
1116
|
+
n = 0;
|
|
1117
|
+
}
|
|
1118
|
+
if (n > max) {
|
|
1119
|
+
max = n;
|
|
1120
|
+
}
|
|
1121
|
+
found = true;
|
|
1122
|
+
}
|
|
1123
|
+
return (0, values_1.rvNumber)(found ? max : 0);
|
|
1124
|
+
};
|
|
1125
|
+
exports.fnMAXA = fnMAXA;
|
|
1126
|
+
const fnMINA = args => {
|
|
1127
|
+
const all = (0, _shared_1.flattenAll)(args);
|
|
1128
|
+
let min = Infinity;
|
|
1129
|
+
let found = false;
|
|
1130
|
+
for (const v of all) {
|
|
1131
|
+
if (v.kind === 0 /* RVKind.Blank */) {
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1134
|
+
if (v.kind === 4 /* RVKind.Error */) {
|
|
1135
|
+
return v;
|
|
1136
|
+
}
|
|
1137
|
+
let n;
|
|
1138
|
+
if (v.kind === 1 /* RVKind.Number */) {
|
|
1139
|
+
n = v.value;
|
|
1140
|
+
}
|
|
1141
|
+
else if (v.kind === 3 /* RVKind.Boolean */) {
|
|
1142
|
+
n = v.value ? 1 : 0;
|
|
1143
|
+
}
|
|
1144
|
+
else {
|
|
1145
|
+
n = 0;
|
|
1146
|
+
}
|
|
1147
|
+
if (n < min) {
|
|
1148
|
+
min = n;
|
|
1149
|
+
}
|
|
1150
|
+
found = true;
|
|
1151
|
+
}
|
|
1152
|
+
return (0, values_1.rvNumber)(found ? min : 0);
|
|
1153
|
+
};
|
|
1154
|
+
exports.fnMINA = fnMINA;
|
|
1155
|
+
// ============================================================================
|
|
1156
|
+
// Private helpers for distributions (pure number → number, unchanged)
|
|
1157
|
+
// ============================================================================
|
|
1158
|
+
function gammaFn(z) {
|
|
1159
|
+
if (z < 0.5) {
|
|
1160
|
+
return Math.PI / (Math.sin(Math.PI * z) * gammaFn(1 - z));
|
|
1161
|
+
}
|
|
1162
|
+
z -= 1;
|
|
1163
|
+
const g = 7;
|
|
1164
|
+
const c = [
|
|
1165
|
+
0.99999999999980993, 676.5203681218851, -1259.1392167224028, 771.32342877765313,
|
|
1166
|
+
-176.61502916214059, 12.507343278686905, -0.13857109526572012, 9.9843695780195716e-6,
|
|
1167
|
+
1.5056327351493116e-7
|
|
1168
|
+
];
|
|
1169
|
+
let x = c[0];
|
|
1170
|
+
for (let i = 1; i < g + 2; i++) {
|
|
1171
|
+
x += c[i] / (z + i);
|
|
1172
|
+
}
|
|
1173
|
+
const t = z + g + 0.5;
|
|
1174
|
+
return Math.sqrt(2 * Math.PI) * Math.pow(t, z + 0.5) * Math.exp(-t) * x;
|
|
1175
|
+
}
|
|
1176
|
+
function lnGamma(x) {
|
|
1177
|
+
return Math.log(gammaFn(x));
|
|
1178
|
+
}
|
|
1179
|
+
function betaIncomplete(x, a, b) {
|
|
1180
|
+
if (x <= 0) {
|
|
1181
|
+
return 0;
|
|
1182
|
+
}
|
|
1183
|
+
if (x >= 1) {
|
|
1184
|
+
return 1;
|
|
1185
|
+
}
|
|
1186
|
+
if (x > (a + 1) / (a + b + 2)) {
|
|
1187
|
+
return 1 - betaIncomplete(1 - x, b, a);
|
|
1188
|
+
}
|
|
1189
|
+
const lbeta = lnGamma(a) + lnGamma(b) - lnGamma(a + b);
|
|
1190
|
+
const front = Math.exp(Math.log(x) * a + Math.log(1 - x) * b - lbeta) / a;
|
|
1191
|
+
// For extreme (a, b) the front factor underflows to 0 or overflows to
|
|
1192
|
+
// Infinity (e.g. T.DIST.2T at df >= 300 computes betaIncomplete with
|
|
1193
|
+
// a around 150 and x near 0.004; Math.log(x)*a dips below -650, so
|
|
1194
|
+
// Math.exp returns 0 and subsequent `f *= c * d` multiplications can
|
|
1195
|
+
// produce 0 * Infinity = NaN in the continued-fraction loop). Short-
|
|
1196
|
+
// circuit those pathological inputs: a zero front dominates the
|
|
1197
|
+
// series, so the integrated value is effectively 0.
|
|
1198
|
+
if (!Number.isFinite(front) || front === 0) {
|
|
1199
|
+
return 0;
|
|
1200
|
+
}
|
|
1201
|
+
let f = 1;
|
|
1202
|
+
let c = 1;
|
|
1203
|
+
let d = 1 - ((a + b) * x) / (a + 1);
|
|
1204
|
+
if (Math.abs(d) < 1e-30) {
|
|
1205
|
+
d = 1e-30;
|
|
1206
|
+
}
|
|
1207
|
+
d = 1 / d;
|
|
1208
|
+
f = d;
|
|
1209
|
+
for (let m = 1; m <= 200; m++) {
|
|
1210
|
+
let num = (m * (b - m) * x) / ((a + 2 * m - 1) * (a + 2 * m));
|
|
1211
|
+
d = 1 + num * d;
|
|
1212
|
+
if (Math.abs(d) < 1e-30) {
|
|
1213
|
+
d = 1e-30;
|
|
1214
|
+
}
|
|
1215
|
+
c = 1 + num / c;
|
|
1216
|
+
if (Math.abs(c) < 1e-30) {
|
|
1217
|
+
c = 1e-30;
|
|
1218
|
+
}
|
|
1219
|
+
d = 1 / d;
|
|
1220
|
+
f *= c * d;
|
|
1221
|
+
num = -((a + m) * (a + b + m) * x) / ((a + 2 * m) * (a + 2 * m + 1));
|
|
1222
|
+
d = 1 + num * d;
|
|
1223
|
+
if (Math.abs(d) < 1e-30) {
|
|
1224
|
+
d = 1e-30;
|
|
1225
|
+
}
|
|
1226
|
+
c = 1 + num / c;
|
|
1227
|
+
if (Math.abs(c) < 1e-30) {
|
|
1228
|
+
c = 1e-30;
|
|
1229
|
+
}
|
|
1230
|
+
d = 1 / d;
|
|
1231
|
+
const delta = c * d;
|
|
1232
|
+
f *= delta;
|
|
1233
|
+
if (Math.abs(delta - 1) < 1e-10) {
|
|
1234
|
+
break;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
return front * f;
|
|
1238
|
+
}
|
|
1239
|
+
function gammaIncomplete(a, x) {
|
|
1240
|
+
if (x < 0) {
|
|
1241
|
+
return 0;
|
|
1242
|
+
}
|
|
1243
|
+
if (x === 0) {
|
|
1244
|
+
return 0;
|
|
1245
|
+
}
|
|
1246
|
+
if (x < a + 1) {
|
|
1247
|
+
let sum = 1 / a;
|
|
1248
|
+
let term = 1 / a;
|
|
1249
|
+
for (let n = 1; n <= 200; n++) {
|
|
1250
|
+
term *= x / (a + n);
|
|
1251
|
+
sum += term;
|
|
1252
|
+
if (Math.abs(term) < Math.abs(sum) * 1e-14) {
|
|
1253
|
+
break;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
return sum * Math.exp(-x + a * Math.log(x) - lnGamma(a));
|
|
1257
|
+
}
|
|
1258
|
+
let f = 1;
|
|
1259
|
+
const b0 = x + 1 - a;
|
|
1260
|
+
let ci = 1e30;
|
|
1261
|
+
let d = 1 / b0;
|
|
1262
|
+
f = d;
|
|
1263
|
+
for (let i = 1; i <= 200; i++) {
|
|
1264
|
+
const an = -i * (i - a);
|
|
1265
|
+
const bn = x + 2 * i + 1 - a;
|
|
1266
|
+
d = bn + an * d;
|
|
1267
|
+
if (Math.abs(d) < 1e-30) {
|
|
1268
|
+
d = 1e-30;
|
|
1269
|
+
}
|
|
1270
|
+
ci = bn + an / ci;
|
|
1271
|
+
if (Math.abs(ci) < 1e-30) {
|
|
1272
|
+
ci = 1e-30;
|
|
1273
|
+
}
|
|
1274
|
+
d = 1 / d;
|
|
1275
|
+
const delta = d * ci;
|
|
1276
|
+
f *= delta;
|
|
1277
|
+
if (Math.abs(delta - 1) < 1e-10) {
|
|
1278
|
+
break;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
return 1 - f * Math.exp(-x + a * Math.log(x) - lnGamma(a));
|
|
1282
|
+
}
|
|
1283
|
+
// ============================================================================
|
|
1284
|
+
// More Statistical Distribution Functions
|
|
1285
|
+
// ============================================================================
|
|
1286
|
+
const fnPOISSON_DIST = args => {
|
|
1287
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
1288
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
1289
|
+
return x;
|
|
1290
|
+
}
|
|
1291
|
+
const mean = (0, _shared_1.argToNumber)(args[1]);
|
|
1292
|
+
if (mean.kind === 4 /* RVKind.Error */) {
|
|
1293
|
+
return mean;
|
|
1294
|
+
}
|
|
1295
|
+
const cum = boolArg(args, 2);
|
|
1296
|
+
if (!cum.ok) {
|
|
1297
|
+
return cum.error;
|
|
1298
|
+
}
|
|
1299
|
+
const k = Math.floor(x.value);
|
|
1300
|
+
if (k < 0 || mean.value < 0) {
|
|
1301
|
+
return values_1.ERRORS.NUM;
|
|
1302
|
+
}
|
|
1303
|
+
// Degenerate mean = 0: the Poisson distribution concentrates all mass at
|
|
1304
|
+
// k = 0. The textbook PMF formula evaluates `k * log(0) = -Infinity`
|
|
1305
|
+
// multiplied by `k = 0`, yielding `NaN`, so we short-circuit.
|
|
1306
|
+
if (mean.value === 0) {
|
|
1307
|
+
if (!cum.value) {
|
|
1308
|
+
return (0, values_1.rvNumber)(k === 0 ? 1 : 0);
|
|
1309
|
+
}
|
|
1310
|
+
return (0, values_1.rvNumber)(1); // CDF is 1 at every k >= 0
|
|
1311
|
+
}
|
|
1312
|
+
if (!cum.value) {
|
|
1313
|
+
return (0, values_1.rvNumber)(Math.exp(-mean.value + k * Math.log(mean.value) - lnGamma(k + 1)));
|
|
1314
|
+
}
|
|
1315
|
+
return (0, values_1.rvNumber)(1 - gammaIncomplete(k + 1, mean.value));
|
|
1316
|
+
};
|
|
1317
|
+
exports.fnPOISSON_DIST = fnPOISSON_DIST;
|
|
1318
|
+
const fnBINOM_DIST = args => {
|
|
1319
|
+
const numS = (0, _shared_1.argToNumber)(args[0]);
|
|
1320
|
+
if (numS.kind === 4 /* RVKind.Error */) {
|
|
1321
|
+
return numS;
|
|
1322
|
+
}
|
|
1323
|
+
const trials = (0, _shared_1.argToNumber)(args[1]);
|
|
1324
|
+
if (trials.kind === 4 /* RVKind.Error */) {
|
|
1325
|
+
return trials;
|
|
1326
|
+
}
|
|
1327
|
+
const probS = (0, _shared_1.argToNumber)(args[2]);
|
|
1328
|
+
if (probS.kind === 4 /* RVKind.Error */) {
|
|
1329
|
+
return probS;
|
|
1330
|
+
}
|
|
1331
|
+
const cum = boolArg(args, 3);
|
|
1332
|
+
if (!cum.ok) {
|
|
1333
|
+
return cum.error;
|
|
1334
|
+
}
|
|
1335
|
+
const k = Math.floor(numS.value);
|
|
1336
|
+
const n = Math.floor(trials.value);
|
|
1337
|
+
if (k < 0 || n < 0 || k > n || probS.value < 0 || probS.value > 1) {
|
|
1338
|
+
return values_1.ERRORS.NUM;
|
|
1339
|
+
}
|
|
1340
|
+
const pmf = (ki) => {
|
|
1341
|
+
// Degenerate p = 0 or p = 1 edges: the textbook formula multiplies
|
|
1342
|
+
// the log of 0 by 0 (for the absent term), producing NaN. Handle
|
|
1343
|
+
// these analytically — all mass is at k = 0 when p = 0, or at k = n
|
|
1344
|
+
// when p = 1.
|
|
1345
|
+
if (probS.value === 0) {
|
|
1346
|
+
return ki === 0 ? 1 : 0;
|
|
1347
|
+
}
|
|
1348
|
+
if (probS.value === 1) {
|
|
1349
|
+
return ki === n ? 1 : 0;
|
|
1350
|
+
}
|
|
1351
|
+
const lnC = lnGamma(n + 1) - lnGamma(ki + 1) - lnGamma(n - ki + 1);
|
|
1352
|
+
return Math.exp(lnC + ki * Math.log(probS.value) + (n - ki) * Math.log(1 - probS.value));
|
|
1353
|
+
};
|
|
1354
|
+
if (!cum.value) {
|
|
1355
|
+
return (0, values_1.rvNumber)(pmf(k));
|
|
1356
|
+
}
|
|
1357
|
+
let sum = 0;
|
|
1358
|
+
for (let i = 0; i <= k; i++) {
|
|
1359
|
+
sum += pmf(i);
|
|
1360
|
+
}
|
|
1361
|
+
return (0, values_1.rvNumber)(sum);
|
|
1362
|
+
};
|
|
1363
|
+
exports.fnBINOM_DIST = fnBINOM_DIST;
|
|
1364
|
+
/**
|
|
1365
|
+
* BINOM.DIST.RANGE(trials, probability, number_s, [number_s2]) —
|
|
1366
|
+
* probability of a binomial trial outcome between `number_s` and
|
|
1367
|
+
* `number_s2` (inclusive). When `number_s2` is omitted, returns the
|
|
1368
|
+
* probability of exactly `number_s` successes.
|
|
1369
|
+
*/
|
|
1370
|
+
const fnBINOM_DIST_RANGE = args => {
|
|
1371
|
+
const trialsV = (0, _shared_1.argToNumber)(args[0]);
|
|
1372
|
+
if (trialsV.kind === 4 /* RVKind.Error */) {
|
|
1373
|
+
return trialsV;
|
|
1374
|
+
}
|
|
1375
|
+
const probV = (0, _shared_1.argToNumber)(args[1]);
|
|
1376
|
+
if (probV.kind === 4 /* RVKind.Error */) {
|
|
1377
|
+
return probV;
|
|
1378
|
+
}
|
|
1379
|
+
const s1V = (0, _shared_1.argToNumber)(args[2]);
|
|
1380
|
+
if (s1V.kind === 4 /* RVKind.Error */) {
|
|
1381
|
+
return s1V;
|
|
1382
|
+
}
|
|
1383
|
+
const s2V = args.length > 3 ? (0, _shared_1.argToNumber)(args[3]) : s1V;
|
|
1384
|
+
if (s2V.kind === 4 /* RVKind.Error */) {
|
|
1385
|
+
return s2V;
|
|
1386
|
+
}
|
|
1387
|
+
const n = Math.floor(trialsV.value);
|
|
1388
|
+
const s1 = Math.floor(s1V.value);
|
|
1389
|
+
const s2 = Math.floor(s2V.value);
|
|
1390
|
+
const p = probV.value;
|
|
1391
|
+
if (n < 0 || p < 0 || p > 1) {
|
|
1392
|
+
return values_1.ERRORS.NUM;
|
|
1393
|
+
}
|
|
1394
|
+
if (s1 < 0 || s1 > n) {
|
|
1395
|
+
return values_1.ERRORS.NUM;
|
|
1396
|
+
}
|
|
1397
|
+
if (s2 < s1 || s2 > n) {
|
|
1398
|
+
return values_1.ERRORS.NUM;
|
|
1399
|
+
}
|
|
1400
|
+
const pmf = (ki) => {
|
|
1401
|
+
if (p === 0) {
|
|
1402
|
+
return ki === 0 ? 1 : 0;
|
|
1403
|
+
}
|
|
1404
|
+
if (p === 1) {
|
|
1405
|
+
return ki === n ? 1 : 0;
|
|
1406
|
+
}
|
|
1407
|
+
const lnC = lnGamma(n + 1) - lnGamma(ki + 1) - lnGamma(n - ki + 1);
|
|
1408
|
+
return Math.exp(lnC + ki * Math.log(p) + (n - ki) * Math.log(1 - p));
|
|
1409
|
+
};
|
|
1410
|
+
let sum = 0;
|
|
1411
|
+
for (let i = s1; i <= s2; i++) {
|
|
1412
|
+
sum += pmf(i);
|
|
1413
|
+
}
|
|
1414
|
+
return (0, values_1.rvNumber)(sum);
|
|
1415
|
+
};
|
|
1416
|
+
exports.fnBINOM_DIST_RANGE = fnBINOM_DIST_RANGE;
|
|
1417
|
+
const fnBINOM_INV = args => {
|
|
1418
|
+
const trials = (0, _shared_1.argToNumber)(args[0]);
|
|
1419
|
+
if (trials.kind === 4 /* RVKind.Error */) {
|
|
1420
|
+
return trials;
|
|
1421
|
+
}
|
|
1422
|
+
const probS = (0, _shared_1.argToNumber)(args[1]);
|
|
1423
|
+
if (probS.kind === 4 /* RVKind.Error */) {
|
|
1424
|
+
return probS;
|
|
1425
|
+
}
|
|
1426
|
+
const alpha = (0, _shared_1.argToNumber)(args[2]);
|
|
1427
|
+
if (alpha.kind === 4 /* RVKind.Error */) {
|
|
1428
|
+
return alpha;
|
|
1429
|
+
}
|
|
1430
|
+
const n = Math.floor(trials.value);
|
|
1431
|
+
if (n < 0 || probS.value < 0 || probS.value > 1 || alpha.value < 0 || alpha.value > 1) {
|
|
1432
|
+
return values_1.ERRORS.NUM;
|
|
1433
|
+
}
|
|
1434
|
+
let cdf = 0;
|
|
1435
|
+
for (let k = 0; k <= n; k++) {
|
|
1436
|
+
const lnC = lnGamma(n + 1) - lnGamma(k + 1) - lnGamma(n - k + 1);
|
|
1437
|
+
cdf += Math.exp(lnC + k * Math.log(probS.value) + (n - k) * Math.log(1 - probS.value));
|
|
1438
|
+
if (cdf >= alpha.value) {
|
|
1439
|
+
return (0, values_1.rvNumber)(k);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return (0, values_1.rvNumber)(n);
|
|
1443
|
+
};
|
|
1444
|
+
exports.fnBINOM_INV = fnBINOM_INV;
|
|
1445
|
+
const fnHYPGEOM_DIST = args => {
|
|
1446
|
+
const sampleS = (0, _shared_1.argToNumber)(args[0]);
|
|
1447
|
+
if (sampleS.kind === 4 /* RVKind.Error */) {
|
|
1448
|
+
return sampleS;
|
|
1449
|
+
}
|
|
1450
|
+
const numberSample = (0, _shared_1.argToNumber)(args[1]);
|
|
1451
|
+
if (numberSample.kind === 4 /* RVKind.Error */) {
|
|
1452
|
+
return numberSample;
|
|
1453
|
+
}
|
|
1454
|
+
const popS = (0, _shared_1.argToNumber)(args[2]);
|
|
1455
|
+
if (popS.kind === 4 /* RVKind.Error */) {
|
|
1456
|
+
return popS;
|
|
1457
|
+
}
|
|
1458
|
+
const numberPop = (0, _shared_1.argToNumber)(args[3]);
|
|
1459
|
+
if (numberPop.kind === 4 /* RVKind.Error */) {
|
|
1460
|
+
return numberPop;
|
|
1461
|
+
}
|
|
1462
|
+
const cum = boolArg(args, 4);
|
|
1463
|
+
if (!cum.ok) {
|
|
1464
|
+
return cum.error;
|
|
1465
|
+
}
|
|
1466
|
+
const ss = Math.floor(sampleS.value);
|
|
1467
|
+
const ns = Math.floor(numberSample.value);
|
|
1468
|
+
const ps = Math.floor(popS.value);
|
|
1469
|
+
const np = Math.floor(numberPop.value);
|
|
1470
|
+
const pmf = (k) => Math.exp(lnGamma(ps + 1) -
|
|
1471
|
+
lnGamma(k + 1) -
|
|
1472
|
+
lnGamma(ps - k + 1) +
|
|
1473
|
+
lnGamma(np - ps + 1) -
|
|
1474
|
+
lnGamma(ns - k + 1) -
|
|
1475
|
+
lnGamma(np - ps - ns + k + 1) -
|
|
1476
|
+
lnGamma(np + 1) +
|
|
1477
|
+
lnGamma(ns + 1) +
|
|
1478
|
+
lnGamma(np - ns + 1));
|
|
1479
|
+
if (!cum.value) {
|
|
1480
|
+
return (0, values_1.rvNumber)(pmf(ss));
|
|
1481
|
+
}
|
|
1482
|
+
let sum = 0;
|
|
1483
|
+
for (let k = 0; k <= ss; k++) {
|
|
1484
|
+
sum += pmf(k);
|
|
1485
|
+
}
|
|
1486
|
+
return (0, values_1.rvNumber)(sum);
|
|
1487
|
+
};
|
|
1488
|
+
exports.fnHYPGEOM_DIST = fnHYPGEOM_DIST;
|
|
1489
|
+
const fnNEGBINOM_DIST = args => {
|
|
1490
|
+
const numF = (0, _shared_1.argToNumber)(args[0]);
|
|
1491
|
+
if (numF.kind === 4 /* RVKind.Error */) {
|
|
1492
|
+
return numF;
|
|
1493
|
+
}
|
|
1494
|
+
const numS = (0, _shared_1.argToNumber)(args[1]);
|
|
1495
|
+
if (numS.kind === 4 /* RVKind.Error */) {
|
|
1496
|
+
return numS;
|
|
1497
|
+
}
|
|
1498
|
+
const probS = (0, _shared_1.argToNumber)(args[2]);
|
|
1499
|
+
if (probS.kind === 4 /* RVKind.Error */) {
|
|
1500
|
+
return probS;
|
|
1501
|
+
}
|
|
1502
|
+
const cum = boolArg(args, 3);
|
|
1503
|
+
if (!cum.ok) {
|
|
1504
|
+
return cum.error;
|
|
1505
|
+
}
|
|
1506
|
+
const f = Math.floor(numF.value);
|
|
1507
|
+
const s = Math.floor(numS.value);
|
|
1508
|
+
if (f < 0 || s < 1 || probS.value < 0 || probS.value > 1) {
|
|
1509
|
+
return values_1.ERRORS.NUM;
|
|
1510
|
+
}
|
|
1511
|
+
const pmf = (k) => {
|
|
1512
|
+
const lnC = lnGamma(k + s) - lnGamma(s) - lnGamma(k + 1);
|
|
1513
|
+
return Math.exp(lnC + s * Math.log(probS.value) + k * Math.log(1 - probS.value));
|
|
1514
|
+
};
|
|
1515
|
+
if (!cum.value) {
|
|
1516
|
+
return (0, values_1.rvNumber)(pmf(f));
|
|
1517
|
+
}
|
|
1518
|
+
let sum = 0;
|
|
1519
|
+
for (let k = 0; k <= f; k++) {
|
|
1520
|
+
sum += pmf(k);
|
|
1521
|
+
}
|
|
1522
|
+
return (0, values_1.rvNumber)(sum);
|
|
1523
|
+
};
|
|
1524
|
+
exports.fnNEGBINOM_DIST = fnNEGBINOM_DIST;
|
|
1525
|
+
const fnCHISQ_DIST = args => {
|
|
1526
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
1527
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
1528
|
+
return x;
|
|
1529
|
+
}
|
|
1530
|
+
const df = (0, _shared_1.argToNumber)(args[1]);
|
|
1531
|
+
if (df.kind === 4 /* RVKind.Error */) {
|
|
1532
|
+
return df;
|
|
1533
|
+
}
|
|
1534
|
+
const cum = boolArg(args, 2);
|
|
1535
|
+
if (!cum.ok) {
|
|
1536
|
+
return cum.error;
|
|
1537
|
+
}
|
|
1538
|
+
if (x.value < 0 || df.value < 1) {
|
|
1539
|
+
return values_1.ERRORS.NUM;
|
|
1540
|
+
}
|
|
1541
|
+
// Excel's CHISQ.DIST accepts non-integer degrees of freedom; the
|
|
1542
|
+
// incomplete-gamma formula is defined for any positive `df/2`. We used
|
|
1543
|
+
// to Math.floor() df, which silently returned the chi-square for the
|
|
1544
|
+
// nearest smaller integer df and produced visibly wrong densities for
|
|
1545
|
+
// fractional inputs.
|
|
1546
|
+
if (cum.value) {
|
|
1547
|
+
return (0, values_1.rvNumber)(gammaIncomplete(df.value / 2, x.value / 2));
|
|
1548
|
+
}
|
|
1549
|
+
const halfK = df.value / 2;
|
|
1550
|
+
return (0, values_1.rvNumber)(Math.exp((halfK - 1) * Math.log(x.value / 2) - x.value / 2 - lnGamma(halfK)) / 2);
|
|
1551
|
+
};
|
|
1552
|
+
exports.fnCHISQ_DIST = fnCHISQ_DIST;
|
|
1553
|
+
const fnCHISQ_INV = args => {
|
|
1554
|
+
const p = (0, _shared_1.argToNumber)(args[0]);
|
|
1555
|
+
if (p.kind === 4 /* RVKind.Error */) {
|
|
1556
|
+
return p;
|
|
1557
|
+
}
|
|
1558
|
+
const df = (0, _shared_1.argToNumber)(args[1]);
|
|
1559
|
+
if (df.kind === 4 /* RVKind.Error */) {
|
|
1560
|
+
return df;
|
|
1561
|
+
}
|
|
1562
|
+
if (p.value < 0 || p.value >= 1 || df.value < 1) {
|
|
1563
|
+
return values_1.ERRORS.NUM;
|
|
1564
|
+
}
|
|
1565
|
+
// P = 0 → x = 0 exactly. Without the short-circuit, Newton from x = df
|
|
1566
|
+
// would compute `(0 - 0) / pdf` = 0 on the first iteration (fine), but
|
|
1567
|
+
// rounding drift can push x negative, triggering the clamp to 0.001 and
|
|
1568
|
+
// returning a non-zero result.
|
|
1569
|
+
if (p.value === 0) {
|
|
1570
|
+
return (0, values_1.rvNumber)(0);
|
|
1571
|
+
}
|
|
1572
|
+
let x = df.value;
|
|
1573
|
+
for (let iter = 0; iter < 100; iter++) {
|
|
1574
|
+
const cdf = gammaIncomplete(df.value / 2, x / 2);
|
|
1575
|
+
const halfK = df.value / 2;
|
|
1576
|
+
const pdf = Math.exp((halfK - 1) * Math.log(x / 2) - x / 2 - lnGamma(halfK)) / 2;
|
|
1577
|
+
if (Math.abs(pdf) < 1e-15) {
|
|
1578
|
+
break;
|
|
1579
|
+
}
|
|
1580
|
+
const delta = (cdf - p.value) / pdf;
|
|
1581
|
+
x -= delta;
|
|
1582
|
+
if (x <= 0) {
|
|
1583
|
+
x = 0.001;
|
|
1584
|
+
}
|
|
1585
|
+
if (Math.abs(delta) < 1e-10) {
|
|
1586
|
+
break;
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
return (0, values_1.rvNumber)(x);
|
|
1590
|
+
};
|
|
1591
|
+
exports.fnCHISQ_INV = fnCHISQ_INV;
|
|
1592
|
+
const fnCHISQ_DIST_RT = args => {
|
|
1593
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
1594
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
1595
|
+
return x;
|
|
1596
|
+
}
|
|
1597
|
+
const df = (0, _shared_1.argToNumber)(args[1]);
|
|
1598
|
+
if (df.kind === 4 /* RVKind.Error */) {
|
|
1599
|
+
return df;
|
|
1600
|
+
}
|
|
1601
|
+
if (x.value < 0 || df.value < 1) {
|
|
1602
|
+
return values_1.ERRORS.NUM;
|
|
1603
|
+
}
|
|
1604
|
+
return (0, values_1.rvNumber)(1 - gammaIncomplete(df.value / 2, x.value / 2));
|
|
1605
|
+
};
|
|
1606
|
+
exports.fnCHISQ_DIST_RT = fnCHISQ_DIST_RT;
|
|
1607
|
+
/**
|
|
1608
|
+
* CHISQ.INV.RT(probability, df) — right-tailed inverse of chi-square.
|
|
1609
|
+
* Equivalent to CHISQ.INV(1 - probability, df). Probabilities of 0 or 1
|
|
1610
|
+
* return +∞ or 0 respectively; values outside (0, 1] are #NUM!.
|
|
1611
|
+
*/
|
|
1612
|
+
const fnCHISQ_INV_RT = args => {
|
|
1613
|
+
const p = (0, _shared_1.argToNumber)(args[0]);
|
|
1614
|
+
if (p.kind === 4 /* RVKind.Error */) {
|
|
1615
|
+
return p;
|
|
1616
|
+
}
|
|
1617
|
+
const df = (0, _shared_1.argToNumber)(args[1]);
|
|
1618
|
+
if (df.kind === 4 /* RVKind.Error */) {
|
|
1619
|
+
return df;
|
|
1620
|
+
}
|
|
1621
|
+
if (p.value <= 0 || p.value > 1 || df.value < 1) {
|
|
1622
|
+
return values_1.ERRORS.NUM;
|
|
1623
|
+
}
|
|
1624
|
+
// Re-use CHISQ.INV with the complementary probability.
|
|
1625
|
+
return (0, exports.fnCHISQ_INV)([(0, values_1.rvNumber)(1 - p.value), df]);
|
|
1626
|
+
};
|
|
1627
|
+
exports.fnCHISQ_INV_RT = fnCHISQ_INV_RT;
|
|
1628
|
+
const fnF_DIST = args => {
|
|
1629
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
1630
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
1631
|
+
return x;
|
|
1632
|
+
}
|
|
1633
|
+
const df1 = (0, _shared_1.argToNumber)(args[1]);
|
|
1634
|
+
if (df1.kind === 4 /* RVKind.Error */) {
|
|
1635
|
+
return df1;
|
|
1636
|
+
}
|
|
1637
|
+
const df2 = (0, _shared_1.argToNumber)(args[2]);
|
|
1638
|
+
if (df2.kind === 4 /* RVKind.Error */) {
|
|
1639
|
+
return df2;
|
|
1640
|
+
}
|
|
1641
|
+
const cum = boolArg(args, 3);
|
|
1642
|
+
if (!cum.ok) {
|
|
1643
|
+
return cum.error;
|
|
1644
|
+
}
|
|
1645
|
+
if (x.value < 0 || df1.value < 1 || df2.value < 1) {
|
|
1646
|
+
return values_1.ERRORS.NUM;
|
|
1647
|
+
}
|
|
1648
|
+
// Accept fractional df. See CHISQ.DIST comment — the betaIncomplete/lnGamma
|
|
1649
|
+
// formulas below are defined for any positive df, so there is no reason
|
|
1650
|
+
// to truncate.
|
|
1651
|
+
const d1 = df1.value;
|
|
1652
|
+
const d2 = df2.value;
|
|
1653
|
+
if (cum.value) {
|
|
1654
|
+
return (0, values_1.rvNumber)(betaIncomplete((d1 * x.value) / (d1 * x.value + d2), d1 / 2, d2 / 2));
|
|
1655
|
+
}
|
|
1656
|
+
const num = (Math.pow(d1 * x.value, d1 / 2) * Math.pow(d2, d2 / 2)) /
|
|
1657
|
+
Math.pow(d1 * x.value + d2, (d1 + d2) / 2);
|
|
1658
|
+
const denom = x.value * Math.exp(lnGamma(d1 / 2) + lnGamma(d2 / 2) - lnGamma((d1 + d2) / 2));
|
|
1659
|
+
return (0, values_1.rvNumber)(denom === 0 ? 0 : num / denom);
|
|
1660
|
+
};
|
|
1661
|
+
exports.fnF_DIST = fnF_DIST;
|
|
1662
|
+
const fnF_INV = args => {
|
|
1663
|
+
const p = (0, _shared_1.argToNumber)(args[0]);
|
|
1664
|
+
if (p.kind === 4 /* RVKind.Error */) {
|
|
1665
|
+
return p;
|
|
1666
|
+
}
|
|
1667
|
+
const df1 = (0, _shared_1.argToNumber)(args[1]);
|
|
1668
|
+
if (df1.kind === 4 /* RVKind.Error */) {
|
|
1669
|
+
return df1;
|
|
1670
|
+
}
|
|
1671
|
+
const df2 = (0, _shared_1.argToNumber)(args[2]);
|
|
1672
|
+
if (df2.kind === 4 /* RVKind.Error */) {
|
|
1673
|
+
return df2;
|
|
1674
|
+
}
|
|
1675
|
+
if (p.value < 0 || p.value >= 1 || df1.value < 1 || df2.value < 1) {
|
|
1676
|
+
return values_1.ERRORS.NUM;
|
|
1677
|
+
}
|
|
1678
|
+
const d1 = df1.value;
|
|
1679
|
+
const d2 = df2.value;
|
|
1680
|
+
let lo = 0;
|
|
1681
|
+
let hi = 1000;
|
|
1682
|
+
for (let i = 0; i < 100; i++) {
|
|
1683
|
+
const mid = (lo + hi) / 2;
|
|
1684
|
+
const cdf = betaIncomplete((d1 * mid) / (d1 * mid + d2), d1 / 2, d2 / 2);
|
|
1685
|
+
if (cdf < p.value) {
|
|
1686
|
+
lo = mid;
|
|
1687
|
+
}
|
|
1688
|
+
else {
|
|
1689
|
+
hi = mid;
|
|
1690
|
+
}
|
|
1691
|
+
if (hi - lo < 1e-10) {
|
|
1692
|
+
break;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
return (0, values_1.rvNumber)((lo + hi) / 2);
|
|
1696
|
+
};
|
|
1697
|
+
exports.fnF_INV = fnF_INV;
|
|
1698
|
+
const fnT_DIST = args => {
|
|
1699
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
1700
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
1701
|
+
return x;
|
|
1702
|
+
}
|
|
1703
|
+
const df = (0, _shared_1.argToNumber)(args[1]);
|
|
1704
|
+
if (df.kind === 4 /* RVKind.Error */) {
|
|
1705
|
+
return df;
|
|
1706
|
+
}
|
|
1707
|
+
const cum = boolArg(args, 2);
|
|
1708
|
+
if (!cum.ok) {
|
|
1709
|
+
return cum.error;
|
|
1710
|
+
}
|
|
1711
|
+
if (df.value < 1) {
|
|
1712
|
+
return values_1.ERRORS.NUM;
|
|
1713
|
+
}
|
|
1714
|
+
const v = df.value;
|
|
1715
|
+
if (cum.value) {
|
|
1716
|
+
const t = v / (v + x.value * x.value);
|
|
1717
|
+
const halfBeta = 0.5 * betaIncomplete(t, v / 2, 0.5);
|
|
1718
|
+
return (0, values_1.rvNumber)(x.value >= 0 ? 1 - halfBeta : halfBeta);
|
|
1719
|
+
}
|
|
1720
|
+
return (0, values_1.rvNumber)(Math.exp(lnGamma((v + 1) / 2) - lnGamma(v / 2)) /
|
|
1721
|
+
(Math.sqrt(v * Math.PI) * Math.pow(1 + (x.value * x.value) / v, (v + 1) / 2)));
|
|
1722
|
+
};
|
|
1723
|
+
exports.fnT_DIST = fnT_DIST;
|
|
1724
|
+
const fnT_INV = args => {
|
|
1725
|
+
const p = (0, _shared_1.argToNumber)(args[0]);
|
|
1726
|
+
if (p.kind === 4 /* RVKind.Error */) {
|
|
1727
|
+
return p;
|
|
1728
|
+
}
|
|
1729
|
+
const df = (0, _shared_1.argToNumber)(args[1]);
|
|
1730
|
+
if (df.kind === 4 /* RVKind.Error */) {
|
|
1731
|
+
return df;
|
|
1732
|
+
}
|
|
1733
|
+
if (p.value <= 0 || p.value >= 1 || df.value < 1) {
|
|
1734
|
+
return values_1.ERRORS.NUM;
|
|
1735
|
+
}
|
|
1736
|
+
let x = normSInv(p.value);
|
|
1737
|
+
const v = df.value;
|
|
1738
|
+
for (let iter = 0; iter < 100; iter++) {
|
|
1739
|
+
const t = v / (v + x * x);
|
|
1740
|
+
const halfBeta = 0.5 * betaIncomplete(t, v / 2, 0.5);
|
|
1741
|
+
const cdf = x >= 0 ? 1 - halfBeta : halfBeta;
|
|
1742
|
+
const pdf = Math.exp(lnGamma((v + 1) / 2) - lnGamma(v / 2)) /
|
|
1743
|
+
(Math.sqrt(v * Math.PI) * Math.pow(1 + (x * x) / v, (v + 1) / 2));
|
|
1744
|
+
if (Math.abs(pdf) < 1e-15) {
|
|
1745
|
+
break;
|
|
1746
|
+
}
|
|
1747
|
+
const delta = (cdf - p.value) / pdf;
|
|
1748
|
+
x -= delta;
|
|
1749
|
+
if (Math.abs(delta) < 1e-10) {
|
|
1750
|
+
break;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
return (0, values_1.rvNumber)(x);
|
|
1754
|
+
};
|
|
1755
|
+
exports.fnT_INV = fnT_INV;
|
|
1756
|
+
const fnT_DIST_2T = args => {
|
|
1757
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
1758
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
1759
|
+
return x;
|
|
1760
|
+
}
|
|
1761
|
+
const df = (0, _shared_1.argToNumber)(args[1]);
|
|
1762
|
+
if (df.kind === 4 /* RVKind.Error */) {
|
|
1763
|
+
return df;
|
|
1764
|
+
}
|
|
1765
|
+
if (x.value < 0 || df.value < 1) {
|
|
1766
|
+
return values_1.ERRORS.NUM;
|
|
1767
|
+
}
|
|
1768
|
+
const v = df.value;
|
|
1769
|
+
return (0, values_1.rvNumber)(betaIncomplete(v / (v + x.value * x.value), v / 2, 0.5));
|
|
1770
|
+
};
|
|
1771
|
+
exports.fnT_DIST_2T = fnT_DIST_2T;
|
|
1772
|
+
const fnT_DIST_RT = args => {
|
|
1773
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
1774
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
1775
|
+
return x;
|
|
1776
|
+
}
|
|
1777
|
+
const df = (0, _shared_1.argToNumber)(args[1]);
|
|
1778
|
+
if (df.kind === 4 /* RVKind.Error */) {
|
|
1779
|
+
return df;
|
|
1780
|
+
}
|
|
1781
|
+
if (df.value < 1) {
|
|
1782
|
+
return values_1.ERRORS.NUM;
|
|
1783
|
+
}
|
|
1784
|
+
const v = df.value;
|
|
1785
|
+
const halfBeta = 0.5 * betaIncomplete(v / (v + x.value * x.value), v / 2, 0.5);
|
|
1786
|
+
// T.DIST.RT(x, df) = right-tail = 1 - CDF(x)
|
|
1787
|
+
// For x >= 0: right-tail = halfBeta
|
|
1788
|
+
// For x < 0: right-tail = 1 - halfBeta
|
|
1789
|
+
return (0, values_1.rvNumber)(x.value >= 0 ? halfBeta : 1 - halfBeta);
|
|
1790
|
+
};
|
|
1791
|
+
exports.fnT_DIST_RT = fnT_DIST_RT;
|
|
1792
|
+
const fnT_INV_2T = args => {
|
|
1793
|
+
const p = (0, _shared_1.argToNumber)(args[0]);
|
|
1794
|
+
if (p.kind === 4 /* RVKind.Error */) {
|
|
1795
|
+
return p;
|
|
1796
|
+
}
|
|
1797
|
+
const df = (0, _shared_1.argToNumber)(args[1]);
|
|
1798
|
+
if (df.kind === 4 /* RVKind.Error */) {
|
|
1799
|
+
return df;
|
|
1800
|
+
}
|
|
1801
|
+
if (p.value <= 0 || p.value > 1 || df.value < 1) {
|
|
1802
|
+
return values_1.ERRORS.NUM;
|
|
1803
|
+
}
|
|
1804
|
+
const result = (0, exports.fnT_INV)([(0, values_1.rvNumber)(1 - p.value / 2), args[1]]);
|
|
1805
|
+
if ((0, values_1.isError)(result)) {
|
|
1806
|
+
return result;
|
|
1807
|
+
}
|
|
1808
|
+
return (0, values_1.rvNumber)(Math.abs(result.value));
|
|
1809
|
+
};
|
|
1810
|
+
exports.fnT_INV_2T = fnT_INV_2T;
|
|
1811
|
+
// ============================================================================
|
|
1812
|
+
// BETA, GAMMA, EXPON, WEIBULL, LOGNORM distributions
|
|
1813
|
+
// ============================================================================
|
|
1814
|
+
const fnBETA_DIST = args => {
|
|
1815
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
1816
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
1817
|
+
return x;
|
|
1818
|
+
}
|
|
1819
|
+
const alpha = (0, _shared_1.argToNumber)(args[1]);
|
|
1820
|
+
if (alpha.kind === 4 /* RVKind.Error */) {
|
|
1821
|
+
return alpha;
|
|
1822
|
+
}
|
|
1823
|
+
const beta = (0, _shared_1.argToNumber)(args[2]);
|
|
1824
|
+
if (beta.kind === 4 /* RVKind.Error */) {
|
|
1825
|
+
return beta;
|
|
1826
|
+
}
|
|
1827
|
+
let cumVal = true;
|
|
1828
|
+
if (args.length > 3) {
|
|
1829
|
+
const cum = boolArg(args, 3);
|
|
1830
|
+
if (!cum.ok) {
|
|
1831
|
+
return cum.error;
|
|
1832
|
+
}
|
|
1833
|
+
cumVal = cum.value;
|
|
1834
|
+
}
|
|
1835
|
+
let A = 0;
|
|
1836
|
+
if (args.length > 4) {
|
|
1837
|
+
const aRV = (0, _shared_1.argToNumber)(args[4]);
|
|
1838
|
+
if (aRV.kind === 4 /* RVKind.Error */) {
|
|
1839
|
+
return aRV;
|
|
1840
|
+
}
|
|
1841
|
+
A = aRV.value;
|
|
1842
|
+
}
|
|
1843
|
+
let B = 1;
|
|
1844
|
+
if (args.length > 5) {
|
|
1845
|
+
const bRV = (0, _shared_1.argToNumber)(args[5]);
|
|
1846
|
+
if (bRV.kind === 4 /* RVKind.Error */) {
|
|
1847
|
+
return bRV;
|
|
1848
|
+
}
|
|
1849
|
+
B = bRV.value;
|
|
1850
|
+
}
|
|
1851
|
+
if (alpha.value <= 0 || beta.value <= 0 || B <= A) {
|
|
1852
|
+
return values_1.ERRORS.NUM;
|
|
1853
|
+
}
|
|
1854
|
+
const xn = (x.value - A) / (B - A);
|
|
1855
|
+
if (xn < 0 || xn > 1) {
|
|
1856
|
+
return values_1.ERRORS.NUM;
|
|
1857
|
+
}
|
|
1858
|
+
if (cumVal) {
|
|
1859
|
+
return (0, values_1.rvNumber)(betaIncomplete(xn, alpha.value, beta.value));
|
|
1860
|
+
}
|
|
1861
|
+
return (0, values_1.rvNumber)(Math.exp((alpha.value - 1) * Math.log(xn) +
|
|
1862
|
+
(beta.value - 1) * Math.log(1 - xn) -
|
|
1863
|
+
lnGamma(alpha.value) -
|
|
1864
|
+
lnGamma(beta.value) +
|
|
1865
|
+
lnGamma(alpha.value + beta.value)) /
|
|
1866
|
+
(B - A));
|
|
1867
|
+
};
|
|
1868
|
+
exports.fnBETA_DIST = fnBETA_DIST;
|
|
1869
|
+
const fnBETA_INV = args => {
|
|
1870
|
+
const p = (0, _shared_1.argToNumber)(args[0]);
|
|
1871
|
+
if (p.kind === 4 /* RVKind.Error */) {
|
|
1872
|
+
return p;
|
|
1873
|
+
}
|
|
1874
|
+
const alpha = (0, _shared_1.argToNumber)(args[1]);
|
|
1875
|
+
if (alpha.kind === 4 /* RVKind.Error */) {
|
|
1876
|
+
return alpha;
|
|
1877
|
+
}
|
|
1878
|
+
const beta = (0, _shared_1.argToNumber)(args[2]);
|
|
1879
|
+
if (beta.kind === 4 /* RVKind.Error */) {
|
|
1880
|
+
return beta;
|
|
1881
|
+
}
|
|
1882
|
+
let A = 0;
|
|
1883
|
+
if (args.length > 3) {
|
|
1884
|
+
const aRV = (0, _shared_1.argToNumber)(args[3]);
|
|
1885
|
+
if (aRV.kind === 4 /* RVKind.Error */) {
|
|
1886
|
+
return aRV;
|
|
1887
|
+
}
|
|
1888
|
+
A = aRV.value;
|
|
1889
|
+
}
|
|
1890
|
+
let B = 1;
|
|
1891
|
+
if (args.length > 4) {
|
|
1892
|
+
const bRV = (0, _shared_1.argToNumber)(args[4]);
|
|
1893
|
+
if (bRV.kind === 4 /* RVKind.Error */) {
|
|
1894
|
+
return bRV;
|
|
1895
|
+
}
|
|
1896
|
+
B = bRV.value;
|
|
1897
|
+
}
|
|
1898
|
+
if (p.value < 0 || p.value > 1 || alpha.value <= 0 || beta.value <= 0) {
|
|
1899
|
+
return values_1.ERRORS.NUM;
|
|
1900
|
+
}
|
|
1901
|
+
let lo = 0;
|
|
1902
|
+
let hi = 1;
|
|
1903
|
+
for (let i = 0; i < 100; i++) {
|
|
1904
|
+
const mid = (lo + hi) / 2;
|
|
1905
|
+
if (betaIncomplete(mid, alpha.value, beta.value) < p.value) {
|
|
1906
|
+
lo = mid;
|
|
1907
|
+
}
|
|
1908
|
+
else {
|
|
1909
|
+
hi = mid;
|
|
1910
|
+
}
|
|
1911
|
+
if (hi - lo < 1e-12) {
|
|
1912
|
+
break;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
return (0, values_1.rvNumber)(A + ((lo + hi) / 2) * (B - A));
|
|
1916
|
+
};
|
|
1917
|
+
exports.fnBETA_INV = fnBETA_INV;
|
|
1918
|
+
const fnGAMMA = args => {
|
|
1919
|
+
const n = (0, _shared_1.argToNumber)(args[0]);
|
|
1920
|
+
if (n.kind === 4 /* RVKind.Error */) {
|
|
1921
|
+
return n;
|
|
1922
|
+
}
|
|
1923
|
+
if (n.value <= 0 && n.value === Math.floor(n.value)) {
|
|
1924
|
+
return values_1.ERRORS.NUM;
|
|
1925
|
+
}
|
|
1926
|
+
return (0, values_1.rvNumber)(gammaFn(n.value));
|
|
1927
|
+
};
|
|
1928
|
+
exports.fnGAMMA = fnGAMMA;
|
|
1929
|
+
const fnGAMMALN = args => {
|
|
1930
|
+
const n = (0, _shared_1.argToNumber)(args[0]);
|
|
1931
|
+
if (n.kind === 4 /* RVKind.Error */) {
|
|
1932
|
+
return n;
|
|
1933
|
+
}
|
|
1934
|
+
if (n.value <= 0) {
|
|
1935
|
+
return values_1.ERRORS.NUM;
|
|
1936
|
+
}
|
|
1937
|
+
return (0, values_1.rvNumber)(lnGamma(n.value));
|
|
1938
|
+
};
|
|
1939
|
+
exports.fnGAMMALN = fnGAMMALN;
|
|
1940
|
+
const fnGAMMA_DIST = args => {
|
|
1941
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
1942
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
1943
|
+
return x;
|
|
1944
|
+
}
|
|
1945
|
+
const alpha = (0, _shared_1.argToNumber)(args[1]);
|
|
1946
|
+
if (alpha.kind === 4 /* RVKind.Error */) {
|
|
1947
|
+
return alpha;
|
|
1948
|
+
}
|
|
1949
|
+
const beta = (0, _shared_1.argToNumber)(args[2]);
|
|
1950
|
+
if (beta.kind === 4 /* RVKind.Error */) {
|
|
1951
|
+
return beta;
|
|
1952
|
+
}
|
|
1953
|
+
const cum = boolArg(args, 3);
|
|
1954
|
+
if (!cum.ok) {
|
|
1955
|
+
return cum.error;
|
|
1956
|
+
}
|
|
1957
|
+
if (x.value < 0 || alpha.value <= 0 || beta.value <= 0) {
|
|
1958
|
+
return values_1.ERRORS.NUM;
|
|
1959
|
+
}
|
|
1960
|
+
if (cum.value) {
|
|
1961
|
+
return (0, values_1.rvNumber)(gammaIncomplete(alpha.value, x.value / beta.value));
|
|
1962
|
+
}
|
|
1963
|
+
return (0, values_1.rvNumber)(Math.exp((alpha.value - 1) * Math.log(x.value) -
|
|
1964
|
+
x.value / beta.value -
|
|
1965
|
+
alpha.value * Math.log(beta.value) -
|
|
1966
|
+
lnGamma(alpha.value)));
|
|
1967
|
+
};
|
|
1968
|
+
exports.fnGAMMA_DIST = fnGAMMA_DIST;
|
|
1969
|
+
const fnGAMMA_INV = args => {
|
|
1970
|
+
const p = (0, _shared_1.argToNumber)(args[0]);
|
|
1971
|
+
if (p.kind === 4 /* RVKind.Error */) {
|
|
1972
|
+
return p;
|
|
1973
|
+
}
|
|
1974
|
+
const alpha = (0, _shared_1.argToNumber)(args[1]);
|
|
1975
|
+
if (alpha.kind === 4 /* RVKind.Error */) {
|
|
1976
|
+
return alpha;
|
|
1977
|
+
}
|
|
1978
|
+
const beta = (0, _shared_1.argToNumber)(args[2]);
|
|
1979
|
+
if (beta.kind === 4 /* RVKind.Error */) {
|
|
1980
|
+
return beta;
|
|
1981
|
+
}
|
|
1982
|
+
if (p.value < 0 || p.value >= 1 || alpha.value <= 0 || beta.value <= 0) {
|
|
1983
|
+
return values_1.ERRORS.NUM;
|
|
1984
|
+
}
|
|
1985
|
+
let lo = 0;
|
|
1986
|
+
let hi = Math.max(alpha.value * beta.value * 10, 100);
|
|
1987
|
+
for (let i = 0; i < 100; i++) {
|
|
1988
|
+
const mid = (lo + hi) / 2;
|
|
1989
|
+
if (gammaIncomplete(alpha.value, mid / beta.value) < p.value) {
|
|
1990
|
+
lo = mid;
|
|
1991
|
+
}
|
|
1992
|
+
else {
|
|
1993
|
+
hi = mid;
|
|
1994
|
+
}
|
|
1995
|
+
if (hi - lo < 1e-10) {
|
|
1996
|
+
break;
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
return (0, values_1.rvNumber)((lo + hi) / 2);
|
|
2000
|
+
};
|
|
2001
|
+
exports.fnGAMMA_INV = fnGAMMA_INV;
|
|
2002
|
+
const fnEXPON_DIST = args => {
|
|
2003
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
2004
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
2005
|
+
return x;
|
|
2006
|
+
}
|
|
2007
|
+
const lambda = (0, _shared_1.argToNumber)(args[1]);
|
|
2008
|
+
if (lambda.kind === 4 /* RVKind.Error */) {
|
|
2009
|
+
return lambda;
|
|
2010
|
+
}
|
|
2011
|
+
const cum = boolArg(args, 2);
|
|
2012
|
+
if (!cum.ok) {
|
|
2013
|
+
return cum.error;
|
|
2014
|
+
}
|
|
2015
|
+
if (x.value < 0 || lambda.value <= 0) {
|
|
2016
|
+
return values_1.ERRORS.NUM;
|
|
2017
|
+
}
|
|
2018
|
+
return (0, values_1.rvNumber)(cum.value
|
|
2019
|
+
? 1 - Math.exp(-lambda.value * x.value)
|
|
2020
|
+
: lambda.value * Math.exp(-lambda.value * x.value));
|
|
2021
|
+
};
|
|
2022
|
+
exports.fnEXPON_DIST = fnEXPON_DIST;
|
|
2023
|
+
const fnWEIBULL_DIST = args => {
|
|
2024
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
2025
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
2026
|
+
return x;
|
|
2027
|
+
}
|
|
2028
|
+
const alpha = (0, _shared_1.argToNumber)(args[1]);
|
|
2029
|
+
if (alpha.kind === 4 /* RVKind.Error */) {
|
|
2030
|
+
return alpha;
|
|
2031
|
+
}
|
|
2032
|
+
const beta = (0, _shared_1.argToNumber)(args[2]);
|
|
2033
|
+
if (beta.kind === 4 /* RVKind.Error */) {
|
|
2034
|
+
return beta;
|
|
2035
|
+
}
|
|
2036
|
+
const cum = boolArg(args, 3);
|
|
2037
|
+
if (!cum.ok) {
|
|
2038
|
+
return cum.error;
|
|
2039
|
+
}
|
|
2040
|
+
if (x.value < 0 || alpha.value <= 0 || beta.value <= 0) {
|
|
2041
|
+
return values_1.ERRORS.NUM;
|
|
2042
|
+
}
|
|
2043
|
+
if (cum.value) {
|
|
2044
|
+
return (0, values_1.rvNumber)(1 - Math.exp(-Math.pow(x.value / beta.value, alpha.value)));
|
|
2045
|
+
}
|
|
2046
|
+
return (0, values_1.rvNumber)((alpha.value / beta.value) *
|
|
2047
|
+
Math.pow(x.value / beta.value, alpha.value - 1) *
|
|
2048
|
+
Math.exp(-Math.pow(x.value / beta.value, alpha.value)));
|
|
2049
|
+
};
|
|
2050
|
+
exports.fnWEIBULL_DIST = fnWEIBULL_DIST;
|
|
2051
|
+
const fnLOGNORM_DIST = args => {
|
|
2052
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
2053
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
2054
|
+
return x;
|
|
2055
|
+
}
|
|
2056
|
+
const mean = (0, _shared_1.argToNumber)(args[1]);
|
|
2057
|
+
if (mean.kind === 4 /* RVKind.Error */) {
|
|
2058
|
+
return mean;
|
|
2059
|
+
}
|
|
2060
|
+
const stddev = (0, _shared_1.argToNumber)(args[2]);
|
|
2061
|
+
if (stddev.kind === 4 /* RVKind.Error */) {
|
|
2062
|
+
return stddev;
|
|
2063
|
+
}
|
|
2064
|
+
const cum = boolArg(args, 3);
|
|
2065
|
+
if (!cum.ok) {
|
|
2066
|
+
return cum.error;
|
|
2067
|
+
}
|
|
2068
|
+
if (x.value <= 0 || stddev.value <= 0) {
|
|
2069
|
+
return values_1.ERRORS.NUM;
|
|
2070
|
+
}
|
|
2071
|
+
const z = (Math.log(x.value) - mean.value) / stddev.value;
|
|
2072
|
+
if (cum.value) {
|
|
2073
|
+
return (0, values_1.rvNumber)(normSDist(z));
|
|
2074
|
+
}
|
|
2075
|
+
return (0, values_1.rvNumber)(normSPdf(z) / (x.value * stddev.value));
|
|
2076
|
+
};
|
|
2077
|
+
exports.fnLOGNORM_DIST = fnLOGNORM_DIST;
|
|
2078
|
+
const fnLOGNORM_INV = args => {
|
|
2079
|
+
const p = (0, _shared_1.argToNumber)(args[0]);
|
|
2080
|
+
if (p.kind === 4 /* RVKind.Error */) {
|
|
2081
|
+
return p;
|
|
2082
|
+
}
|
|
2083
|
+
const mean = (0, _shared_1.argToNumber)(args[1]);
|
|
2084
|
+
if (mean.kind === 4 /* RVKind.Error */) {
|
|
2085
|
+
return mean;
|
|
2086
|
+
}
|
|
2087
|
+
const stddev = (0, _shared_1.argToNumber)(args[2]);
|
|
2088
|
+
if (stddev.kind === 4 /* RVKind.Error */) {
|
|
2089
|
+
return stddev;
|
|
2090
|
+
}
|
|
2091
|
+
if (p.value <= 0 || p.value >= 1 || stddev.value <= 0) {
|
|
2092
|
+
return values_1.ERRORS.NUM;
|
|
2093
|
+
}
|
|
2094
|
+
return (0, values_1.rvNumber)(Math.exp(mean.value + stddev.value * normSInv(p.value)));
|
|
2095
|
+
};
|
|
2096
|
+
exports.fnLOGNORM_INV = fnLOGNORM_INV;
|
|
2097
|
+
const fnPHI = args => {
|
|
2098
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
2099
|
+
return x.kind === 4 /* RVKind.Error */ ? x : (0, values_1.rvNumber)(normSPdf(x.value));
|
|
2100
|
+
};
|
|
2101
|
+
exports.fnPHI = fnPHI;
|
|
2102
|
+
const fnGAUSS = args => {
|
|
2103
|
+
const z = (0, _shared_1.argToNumber)(args[0]);
|
|
2104
|
+
return z.kind === 4 /* RVKind.Error */ ? z : (0, values_1.rvNumber)(normSDist(z.value) - 0.5);
|
|
2105
|
+
};
|
|
2106
|
+
exports.fnGAUSS = fnGAUSS;
|
|
2107
|
+
// ============================================================================
|
|
2108
|
+
// ERF, ERFC, STANDARDIZE
|
|
2109
|
+
// ============================================================================
|
|
2110
|
+
function erfFn(x) {
|
|
2111
|
+
// Exact at 0 — the rational approximation below leaves a ~7e-10 drift at
|
|
2112
|
+
// t = 1 / (1 + p·0) = 1, which is visible to callers that expect
|
|
2113
|
+
// `ERF(0) === 0` (and especially to `ERFC(0) === 1`).
|
|
2114
|
+
if (x === 0) {
|
|
2115
|
+
return 0;
|
|
2116
|
+
}
|
|
2117
|
+
const a1 = 0.254829592;
|
|
2118
|
+
const a2 = -0.284496736;
|
|
2119
|
+
const a3 = 1.421413741;
|
|
2120
|
+
const a4 = -1.453152027;
|
|
2121
|
+
const a5 = 1.061405429;
|
|
2122
|
+
const p = 0.3275911;
|
|
2123
|
+
const sign = x < 0 ? -1 : 1;
|
|
2124
|
+
const ax = Math.abs(x);
|
|
2125
|
+
const t = 1.0 / (1.0 + p * ax);
|
|
2126
|
+
const y = 1.0 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-ax * ax);
|
|
2127
|
+
return sign * y;
|
|
2128
|
+
}
|
|
2129
|
+
const fnERF = args => {
|
|
2130
|
+
const lower = (0, _shared_1.argToNumber)(args[0]);
|
|
2131
|
+
if (lower.kind === 4 /* RVKind.Error */) {
|
|
2132
|
+
return lower;
|
|
2133
|
+
}
|
|
2134
|
+
if (args.length > 1) {
|
|
2135
|
+
const upper = (0, _shared_1.argToNumber)(args[1]);
|
|
2136
|
+
if (upper.kind === 4 /* RVKind.Error */) {
|
|
2137
|
+
return upper;
|
|
2138
|
+
}
|
|
2139
|
+
return (0, values_1.rvNumber)(erfFn(upper.value) - erfFn(lower.value));
|
|
2140
|
+
}
|
|
2141
|
+
return (0, values_1.rvNumber)(erfFn(lower.value));
|
|
2142
|
+
};
|
|
2143
|
+
exports.fnERF = fnERF;
|
|
2144
|
+
const fnERFC = args => {
|
|
2145
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
2146
|
+
return x.kind === 4 /* RVKind.Error */ ? x : (0, values_1.rvNumber)(1 - erfFn(x.value));
|
|
2147
|
+
};
|
|
2148
|
+
exports.fnERFC = fnERFC;
|
|
2149
|
+
const fnSTANDARDIZE = args => {
|
|
2150
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
2151
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
2152
|
+
return x;
|
|
2153
|
+
}
|
|
2154
|
+
const mean = (0, _shared_1.argToNumber)(args[1]);
|
|
2155
|
+
if (mean.kind === 4 /* RVKind.Error */) {
|
|
2156
|
+
return mean;
|
|
2157
|
+
}
|
|
2158
|
+
const stddev = (0, _shared_1.argToNumber)(args[2]);
|
|
2159
|
+
if (stddev.kind === 4 /* RVKind.Error */) {
|
|
2160
|
+
return stddev;
|
|
2161
|
+
}
|
|
2162
|
+
if (stddev.value <= 0) {
|
|
2163
|
+
return values_1.ERRORS.NUM;
|
|
2164
|
+
}
|
|
2165
|
+
return (0, values_1.rvNumber)((x.value - mean.value) / stddev.value);
|
|
2166
|
+
};
|
|
2167
|
+
exports.fnSTANDARDIZE = fnSTANDARDIZE;
|
|
2168
|
+
// ============================================================================
|
|
2169
|
+
// Array-returning functions: FREQUENCY, GROWTH, TREND, LINEST, LOGEST
|
|
2170
|
+
// ============================================================================
|
|
2171
|
+
const fnFREQUENCY = args => {
|
|
2172
|
+
if (!isArrayArg(args[0]) || !isArrayArg(args[1])) {
|
|
2173
|
+
return values_1.ERRORS.VALUE;
|
|
2174
|
+
}
|
|
2175
|
+
const rawData = (0, _shared_1.flattenNumbers)([args[0]]);
|
|
2176
|
+
const dataErr = (0, _shared_1.firstError)(rawData);
|
|
2177
|
+
if (dataErr) {
|
|
2178
|
+
return dataErr;
|
|
2179
|
+
}
|
|
2180
|
+
const data = rawData.map(n => n.value);
|
|
2181
|
+
const rawBins = (0, _shared_1.flattenNumbers)([args[1]]);
|
|
2182
|
+
const binsErr = (0, _shared_1.firstError)(rawBins);
|
|
2183
|
+
if (binsErr) {
|
|
2184
|
+
return binsErr;
|
|
2185
|
+
}
|
|
2186
|
+
const bins = rawBins.map(n => n.value);
|
|
2187
|
+
// IMPORTANT: do NOT sort `bins`. Excel's FREQUENCY bucketises data into
|
|
2188
|
+
// the bins as the user supplied them — both the output count order and
|
|
2189
|
+
// the bucket boundaries follow the original sequence. Sorting would
|
|
2190
|
+
// silently reshuffle the result array, which is a common historical
|
|
2191
|
+
// implementation mistake.
|
|
2192
|
+
//
|
|
2193
|
+
// For each datum we assign it to the first bin `i` whose upper bound
|
|
2194
|
+
// satisfies `data <= bins[i]`; if no such bin exists the datum falls
|
|
2195
|
+
// into the overflow bucket at index `bins.length`. This matches Excel's
|
|
2196
|
+
// semantics for any bin order (monotonic or not).
|
|
2197
|
+
const result = [];
|
|
2198
|
+
const counts = new Array(bins.length + 1).fill(0);
|
|
2199
|
+
for (const d of data) {
|
|
2200
|
+
let assigned = false;
|
|
2201
|
+
for (let i = 0; i < bins.length; i++) {
|
|
2202
|
+
if (d <= bins[i]) {
|
|
2203
|
+
counts[i]++;
|
|
2204
|
+
assigned = true;
|
|
2205
|
+
break;
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
if (!assigned) {
|
|
2209
|
+
counts[bins.length]++;
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
for (const c of counts) {
|
|
2213
|
+
result.push([(0, values_1.rvNumber)(c)]);
|
|
2214
|
+
}
|
|
2215
|
+
return (0, values_1.rvArray)(result);
|
|
2216
|
+
};
|
|
2217
|
+
exports.fnFREQUENCY = fnFREQUENCY;
|
|
2218
|
+
/** Narrow `T | ErrorValue` to `ErrorValue`. */
|
|
2219
|
+
function isErr(v) {
|
|
2220
|
+
return v.kind === 4 /* RVKind.Error */;
|
|
2221
|
+
}
|
|
2222
|
+
function extractMatrix(arg) {
|
|
2223
|
+
if (arg.kind !== 5 /* RVKind.Array */) {
|
|
2224
|
+
const sv = (0, values_1.topLeft)(arg);
|
|
2225
|
+
if (sv.kind === 4 /* RVKind.Error */) {
|
|
2226
|
+
return sv;
|
|
2227
|
+
}
|
|
2228
|
+
const n = (0, values_1.toNumberRV)(sv);
|
|
2229
|
+
if (n.kind === 4 /* RVKind.Error */) {
|
|
2230
|
+
return n;
|
|
2231
|
+
}
|
|
2232
|
+
return { m: { data: [[n.value]], rows: 1, cols: 1 }, orient: "row" };
|
|
2233
|
+
}
|
|
2234
|
+
const data = [];
|
|
2235
|
+
for (const row of arg.rows) {
|
|
2236
|
+
const out = [];
|
|
2237
|
+
for (const cell of row) {
|
|
2238
|
+
if (cell.kind === 4 /* RVKind.Error */) {
|
|
2239
|
+
return cell;
|
|
2240
|
+
}
|
|
2241
|
+
if (cell.kind === 1 /* RVKind.Number */) {
|
|
2242
|
+
out.push(cell.value);
|
|
2243
|
+
}
|
|
2244
|
+
else if (cell.kind === 0 /* RVKind.Blank */) {
|
|
2245
|
+
return values_1.ERRORS.VALUE;
|
|
2246
|
+
}
|
|
2247
|
+
else {
|
|
2248
|
+
const n = (0, values_1.toNumberRV)(cell);
|
|
2249
|
+
if (n.kind === 4 /* RVKind.Error */) {
|
|
2250
|
+
return n;
|
|
2251
|
+
}
|
|
2252
|
+
out.push(n.value);
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
data.push(out);
|
|
2256
|
+
}
|
|
2257
|
+
const rows = arg.height;
|
|
2258
|
+
const cols = arg.width;
|
|
2259
|
+
const orient = rows === 1 ? "row" : cols === 1 ? "col" : "matrix";
|
|
2260
|
+
return { m: { data, rows, cols }, orient };
|
|
2261
|
+
}
|
|
2262
|
+
/**
|
|
2263
|
+
* Normalize the `known_x's` argument to a design matrix X of shape [n, k],
|
|
2264
|
+
* where n is the number of observations and k is the number of predictor
|
|
2265
|
+
* variables. Excel infers k from the orientation of known_x's: when y is a
|
|
2266
|
+
* column vector (or square), each column of known_x's is one predictor; when
|
|
2267
|
+
* y is a row vector, each row of known_x's is one predictor.
|
|
2268
|
+
*/
|
|
2269
|
+
function buildDesignMatrix(m, nObs, yOrient) {
|
|
2270
|
+
// Decide orientation of predictors based on y's orientation and matrix shape
|
|
2271
|
+
let byRows;
|
|
2272
|
+
let xOrient;
|
|
2273
|
+
if (m.rows === 1) {
|
|
2274
|
+
// Row vector of length n: single predictor, one observation per column
|
|
2275
|
+
byRows = false;
|
|
2276
|
+
xOrient = "row";
|
|
2277
|
+
}
|
|
2278
|
+
else if (m.cols === 1) {
|
|
2279
|
+
// Column vector of length n: single predictor, one observation per row
|
|
2280
|
+
byRows = true;
|
|
2281
|
+
xOrient = "col";
|
|
2282
|
+
}
|
|
2283
|
+
else if (yOrient === "row") {
|
|
2284
|
+
// y is a row vector: predictors are rows of known_x's
|
|
2285
|
+
byRows = false;
|
|
2286
|
+
xOrient = "row";
|
|
2287
|
+
}
|
|
2288
|
+
else {
|
|
2289
|
+
// y is a column/matrix: predictors are columns of known_x's
|
|
2290
|
+
byRows = true;
|
|
2291
|
+
xOrient = "col";
|
|
2292
|
+
}
|
|
2293
|
+
const n = byRows ? m.rows : m.cols;
|
|
2294
|
+
const k = byRows ? m.cols : m.rows;
|
|
2295
|
+
if (n !== nObs) {
|
|
2296
|
+
return values_1.ERRORS.REF;
|
|
2297
|
+
}
|
|
2298
|
+
const X = [];
|
|
2299
|
+
for (let i = 0; i < n; i++) {
|
|
2300
|
+
const row = [];
|
|
2301
|
+
for (let j = 0; j < k; j++) {
|
|
2302
|
+
row.push(byRows ? m.data[i][j] : m.data[j][i]);
|
|
2303
|
+
}
|
|
2304
|
+
X.push(row);
|
|
2305
|
+
}
|
|
2306
|
+
return { X, k, xOrient };
|
|
2307
|
+
}
|
|
2308
|
+
/** Solve (A^T A) β = A^T b via Gauss–Jordan on the augmented matrix. */
|
|
2309
|
+
function solveNormalEquations(A, b) {
|
|
2310
|
+
const n = A.length;
|
|
2311
|
+
const k = A[0]?.length ?? 0;
|
|
2312
|
+
// Build augmented [AᵀA | Aᵀb]
|
|
2313
|
+
const aug = Array.from({ length: k }, () => new Array(k + 1).fill(0));
|
|
2314
|
+
for (let i = 0; i < k; i++) {
|
|
2315
|
+
for (let j = 0; j < k; j++) {
|
|
2316
|
+
let s = 0;
|
|
2317
|
+
for (let r = 0; r < n; r++) {
|
|
2318
|
+
s += A[r][i] * A[r][j];
|
|
2319
|
+
}
|
|
2320
|
+
aug[i][j] = s;
|
|
2321
|
+
}
|
|
2322
|
+
let sb = 0;
|
|
2323
|
+
for (let r = 0; r < n; r++) {
|
|
2324
|
+
sb += A[r][i] * b[r];
|
|
2325
|
+
}
|
|
2326
|
+
aug[i][k] = sb;
|
|
2327
|
+
}
|
|
2328
|
+
// Gauss–Jordan with partial pivoting
|
|
2329
|
+
for (let i = 0; i < k; i++) {
|
|
2330
|
+
let piv = i;
|
|
2331
|
+
for (let r = i + 1; r < k; r++) {
|
|
2332
|
+
if (Math.abs(aug[r][i]) > Math.abs(aug[piv][i])) {
|
|
2333
|
+
piv = r;
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
if (Math.abs(aug[piv][i]) < 1e-12) {
|
|
2337
|
+
return null;
|
|
2338
|
+
}
|
|
2339
|
+
if (piv !== i) {
|
|
2340
|
+
[aug[i], aug[piv]] = [aug[piv], aug[i]];
|
|
2341
|
+
}
|
|
2342
|
+
const d = aug[i][i];
|
|
2343
|
+
for (let j = i; j <= k; j++) {
|
|
2344
|
+
aug[i][j] /= d;
|
|
2345
|
+
}
|
|
2346
|
+
for (let r = 0; r < k; r++) {
|
|
2347
|
+
if (r === i) {
|
|
2348
|
+
continue;
|
|
2349
|
+
}
|
|
2350
|
+
const f = aug[r][i];
|
|
2351
|
+
if (f === 0) {
|
|
2352
|
+
continue;
|
|
2353
|
+
}
|
|
2354
|
+
for (let j = i; j <= k; j++) {
|
|
2355
|
+
aug[r][j] -= f * aug[i][j];
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
return aug.map(row => row[k]);
|
|
2360
|
+
}
|
|
2361
|
+
/** Compute inverse of k×k symmetric positive-definite matrix via Gauss–Jordan. */
|
|
2362
|
+
function invertSquareMatrix(M) {
|
|
2363
|
+
const k = M.length;
|
|
2364
|
+
const aug = Array.from({ length: k }, (_, i) => {
|
|
2365
|
+
const row = new Array(2 * k).fill(0);
|
|
2366
|
+
for (let j = 0; j < k; j++) {
|
|
2367
|
+
row[j] = M[i][j];
|
|
2368
|
+
}
|
|
2369
|
+
row[k + i] = 1;
|
|
2370
|
+
return row;
|
|
2371
|
+
});
|
|
2372
|
+
for (let i = 0; i < k; i++) {
|
|
2373
|
+
let piv = i;
|
|
2374
|
+
for (let r = i + 1; r < k; r++) {
|
|
2375
|
+
if (Math.abs(aug[r][i]) > Math.abs(aug[piv][i])) {
|
|
2376
|
+
piv = r;
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
if (Math.abs(aug[piv][i]) < 1e-12) {
|
|
2380
|
+
return null;
|
|
2381
|
+
}
|
|
2382
|
+
if (piv !== i) {
|
|
2383
|
+
[aug[i], aug[piv]] = [aug[piv], aug[i]];
|
|
2384
|
+
}
|
|
2385
|
+
const d = aug[i][i];
|
|
2386
|
+
for (let j = 0; j < 2 * k; j++) {
|
|
2387
|
+
aug[i][j] /= d;
|
|
2388
|
+
}
|
|
2389
|
+
for (let r = 0; r < k; r++) {
|
|
2390
|
+
if (r === i) {
|
|
2391
|
+
continue;
|
|
2392
|
+
}
|
|
2393
|
+
const f = aug[r][i];
|
|
2394
|
+
if (f === 0) {
|
|
2395
|
+
continue;
|
|
2396
|
+
}
|
|
2397
|
+
for (let j = 0; j < 2 * k; j++) {
|
|
2398
|
+
aug[r][j] -= f * aug[i][j];
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
return aug.map(row => row.slice(k));
|
|
2403
|
+
}
|
|
2404
|
+
function runLeastSquares(input, withStats) {
|
|
2405
|
+
const { includeIntercept, logMode } = input;
|
|
2406
|
+
const n = input.y.length;
|
|
2407
|
+
const k = input.X[0]?.length ?? 0;
|
|
2408
|
+
if (n < 1 || k < 1) {
|
|
2409
|
+
return values_1.ERRORS.VALUE;
|
|
2410
|
+
}
|
|
2411
|
+
// y in log domain for LOGEST/GROWTH
|
|
2412
|
+
const y = new Array(n);
|
|
2413
|
+
for (let i = 0; i < n; i++) {
|
|
2414
|
+
const yi = input.y[i];
|
|
2415
|
+
if (logMode) {
|
|
2416
|
+
if (yi <= 0) {
|
|
2417
|
+
return values_1.ERRORS.NUM;
|
|
2418
|
+
}
|
|
2419
|
+
y[i] = Math.log(yi);
|
|
2420
|
+
}
|
|
2421
|
+
else {
|
|
2422
|
+
y[i] = yi;
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
// Augment X with intercept column (as the last column → Excel puts b at the end)
|
|
2426
|
+
const kAug = includeIntercept ? k + 1 : k;
|
|
2427
|
+
const A = new Array(n);
|
|
2428
|
+
for (let i = 0; i < n; i++) {
|
|
2429
|
+
const row = new Array(kAug);
|
|
2430
|
+
for (let j = 0; j < k; j++) {
|
|
2431
|
+
row[j] = input.X[i][j];
|
|
2432
|
+
}
|
|
2433
|
+
if (includeIntercept) {
|
|
2434
|
+
row[k] = 1;
|
|
2435
|
+
}
|
|
2436
|
+
A[i] = row;
|
|
2437
|
+
}
|
|
2438
|
+
const beta = solveNormalEquations(A, y);
|
|
2439
|
+
if (beta === null) {
|
|
2440
|
+
return values_1.ERRORS.NUM;
|
|
2441
|
+
}
|
|
2442
|
+
// Excel orders coefficients as [mk, m(k-1), ..., m1, b] — reverse the slope
|
|
2443
|
+
// portion but keep the intercept last.
|
|
2444
|
+
const coeffs = [];
|
|
2445
|
+
for (let j = k - 1; j >= 0; j--) {
|
|
2446
|
+
coeffs.push(beta[j]);
|
|
2447
|
+
}
|
|
2448
|
+
if (includeIntercept) {
|
|
2449
|
+
coeffs.push(beta[k]);
|
|
2450
|
+
}
|
|
2451
|
+
else {
|
|
2452
|
+
coeffs.push(0);
|
|
2453
|
+
}
|
|
2454
|
+
if (!withStats) {
|
|
2455
|
+
return { coeffs };
|
|
2456
|
+
}
|
|
2457
|
+
// Residuals, sums of squares
|
|
2458
|
+
const yhat = new Array(n);
|
|
2459
|
+
for (let i = 0; i < n; i++) {
|
|
2460
|
+
let s = 0;
|
|
2461
|
+
for (let j = 0; j < kAug; j++) {
|
|
2462
|
+
s += A[i][j] * beta[j];
|
|
2463
|
+
}
|
|
2464
|
+
yhat[i] = s;
|
|
2465
|
+
}
|
|
2466
|
+
let ybar = 0;
|
|
2467
|
+
if (includeIntercept) {
|
|
2468
|
+
for (const v of y) {
|
|
2469
|
+
ybar += v;
|
|
2470
|
+
}
|
|
2471
|
+
ybar /= n;
|
|
2472
|
+
}
|
|
2473
|
+
let ssResid = 0, ssTot = 0, ssReg = 0;
|
|
2474
|
+
for (let i = 0; i < n; i++) {
|
|
2475
|
+
ssResid += (y[i] - yhat[i]) ** 2;
|
|
2476
|
+
ssTot += (y[i] - ybar) ** 2;
|
|
2477
|
+
ssReg += (yhat[i] - ybar) ** 2;
|
|
2478
|
+
}
|
|
2479
|
+
const df = n - kAug;
|
|
2480
|
+
const r2 = ssTot === 0 ? 1 : 1 - ssResid / ssTot;
|
|
2481
|
+
const sey = df > 0 ? Math.sqrt(ssResid / df) : 0;
|
|
2482
|
+
const fStat = df > 0 && ssResid > 0 ? ssReg / k / (ssResid / df) : Number.POSITIVE_INFINITY;
|
|
2483
|
+
// Standard errors: diag(sey² · (AᵀA)⁻¹)
|
|
2484
|
+
const AtA = Array.from({ length: kAug }, () => new Array(kAug).fill(0));
|
|
2485
|
+
for (let i = 0; i < kAug; i++) {
|
|
2486
|
+
for (let j = 0; j < kAug; j++) {
|
|
2487
|
+
let s = 0;
|
|
2488
|
+
for (let r = 0; r < n; r++) {
|
|
2489
|
+
s += A[r][i] * A[r][j];
|
|
2490
|
+
}
|
|
2491
|
+
AtA[i][j] = s;
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
const inv = invertSquareMatrix(AtA);
|
|
2495
|
+
const seCoeffs = [];
|
|
2496
|
+
if (inv) {
|
|
2497
|
+
for (let j = k - 1; j >= 0; j--) {
|
|
2498
|
+
const v = sey * sey * inv[j][j];
|
|
2499
|
+
seCoeffs.push(v >= 0 ? Math.sqrt(v) : 0);
|
|
2500
|
+
}
|
|
2501
|
+
if (includeIntercept) {
|
|
2502
|
+
const v = sey * sey * inv[k][k];
|
|
2503
|
+
seCoeffs.push(v >= 0 ? Math.sqrt(v) : 0);
|
|
2504
|
+
}
|
|
2505
|
+
else {
|
|
2506
|
+
seCoeffs.push(0);
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
else {
|
|
2510
|
+
// Cannot invert — fill with NaN per Excel behavior (but we use 0 since
|
|
2511
|
+
// downstream consumers expect numbers).
|
|
2512
|
+
for (let j = 0; j <= k; j++) {
|
|
2513
|
+
seCoeffs.push(0);
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
return { coeffs, seCoeffs, r2, sey, fStat, df, ssReg, ssResid };
|
|
2517
|
+
}
|
|
2518
|
+
/** Build Excel's 5×(k+1) LINEST stats block. */
|
|
2519
|
+
function buildStatsBlock(res) {
|
|
2520
|
+
const kPlus1 = res.coeffs.length;
|
|
2521
|
+
const row1 = res.coeffs.map(v => (0, values_1.rvNumber)(v));
|
|
2522
|
+
const row2 = (res.seCoeffs ?? []).map(v => (0, values_1.rvNumber)(v));
|
|
2523
|
+
// Row 3: r² in col 0, sey in col 1, then #N/A for k+1 ≥ 3
|
|
2524
|
+
const row3 = new Array(kPlus1).fill(values_1.ERRORS.NA);
|
|
2525
|
+
row3[0] = (0, values_1.rvNumber)(res.r2 ?? 0);
|
|
2526
|
+
if (kPlus1 >= 2) {
|
|
2527
|
+
row3[1] = (0, values_1.rvNumber)(res.sey ?? 0);
|
|
2528
|
+
}
|
|
2529
|
+
// Row 4: F in col 0, df in col 1, then #N/A
|
|
2530
|
+
const row4 = new Array(kPlus1).fill(values_1.ERRORS.NA);
|
|
2531
|
+
row4[0] = (0, values_1.rvNumber)(res.fStat ?? 0);
|
|
2532
|
+
if (kPlus1 >= 2) {
|
|
2533
|
+
row4[1] = (0, values_1.rvNumber)(res.df ?? 0);
|
|
2534
|
+
}
|
|
2535
|
+
// Row 5: ssReg in col 0, ssResid in col 1, then #N/A
|
|
2536
|
+
const row5 = new Array(kPlus1).fill(values_1.ERRORS.NA);
|
|
2537
|
+
row5[0] = (0, values_1.rvNumber)(res.ssReg ?? 0);
|
|
2538
|
+
if (kPlus1 >= 2) {
|
|
2539
|
+
row5[1] = (0, values_1.rvNumber)(res.ssResid ?? 0);
|
|
2540
|
+
}
|
|
2541
|
+
return [row1, row2, row3, row4, row5];
|
|
2542
|
+
}
|
|
2543
|
+
/** Parse known_y / known_x from args, returning design matrix and orientation info. */
|
|
2544
|
+
function parseRegressionInputs(args) {
|
|
2545
|
+
if (!args[0]) {
|
|
2546
|
+
return values_1.ERRORS.VALUE;
|
|
2547
|
+
}
|
|
2548
|
+
const yInfo = extractMatrix(args[0]);
|
|
2549
|
+
if (isErr(yInfo)) {
|
|
2550
|
+
return yInfo;
|
|
2551
|
+
}
|
|
2552
|
+
const y = [];
|
|
2553
|
+
for (const row of yInfo.m.data) {
|
|
2554
|
+
for (const v of row) {
|
|
2555
|
+
y.push(v);
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
const nObs = y.length;
|
|
2559
|
+
if (nObs < 1) {
|
|
2560
|
+
return values_1.ERRORS.VALUE;
|
|
2561
|
+
}
|
|
2562
|
+
if (args.length > 1 && args[1].kind !== 0 /* RVKind.Blank */) {
|
|
2563
|
+
const xInfo = extractMatrix(args[1]);
|
|
2564
|
+
if (isErr(xInfo)) {
|
|
2565
|
+
return xInfo;
|
|
2566
|
+
}
|
|
2567
|
+
const built = buildDesignMatrix(xInfo.m, nObs, yInfo.orient);
|
|
2568
|
+
if (isErr(built)) {
|
|
2569
|
+
return built;
|
|
2570
|
+
}
|
|
2571
|
+
return { y, X: built.X, k: built.k, yOrient: yInfo.orient, xOrient: built.xOrient };
|
|
2572
|
+
}
|
|
2573
|
+
// Default known_x's = 1..n as a column vector
|
|
2574
|
+
const X = y.map((_, i) => [i + 1]);
|
|
2575
|
+
return { y, X, k: 1, yOrient: yInfo.orient, xOrient: yInfo.orient };
|
|
2576
|
+
}
|
|
2577
|
+
/** Parse new_x (third arg of TREND/GROWTH). Returns new-X matrix [m, k] and output orientation. */
|
|
2578
|
+
function parseNewX(arg, fallback, k, xOrient) {
|
|
2579
|
+
if (!arg || arg.kind === 0 /* RVKind.Blank */) {
|
|
2580
|
+
return { X: fallback, outOrient: xOrient };
|
|
2581
|
+
}
|
|
2582
|
+
const info = extractMatrix(arg);
|
|
2583
|
+
if (isErr(info)) {
|
|
2584
|
+
return info;
|
|
2585
|
+
}
|
|
2586
|
+
const m = info.m;
|
|
2587
|
+
// Determine orientation: need one of the dimensions to equal k (the number of predictors)
|
|
2588
|
+
if (k === 1) {
|
|
2589
|
+
// Single predictor → flatten whatever shape was given
|
|
2590
|
+
const X = [];
|
|
2591
|
+
for (const row of m.data) {
|
|
2592
|
+
for (const v of row) {
|
|
2593
|
+
X.push([v]);
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
const outOrient = m.rows === 1 ? "row" : "col";
|
|
2597
|
+
return { X, outOrient };
|
|
2598
|
+
}
|
|
2599
|
+
// Multi-predictor: match orientation with known_x's
|
|
2600
|
+
if (xOrient === "col" || xOrient === "matrix") {
|
|
2601
|
+
// Each row is one observation with k columns of predictors
|
|
2602
|
+
if (m.cols !== k) {
|
|
2603
|
+
return values_1.ERRORS.REF;
|
|
2604
|
+
}
|
|
2605
|
+
return { X: m.data.map(r => r.slice()), outOrient: "col" };
|
|
2606
|
+
}
|
|
2607
|
+
// xOrient === "row": each column is one observation
|
|
2608
|
+
if (m.rows !== k) {
|
|
2609
|
+
return values_1.ERRORS.REF;
|
|
2610
|
+
}
|
|
2611
|
+
const X = [];
|
|
2612
|
+
for (let c = 0; c < m.cols; c++) {
|
|
2613
|
+
const row = [];
|
|
2614
|
+
for (let r = 0; r < k; r++) {
|
|
2615
|
+
row.push(m.data[r][c]);
|
|
2616
|
+
}
|
|
2617
|
+
X.push(row);
|
|
2618
|
+
}
|
|
2619
|
+
return { X, outOrient: "row" };
|
|
2620
|
+
}
|
|
2621
|
+
/** Emit an array of predictions matching the requested output orientation. */
|
|
2622
|
+
function emitPredictions(values, outOrient) {
|
|
2623
|
+
if (outOrient === "row") {
|
|
2624
|
+
return (0, values_1.rvArray)([values.map(v => (0, values_1.rvNumber)(v))]);
|
|
2625
|
+
}
|
|
2626
|
+
return (0, values_1.rvArray)(values.map(v => [(0, values_1.rvNumber)(v)]));
|
|
2627
|
+
}
|
|
2628
|
+
const fnGROWTH = args => {
|
|
2629
|
+
const parsed = parseRegressionInputs(args);
|
|
2630
|
+
if (isErr(parsed)) {
|
|
2631
|
+
return parsed;
|
|
2632
|
+
}
|
|
2633
|
+
const { y, X, k, xOrient } = parsed;
|
|
2634
|
+
const lsq = runLeastSquares({ y, X, includeIntercept: true, logMode: true }, false);
|
|
2635
|
+
if (isErr(lsq)) {
|
|
2636
|
+
return lsq;
|
|
2637
|
+
}
|
|
2638
|
+
// coeffs order: [mk, ..., m1, b]; slopes in log space correspond to positions [0..k-1]
|
|
2639
|
+
const b = lsq.coeffs[k];
|
|
2640
|
+
const slopes = new Array(k);
|
|
2641
|
+
for (let j = 0; j < k; j++) {
|
|
2642
|
+
slopes[j] = lsq.coeffs[k - 1 - j];
|
|
2643
|
+
} // m1..mk
|
|
2644
|
+
const newInfo = parseNewX(args[2], X, k, xOrient);
|
|
2645
|
+
if (isErr(newInfo)) {
|
|
2646
|
+
return newInfo;
|
|
2647
|
+
}
|
|
2648
|
+
const preds = newInfo.X.map(row => {
|
|
2649
|
+
let lp = b;
|
|
2650
|
+
for (let j = 0; j < k; j++) {
|
|
2651
|
+
lp += slopes[j] * row[j];
|
|
2652
|
+
}
|
|
2653
|
+
return Math.exp(lp);
|
|
2654
|
+
});
|
|
2655
|
+
return emitPredictions(preds, newInfo.outOrient);
|
|
2656
|
+
};
|
|
2657
|
+
exports.fnGROWTH = fnGROWTH;
|
|
2658
|
+
const fnTREND = args => {
|
|
2659
|
+
const parsed = parseRegressionInputs(args);
|
|
2660
|
+
if (isErr(parsed)) {
|
|
2661
|
+
return parsed;
|
|
2662
|
+
}
|
|
2663
|
+
const { y, X, k, xOrient } = parsed;
|
|
2664
|
+
const lsq = runLeastSquares({ y, X, includeIntercept: true, logMode: false }, false);
|
|
2665
|
+
if (isErr(lsq)) {
|
|
2666
|
+
return lsq;
|
|
2667
|
+
}
|
|
2668
|
+
const b = lsq.coeffs[k];
|
|
2669
|
+
const slopes = new Array(k);
|
|
2670
|
+
for (let j = 0; j < k; j++) {
|
|
2671
|
+
slopes[j] = lsq.coeffs[k - 1 - j];
|
|
2672
|
+
}
|
|
2673
|
+
const newInfo = parseNewX(args[2], X, k, xOrient);
|
|
2674
|
+
if (isErr(newInfo)) {
|
|
2675
|
+
return newInfo;
|
|
2676
|
+
}
|
|
2677
|
+
const preds = newInfo.X.map(row => {
|
|
2678
|
+
let p = b;
|
|
2679
|
+
for (let j = 0; j < k; j++) {
|
|
2680
|
+
p += slopes[j] * row[j];
|
|
2681
|
+
}
|
|
2682
|
+
return p;
|
|
2683
|
+
});
|
|
2684
|
+
return emitPredictions(preds, newInfo.outOrient);
|
|
2685
|
+
};
|
|
2686
|
+
exports.fnTREND = fnTREND;
|
|
2687
|
+
const fnLINEST = args => {
|
|
2688
|
+
const parsed = parseRegressionInputs(args);
|
|
2689
|
+
if (isErr(parsed)) {
|
|
2690
|
+
return parsed;
|
|
2691
|
+
}
|
|
2692
|
+
const { y, X } = parsed;
|
|
2693
|
+
// 3rd arg: const (default TRUE — include intercept). FALSE → force intercept = 0.
|
|
2694
|
+
let includeIntercept = true;
|
|
2695
|
+
if (args.length > 2 && args[2].kind !== 0 /* RVKind.Blank */) {
|
|
2696
|
+
const b = (0, values_1.toBooleanRV)((0, values_1.topLeft)(args[2]));
|
|
2697
|
+
if (b.kind === 4 /* RVKind.Error */) {
|
|
2698
|
+
return b;
|
|
2699
|
+
}
|
|
2700
|
+
includeIntercept = b.value;
|
|
2701
|
+
}
|
|
2702
|
+
// 4th arg: stats (default FALSE).
|
|
2703
|
+
let withStats = false;
|
|
2704
|
+
if (args.length > 3 && args[3].kind !== 0 /* RVKind.Blank */) {
|
|
2705
|
+
const b = (0, values_1.toBooleanRV)((0, values_1.topLeft)(args[3]));
|
|
2706
|
+
if (b.kind === 4 /* RVKind.Error */) {
|
|
2707
|
+
return b;
|
|
2708
|
+
}
|
|
2709
|
+
withStats = b.value;
|
|
2710
|
+
}
|
|
2711
|
+
const lsq = runLeastSquares({ y, X, includeIntercept, logMode: false }, withStats);
|
|
2712
|
+
if (isErr(lsq)) {
|
|
2713
|
+
return lsq;
|
|
2714
|
+
}
|
|
2715
|
+
if (withStats) {
|
|
2716
|
+
return (0, values_1.rvArray)(buildStatsBlock(lsq));
|
|
2717
|
+
}
|
|
2718
|
+
return (0, values_1.rvArray)([lsq.coeffs.map(v => (0, values_1.rvNumber)(v))]);
|
|
2719
|
+
};
|
|
2720
|
+
exports.fnLINEST = fnLINEST;
|
|
2721
|
+
const fnLOGEST = args => {
|
|
2722
|
+
const parsed = parseRegressionInputs(args);
|
|
2723
|
+
if (isErr(parsed)) {
|
|
2724
|
+
return parsed;
|
|
2725
|
+
}
|
|
2726
|
+
const { y, X } = parsed;
|
|
2727
|
+
let includeIntercept = true;
|
|
2728
|
+
if (args.length > 2 && args[2].kind !== 0 /* RVKind.Blank */) {
|
|
2729
|
+
const b = (0, values_1.toBooleanRV)((0, values_1.topLeft)(args[2]));
|
|
2730
|
+
if (b.kind === 4 /* RVKind.Error */) {
|
|
2731
|
+
return b;
|
|
2732
|
+
}
|
|
2733
|
+
includeIntercept = b.value;
|
|
2734
|
+
}
|
|
2735
|
+
let withStats = false;
|
|
2736
|
+
if (args.length > 3 && args[3].kind !== 0 /* RVKind.Blank */) {
|
|
2737
|
+
const b = (0, values_1.toBooleanRV)((0, values_1.topLeft)(args[3]));
|
|
2738
|
+
if (b.kind === 4 /* RVKind.Error */) {
|
|
2739
|
+
return b;
|
|
2740
|
+
}
|
|
2741
|
+
withStats = b.value;
|
|
2742
|
+
}
|
|
2743
|
+
const lsq = runLeastSquares({ y, X, includeIntercept, logMode: true }, withStats);
|
|
2744
|
+
if (isErr(lsq)) {
|
|
2745
|
+
return lsq;
|
|
2746
|
+
}
|
|
2747
|
+
if (withStats) {
|
|
2748
|
+
// LOGEST reports exp-transformed slopes/intercept in row 1 but keeps rows 2-5 as LINEST
|
|
2749
|
+
const block = buildStatsBlock(lsq);
|
|
2750
|
+
block[0] = lsq.coeffs.map(v => (0, values_1.rvNumber)(Math.exp(v)));
|
|
2751
|
+
return (0, values_1.rvArray)(block);
|
|
2752
|
+
}
|
|
2753
|
+
return (0, values_1.rvArray)([lsq.coeffs.map(v => (0, values_1.rvNumber)(Math.exp(v)))]);
|
|
2754
|
+
};
|
|
2755
|
+
exports.fnLOGEST = fnLOGEST;
|
|
2756
|
+
// ============================================================================
|
|
2757
|
+
// F.DIST.RT, F.INV.RT — F-distribution right tail
|
|
2758
|
+
// ============================================================================
|
|
2759
|
+
/**
|
|
2760
|
+
* F.DIST.RT(x, d1, d2) — right-tail probability of the F-distribution.
|
|
2761
|
+
*
|
|
2762
|
+
* Equivalent to `1 - F.DIST(x, d1, d2, TRUE)`. Using the symmetry of the
|
|
2763
|
+
* regularized incomplete beta function, this can be expressed as
|
|
2764
|
+
* `I(d2/(d2 + d1*x), d2/2, d1/2)`, which avoids subtracting from 1 and
|
|
2765
|
+
* is numerically stable in the upper tail.
|
|
2766
|
+
*/
|
|
2767
|
+
const fnF_DIST_RT = args => {
|
|
2768
|
+
const x = (0, _shared_1.argToNumber)(args[0]);
|
|
2769
|
+
if (x.kind === 4 /* RVKind.Error */) {
|
|
2770
|
+
return x;
|
|
2771
|
+
}
|
|
2772
|
+
const df1 = (0, _shared_1.argToNumber)(args[1]);
|
|
2773
|
+
if (df1.kind === 4 /* RVKind.Error */) {
|
|
2774
|
+
return df1;
|
|
2775
|
+
}
|
|
2776
|
+
const df2 = (0, _shared_1.argToNumber)(args[2]);
|
|
2777
|
+
if (df2.kind === 4 /* RVKind.Error */) {
|
|
2778
|
+
return df2;
|
|
2779
|
+
}
|
|
2780
|
+
if (x.value < 0 || df1.value < 1 || df2.value < 1) {
|
|
2781
|
+
return values_1.ERRORS.NUM;
|
|
2782
|
+
}
|
|
2783
|
+
// At x=0 the right tail equals 1 (entire distribution above 0).
|
|
2784
|
+
if (x.value === 0) {
|
|
2785
|
+
return (0, values_1.rvNumber)(1);
|
|
2786
|
+
}
|
|
2787
|
+
const d1 = df1.value;
|
|
2788
|
+
const d2 = df2.value;
|
|
2789
|
+
// Right tail via symmetry: I(d2/(d2 + d1*x), d2/2, d1/2).
|
|
2790
|
+
return (0, values_1.rvNumber)(betaIncomplete(d2 / (d2 + d1 * x.value), d2 / 2, d1 / 2));
|
|
2791
|
+
};
|
|
2792
|
+
exports.fnF_DIST_RT = fnF_DIST_RT;
|
|
2793
|
+
/**
|
|
2794
|
+
* F.INV.RT(p, d1, d2) — inverse right-tail of the F-distribution.
|
|
2795
|
+
* Returns x such that P(F > x) = p. Implemented via binary search on the
|
|
2796
|
+
* right-tail CDF (monotonically decreasing from 1 at x=0 to 0 at x=∞).
|
|
2797
|
+
*/
|
|
2798
|
+
const fnF_INV_RT = args => {
|
|
2799
|
+
const p = (0, _shared_1.argToNumber)(args[0]);
|
|
2800
|
+
if (p.kind === 4 /* RVKind.Error */) {
|
|
2801
|
+
return p;
|
|
2802
|
+
}
|
|
2803
|
+
const df1 = (0, _shared_1.argToNumber)(args[1]);
|
|
2804
|
+
if (df1.kind === 4 /* RVKind.Error */) {
|
|
2805
|
+
return df1;
|
|
2806
|
+
}
|
|
2807
|
+
const df2 = (0, _shared_1.argToNumber)(args[2]);
|
|
2808
|
+
if (df2.kind === 4 /* RVKind.Error */) {
|
|
2809
|
+
return df2;
|
|
2810
|
+
}
|
|
2811
|
+
if (p.value <= 0 || p.value > 1 || df1.value < 1 || df2.value < 1) {
|
|
2812
|
+
return values_1.ERRORS.NUM;
|
|
2813
|
+
}
|
|
2814
|
+
const d1 = df1.value;
|
|
2815
|
+
const d2 = df2.value;
|
|
2816
|
+
// Right-tail CDF at x: I(d2/(d2 + d1*x), d2/2, d1/2), decreases with x.
|
|
2817
|
+
let lo = 0;
|
|
2818
|
+
let hi = 1e6;
|
|
2819
|
+
for (let i = 0; i < 200; i++) {
|
|
2820
|
+
const mid = (lo + hi) / 2;
|
|
2821
|
+
const rt = betaIncomplete(d2 / (d2 + d1 * mid), d2 / 2, d1 / 2);
|
|
2822
|
+
if (rt > p.value) {
|
|
2823
|
+
lo = mid;
|
|
2824
|
+
}
|
|
2825
|
+
else {
|
|
2826
|
+
hi = mid;
|
|
2827
|
+
}
|
|
2828
|
+
if (hi - lo < 1e-10) {
|
|
2829
|
+
break;
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
return (0, values_1.rvNumber)((lo + hi) / 2);
|
|
2833
|
+
};
|
|
2834
|
+
exports.fnF_INV_RT = fnF_INV_RT;
|
|
2835
|
+
// ============================================================================
|
|
2836
|
+
// SKEW, SKEW.P, KURT
|
|
2837
|
+
// ============================================================================
|
|
2838
|
+
/**
|
|
2839
|
+
* SKEW — sample skewness.
|
|
2840
|
+
* Formula: n / ((n-1)(n-2)) * Σ((xi-mean)/s)^3, where s is the sample stdev.
|
|
2841
|
+
*/
|
|
2842
|
+
const fnSKEW = args => {
|
|
2843
|
+
const nums = (0, _shared_1.flattenNumbers)(args);
|
|
2844
|
+
const err = (0, _shared_1.firstError)(nums);
|
|
2845
|
+
if (err) {
|
|
2846
|
+
return err;
|
|
2847
|
+
}
|
|
2848
|
+
const xs = nums.map(n => n.value);
|
|
2849
|
+
const n = xs.length;
|
|
2850
|
+
if (n < 3) {
|
|
2851
|
+
return values_1.ERRORS.DIV0;
|
|
2852
|
+
}
|
|
2853
|
+
let sum = 0;
|
|
2854
|
+
for (const v of xs) {
|
|
2855
|
+
sum += v;
|
|
2856
|
+
}
|
|
2857
|
+
const mean = sum / n;
|
|
2858
|
+
let sumSq = 0;
|
|
2859
|
+
for (const v of xs) {
|
|
2860
|
+
sumSq += (v - mean) ** 2;
|
|
2861
|
+
}
|
|
2862
|
+
const sampleStd = Math.sqrt(sumSq / (n - 1));
|
|
2863
|
+
if (sampleStd === 0) {
|
|
2864
|
+
return values_1.ERRORS.DIV0;
|
|
2865
|
+
}
|
|
2866
|
+
let sumCubed = 0;
|
|
2867
|
+
for (const v of xs) {
|
|
2868
|
+
sumCubed += ((v - mean) / sampleStd) ** 3;
|
|
2869
|
+
}
|
|
2870
|
+
return (0, values_1.rvNumber)((n / ((n - 1) * (n - 2))) * sumCubed);
|
|
2871
|
+
};
|
|
2872
|
+
exports.fnSKEW = fnSKEW;
|
|
2873
|
+
/**
|
|
2874
|
+
* SKEW.P — population skewness.
|
|
2875
|
+
* Formula: (1/n) * Σ((xi-mean)/σ)^3, where σ is the population stdev.
|
|
2876
|
+
*/
|
|
2877
|
+
const fnSKEW_P = args => {
|
|
2878
|
+
const nums = (0, _shared_1.flattenNumbers)(args);
|
|
2879
|
+
const err = (0, _shared_1.firstError)(nums);
|
|
2880
|
+
if (err) {
|
|
2881
|
+
return err;
|
|
2882
|
+
}
|
|
2883
|
+
const xs = nums.map(n => n.value);
|
|
2884
|
+
const n = xs.length;
|
|
2885
|
+
if (n < 1) {
|
|
2886
|
+
return values_1.ERRORS.DIV0;
|
|
2887
|
+
}
|
|
2888
|
+
let sum = 0;
|
|
2889
|
+
for (const v of xs) {
|
|
2890
|
+
sum += v;
|
|
2891
|
+
}
|
|
2892
|
+
const mean = sum / n;
|
|
2893
|
+
let sumSq = 0;
|
|
2894
|
+
for (const v of xs) {
|
|
2895
|
+
sumSq += (v - mean) ** 2;
|
|
2896
|
+
}
|
|
2897
|
+
const popStd = Math.sqrt(sumSq / n);
|
|
2898
|
+
if (popStd === 0) {
|
|
2899
|
+
return values_1.ERRORS.DIV0;
|
|
2900
|
+
}
|
|
2901
|
+
let sumCubed = 0;
|
|
2902
|
+
for (const v of xs) {
|
|
2903
|
+
sumCubed += ((v - mean) / popStd) ** 3;
|
|
2904
|
+
}
|
|
2905
|
+
return (0, values_1.rvNumber)(sumCubed / n);
|
|
2906
|
+
};
|
|
2907
|
+
exports.fnSKEW_P = fnSKEW_P;
|
|
2908
|
+
/**
|
|
2909
|
+
* KURT — sample excess kurtosis.
|
|
2910
|
+
* Formula: n(n+1) / ((n-1)(n-2)(n-3)) * Σ((xi-mean)/s)^4 - 3(n-1)^2 / ((n-2)(n-3)).
|
|
2911
|
+
*/
|
|
2912
|
+
const fnKURT = args => {
|
|
2913
|
+
const nums = (0, _shared_1.flattenNumbers)(args);
|
|
2914
|
+
const err = (0, _shared_1.firstError)(nums);
|
|
2915
|
+
if (err) {
|
|
2916
|
+
return err;
|
|
2917
|
+
}
|
|
2918
|
+
const xs = nums.map(n => n.value);
|
|
2919
|
+
const n = xs.length;
|
|
2920
|
+
if (n < 4) {
|
|
2921
|
+
return values_1.ERRORS.DIV0;
|
|
2922
|
+
}
|
|
2923
|
+
let sum = 0;
|
|
2924
|
+
for (const v of xs) {
|
|
2925
|
+
sum += v;
|
|
2926
|
+
}
|
|
2927
|
+
const mean = sum / n;
|
|
2928
|
+
let sumSq = 0;
|
|
2929
|
+
for (const v of xs) {
|
|
2930
|
+
sumSq += (v - mean) ** 2;
|
|
2931
|
+
}
|
|
2932
|
+
const sampleStd = Math.sqrt(sumSq / (n - 1));
|
|
2933
|
+
if (sampleStd === 0) {
|
|
2934
|
+
return values_1.ERRORS.DIV0;
|
|
2935
|
+
}
|
|
2936
|
+
let sumQuad = 0;
|
|
2937
|
+
for (const v of xs) {
|
|
2938
|
+
sumQuad += ((v - mean) / sampleStd) ** 4;
|
|
2939
|
+
}
|
|
2940
|
+
const term1 = (n * (n + 1)) / ((n - 1) * (n - 2) * (n - 3));
|
|
2941
|
+
const term2 = (3 * (n - 1) ** 2) / ((n - 2) * (n - 3));
|
|
2942
|
+
return (0, values_1.rvNumber)(term1 * sumQuad - term2);
|
|
2943
|
+
};
|
|
2944
|
+
exports.fnKURT = fnKURT;
|
|
2945
|
+
// ============================================================================
|
|
2946
|
+
// PERCENTRANK family
|
|
2947
|
+
// ============================================================================
|
|
2948
|
+
/**
|
|
2949
|
+
* Compute PERCENTRANK's significance-truncated result.
|
|
2950
|
+
*
|
|
2951
|
+
* Excel's `significance` truncates the return value to that many
|
|
2952
|
+
* digits — `significance=3` (default) yields 0.123 rather than
|
|
2953
|
+
* 0.12345. It is a display truncation, not a rounding, so we
|
|
2954
|
+
* implement it by `floor(value * 10^n) / 10^n`.
|
|
2955
|
+
*/
|
|
2956
|
+
function truncateToSignificance(value, significance) {
|
|
2957
|
+
if (significance < 1) {
|
|
2958
|
+
return Number.NaN;
|
|
2959
|
+
}
|
|
2960
|
+
// Significance must be an integer; Excel truncates toward zero.
|
|
2961
|
+
const n = Math.trunc(significance);
|
|
2962
|
+
const scale = Math.pow(10, n);
|
|
2963
|
+
return Math.floor(value * scale) / scale;
|
|
2964
|
+
}
|
|
2965
|
+
/**
|
|
2966
|
+
* Compute the PERCENTRANK.INC or PERCENTRANK.EXC value.
|
|
2967
|
+
*
|
|
2968
|
+
* For PERCENTRANK.INC (inclusive):
|
|
2969
|
+
* rank = i / (n - 1) when x is at sorted index i (0-based, exact match),
|
|
2970
|
+
* or linear interpolation between the two bracketing values.
|
|
2971
|
+
* x < min or x > max → #N/A.
|
|
2972
|
+
*
|
|
2973
|
+
* For PERCENTRANK.EXC (exclusive):
|
|
2974
|
+
* rank = (i + 1) / (n + 1) when x is at sorted index i (0-based, exact match).
|
|
2975
|
+
* x outside [min, max] or rank outside [1/(n+1), n/(n+1)] → #N/A.
|
|
2976
|
+
*/
|
|
2977
|
+
function computePercentRank(sorted, x, inclusive) {
|
|
2978
|
+
const n = sorted.length;
|
|
2979
|
+
if (n === 0) {
|
|
2980
|
+
return null;
|
|
2981
|
+
}
|
|
2982
|
+
// Exact match: find the first index where sorted[i] === x.
|
|
2983
|
+
// Binary search for lower bound.
|
|
2984
|
+
let lo = 0;
|
|
2985
|
+
let hi = n - 1;
|
|
2986
|
+
let found = -1;
|
|
2987
|
+
while (lo <= hi) {
|
|
2988
|
+
const mid = (lo + hi) >>> 1;
|
|
2989
|
+
if (sorted[mid] === x) {
|
|
2990
|
+
// Find first occurrence (ties go to earliest index).
|
|
2991
|
+
let i = mid;
|
|
2992
|
+
while (i > 0 && sorted[i - 1] === x) {
|
|
2993
|
+
i--;
|
|
2994
|
+
}
|
|
2995
|
+
found = i;
|
|
2996
|
+
break;
|
|
2997
|
+
}
|
|
2998
|
+
if (sorted[mid] < x) {
|
|
2999
|
+
lo = mid + 1;
|
|
3000
|
+
}
|
|
3001
|
+
else {
|
|
3002
|
+
hi = mid - 1;
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
if (found >= 0) {
|
|
3006
|
+
if (inclusive) {
|
|
3007
|
+
return n === 1 ? 1 : found / (n - 1);
|
|
3008
|
+
}
|
|
3009
|
+
return (found + 1) / (n + 1);
|
|
3010
|
+
}
|
|
3011
|
+
// Interpolation case — x is strictly between sorted[hi] and sorted[lo].
|
|
3012
|
+
// After the loop, lo = insertion point; hi = lo - 1.
|
|
3013
|
+
if (hi < 0 || lo >= n) {
|
|
3014
|
+
return null; // out of range
|
|
3015
|
+
}
|
|
3016
|
+
// Linear interpolation between the two neighbors.
|
|
3017
|
+
const lower = sorted[hi];
|
|
3018
|
+
const upper = sorted[lo];
|
|
3019
|
+
if (upper === lower) {
|
|
3020
|
+
// Degenerate — shouldn't happen given the exact-match check above.
|
|
3021
|
+
return null;
|
|
3022
|
+
}
|
|
3023
|
+
const fraction = (x - lower) / (upper - lower);
|
|
3024
|
+
if (inclusive) {
|
|
3025
|
+
if (n === 1) {
|
|
3026
|
+
return 1;
|
|
3027
|
+
}
|
|
3028
|
+
return (hi + fraction) / (n - 1);
|
|
3029
|
+
}
|
|
3030
|
+
const rank = (hi + 1 + fraction) / (n + 1);
|
|
3031
|
+
// PERCENTRANK.EXC return range is [1/(n+1), n/(n+1)].
|
|
3032
|
+
const minR = 1 / (n + 1);
|
|
3033
|
+
const maxR = n / (n + 1);
|
|
3034
|
+
if (rank < minR || rank > maxR) {
|
|
3035
|
+
return null;
|
|
3036
|
+
}
|
|
3037
|
+
return rank;
|
|
3038
|
+
}
|
|
3039
|
+
function fnPercentRankImpl(args, inclusive) {
|
|
3040
|
+
if (args.length < 2) {
|
|
3041
|
+
return values_1.ERRORS.VALUE;
|
|
3042
|
+
}
|
|
3043
|
+
const vals = (0, _shared_1.flattenNumbers)([args[0]]);
|
|
3044
|
+
const err = (0, _shared_1.firstError)(vals);
|
|
3045
|
+
if (err) {
|
|
3046
|
+
return err;
|
|
3047
|
+
}
|
|
3048
|
+
const xV = (0, values_1.toNumberRV)((0, values_1.topLeft)(args[1]));
|
|
3049
|
+
if ((0, values_1.isError)(xV)) {
|
|
3050
|
+
return xV;
|
|
3051
|
+
}
|
|
3052
|
+
const significanceV = args.length > 2 ? (0, values_1.toNumberRV)((0, values_1.topLeft)(args[2])) : (0, values_1.rvNumber)(3);
|
|
3053
|
+
if ((0, values_1.isError)(significanceV)) {
|
|
3054
|
+
return significanceV;
|
|
3055
|
+
}
|
|
3056
|
+
const significance = significanceV.value;
|
|
3057
|
+
if (significance < 1) {
|
|
3058
|
+
return values_1.ERRORS.NUM;
|
|
3059
|
+
}
|
|
3060
|
+
const nums = vals.map(v => v.value);
|
|
3061
|
+
if (nums.length === 0) {
|
|
3062
|
+
return values_1.ERRORS.NUM;
|
|
3063
|
+
}
|
|
3064
|
+
const sorted = [...nums].sort((a, b) => a - b);
|
|
3065
|
+
const rank = computePercentRank(sorted, xV.value, inclusive);
|
|
3066
|
+
if (rank === null) {
|
|
3067
|
+
return values_1.ERRORS.NA;
|
|
3068
|
+
}
|
|
3069
|
+
const truncated = truncateToSignificance(rank, significance);
|
|
3070
|
+
if (Number.isNaN(truncated)) {
|
|
3071
|
+
return values_1.ERRORS.NUM;
|
|
3072
|
+
}
|
|
3073
|
+
return (0, values_1.rvNumber)(truncated);
|
|
3074
|
+
}
|
|
3075
|
+
const fnPERCENTRANK = args => fnPercentRankImpl(args, true);
|
|
3076
|
+
exports.fnPERCENTRANK = fnPERCENTRANK;
|
|
3077
|
+
const fnPERCENTRANK_INC = args => fnPercentRankImpl(args, true);
|
|
3078
|
+
exports.fnPERCENTRANK_INC = fnPERCENTRANK_INC;
|
|
3079
|
+
const fnPERCENTRANK_EXC = args => fnPercentRankImpl(args, false);
|
|
3080
|
+
exports.fnPERCENTRANK_EXC = fnPERCENTRANK_EXC;
|
|
3081
|
+
// ============================================================================
|
|
3082
|
+
// PROB — probability that values in X-range are between two limits
|
|
3083
|
+
// ============================================================================
|
|
3084
|
+
/**
|
|
3085
|
+
* PROB(x_range, prob_range, lower_limit, [upper_limit]) — probability that
|
|
3086
|
+
* values in x_range are between lower_limit and upper_limit inclusive.
|
|
3087
|
+
*
|
|
3088
|
+
* Excel rules:
|
|
3089
|
+
* - x_range and prob_range must have the same dimensions
|
|
3090
|
+
* - prob_range entries must sum to 1 (±ε); otherwise #NUM!
|
|
3091
|
+
* - Any prob entry ≤ 0 or > 1 → #NUM!
|
|
3092
|
+
* - upper_limit omitted → probability that x = lower_limit
|
|
3093
|
+
* - Result is the sum of prob_range entries for which
|
|
3094
|
+
* lower_limit ≤ x_range value ≤ upper_limit
|
|
3095
|
+
*/
|
|
3096
|
+
const fnPROB = args => {
|
|
3097
|
+
if (args.length < 3 || args.length > 4) {
|
|
3098
|
+
return values_1.ERRORS.VALUE;
|
|
3099
|
+
}
|
|
3100
|
+
const xVals = (0, _shared_1.flattenNumbers)([args[0]]);
|
|
3101
|
+
const xErr = (0, _shared_1.firstError)(xVals);
|
|
3102
|
+
if (xErr) {
|
|
3103
|
+
return xErr;
|
|
3104
|
+
}
|
|
3105
|
+
const pVals = (0, _shared_1.flattenNumbers)([args[1]]);
|
|
3106
|
+
const pErr = (0, _shared_1.firstError)(pVals);
|
|
3107
|
+
if (pErr) {
|
|
3108
|
+
return pErr;
|
|
3109
|
+
}
|
|
3110
|
+
if (xVals.length !== pVals.length) {
|
|
3111
|
+
return values_1.ERRORS.NA;
|
|
3112
|
+
}
|
|
3113
|
+
if (xVals.length === 0) {
|
|
3114
|
+
return values_1.ERRORS.NUM;
|
|
3115
|
+
}
|
|
3116
|
+
const lowerV = (0, values_1.toNumberRV)((0, values_1.topLeft)(args[2]));
|
|
3117
|
+
if ((0, values_1.isError)(lowerV)) {
|
|
3118
|
+
return lowerV;
|
|
3119
|
+
}
|
|
3120
|
+
const upperV = args.length > 3 ? (0, values_1.toNumberRV)((0, values_1.topLeft)(args[3])) : lowerV;
|
|
3121
|
+
if ((0, values_1.isError)(upperV)) {
|
|
3122
|
+
return upperV;
|
|
3123
|
+
}
|
|
3124
|
+
const lower = lowerV.value;
|
|
3125
|
+
const upper = upperV.value;
|
|
3126
|
+
if (lower > upper) {
|
|
3127
|
+
return values_1.ERRORS.NUM;
|
|
3128
|
+
}
|
|
3129
|
+
// Probabilities must be in (0, 1] and sum to 1.
|
|
3130
|
+
let total = 0;
|
|
3131
|
+
for (const p of pVals) {
|
|
3132
|
+
const pv = p.value;
|
|
3133
|
+
if (pv <= 0 || pv > 1) {
|
|
3134
|
+
return values_1.ERRORS.NUM;
|
|
3135
|
+
}
|
|
3136
|
+
total += pv;
|
|
3137
|
+
}
|
|
3138
|
+
// Excel's tolerance is fairly loose — well within float noise.
|
|
3139
|
+
if (Math.abs(total - 1) > 1e-9) {
|
|
3140
|
+
return values_1.ERRORS.NUM;
|
|
3141
|
+
}
|
|
3142
|
+
let result = 0;
|
|
3143
|
+
for (let i = 0; i < xVals.length; i++) {
|
|
3144
|
+
const xv = xVals[i].value;
|
|
3145
|
+
if (xv >= lower && xv <= upper) {
|
|
3146
|
+
result += pVals[i].value;
|
|
3147
|
+
}
|
|
3148
|
+
}
|
|
3149
|
+
return (0, values_1.rvNumber)(result);
|
|
3150
|
+
};
|
|
3151
|
+
exports.fnPROB = fnPROB;
|
|
3152
|
+
// ============================================================================
|
|
3153
|
+
// Z.TEST, T.TEST, F.TEST, CHISQ.TEST — hypothesis tests
|
|
3154
|
+
// ============================================================================
|
|
3155
|
+
/**
|
|
3156
|
+
* Standard normal CDF — shared by Z.TEST and the T.TEST approximations.
|
|
3157
|
+
* Uses the same erf-based formula as fnNORMSDIST.
|
|
3158
|
+
*/
|
|
3159
|
+
function normalCDF(z) {
|
|
3160
|
+
// Abramowitz & Stegun 7.1.26 approximation, same family used elsewhere.
|
|
3161
|
+
const sign = z < 0 ? -1 : 1;
|
|
3162
|
+
const x = Math.abs(z) / Math.sqrt(2);
|
|
3163
|
+
const t = 1 / (1 + 0.3275911 * x);
|
|
3164
|
+
const y = 1 -
|
|
3165
|
+
((((1.061405429 * t - 1.453152027) * t + 1.421413741) * t - 0.284496736) * t + 0.254829592) *
|
|
3166
|
+
t *
|
|
3167
|
+
Math.exp(-x * x);
|
|
3168
|
+
return 0.5 * (1 + sign * y);
|
|
3169
|
+
}
|
|
3170
|
+
/**
|
|
3171
|
+
* Z.TEST(array, x, [sigma]) — one-tailed probability value for z-test.
|
|
3172
|
+
* mean = AVERAGE(array), n = COUNT(array)
|
|
3173
|
+
* sigma = provided OR sample standard deviation of array
|
|
3174
|
+
* z = (mean - x) / (sigma / √n)
|
|
3175
|
+
* Z.TEST = 1 - NORM.S.DIST(z, TRUE)
|
|
3176
|
+
*/
|
|
3177
|
+
const fnZ_TEST = args => {
|
|
3178
|
+
const vals = (0, _shared_1.flattenNumbers)([args[0]]);
|
|
3179
|
+
const err = (0, _shared_1.firstError)(vals);
|
|
3180
|
+
if (err) {
|
|
3181
|
+
return err;
|
|
3182
|
+
}
|
|
3183
|
+
const xV = (0, values_1.toNumberRV)((0, values_1.topLeft)(args[1]));
|
|
3184
|
+
if ((0, values_1.isError)(xV)) {
|
|
3185
|
+
return xV;
|
|
3186
|
+
}
|
|
3187
|
+
const nums = vals.map(v => v.value);
|
|
3188
|
+
if (nums.length === 0) {
|
|
3189
|
+
return values_1.ERRORS.NA;
|
|
3190
|
+
}
|
|
3191
|
+
const mean = nums.reduce((s, v) => s + v, 0) / nums.length;
|
|
3192
|
+
let sigma;
|
|
3193
|
+
if (args.length > 2) {
|
|
3194
|
+
const sigmaV = (0, values_1.toNumberRV)((0, values_1.topLeft)(args[2]));
|
|
3195
|
+
if ((0, values_1.isError)(sigmaV)) {
|
|
3196
|
+
return sigmaV;
|
|
3197
|
+
}
|
|
3198
|
+
if (sigmaV.value <= 0) {
|
|
3199
|
+
return values_1.ERRORS.NUM;
|
|
3200
|
+
}
|
|
3201
|
+
sigma = sigmaV.value;
|
|
3202
|
+
}
|
|
3203
|
+
else {
|
|
3204
|
+
// Sample std dev.
|
|
3205
|
+
if (nums.length < 2) {
|
|
3206
|
+
return values_1.ERRORS.DIV0;
|
|
3207
|
+
}
|
|
3208
|
+
let ss = 0;
|
|
3209
|
+
for (const v of nums) {
|
|
3210
|
+
ss += (v - mean) * (v - mean);
|
|
3211
|
+
}
|
|
3212
|
+
sigma = Math.sqrt(ss / (nums.length - 1));
|
|
3213
|
+
if (sigma === 0) {
|
|
3214
|
+
return values_1.ERRORS.DIV0;
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
const z = (mean - xV.value) / (sigma / Math.sqrt(nums.length));
|
|
3218
|
+
return (0, values_1.rvNumber)(1 - normalCDF(z));
|
|
3219
|
+
};
|
|
3220
|
+
exports.fnZ_TEST = fnZ_TEST;
|
|
3221
|
+
/**
|
|
3222
|
+
* F.TEST(array1, array2) — two-tailed F-test probability comparing
|
|
3223
|
+
* the variances of two samples.
|
|
3224
|
+
*
|
|
3225
|
+
* F = var(larger) / var(smaller) (always >= 1)
|
|
3226
|
+
* P = 2 × P(F_dist(df1, df2) > F)
|
|
3227
|
+
*/
|
|
3228
|
+
const fnF_TEST = args => {
|
|
3229
|
+
const v1 = (0, _shared_1.flattenNumbers)([args[0]]);
|
|
3230
|
+
const e1 = (0, _shared_1.firstError)(v1);
|
|
3231
|
+
if (e1) {
|
|
3232
|
+
return e1;
|
|
3233
|
+
}
|
|
3234
|
+
const v2 = (0, _shared_1.flattenNumbers)([args[1]]);
|
|
3235
|
+
const e2 = (0, _shared_1.firstError)(v2);
|
|
3236
|
+
if (e2) {
|
|
3237
|
+
return e2;
|
|
3238
|
+
}
|
|
3239
|
+
const a1 = v1.map(v => v.value);
|
|
3240
|
+
const a2 = v2.map(v => v.value);
|
|
3241
|
+
if (a1.length < 2 || a2.length < 2) {
|
|
3242
|
+
return values_1.ERRORS.DIV0;
|
|
3243
|
+
}
|
|
3244
|
+
const varOf = (xs) => {
|
|
3245
|
+
const mean = xs.reduce((s, v) => s + v, 0) / xs.length;
|
|
3246
|
+
let ss = 0;
|
|
3247
|
+
for (const v of xs) {
|
|
3248
|
+
ss += (v - mean) * (v - mean);
|
|
3249
|
+
}
|
|
3250
|
+
return ss / (xs.length - 1);
|
|
3251
|
+
};
|
|
3252
|
+
const var1 = varOf(a1);
|
|
3253
|
+
const var2 = varOf(a2);
|
|
3254
|
+
if (var1 === 0 || var2 === 0) {
|
|
3255
|
+
return values_1.ERRORS.DIV0;
|
|
3256
|
+
}
|
|
3257
|
+
// Ensure f >= 1 so we evaluate the right tail.
|
|
3258
|
+
const f = var1 / var2;
|
|
3259
|
+
const df1 = a1.length - 1;
|
|
3260
|
+
const df2 = a2.length - 1;
|
|
3261
|
+
// Regularised incomplete beta: Ix(a,b).
|
|
3262
|
+
// F-distribution right-tail = I_{df2/(df2+df1*F)}(df2/2, df1/2).
|
|
3263
|
+
// Two-tailed p = 2 × min(right, left) = 2 × min(right, 1-right).
|
|
3264
|
+
const x = df2 / (df2 + df1 * f);
|
|
3265
|
+
const rightTail = incompleteBeta(x, df2 / 2, df1 / 2);
|
|
3266
|
+
const twoTail = 2 * Math.min(rightTail, 1 - rightTail);
|
|
3267
|
+
return (0, values_1.rvNumber)(twoTail);
|
|
3268
|
+
};
|
|
3269
|
+
exports.fnF_TEST = fnF_TEST;
|
|
3270
|
+
/**
|
|
3271
|
+
* Regularised incomplete beta function I_x(a, b). Lentz's continued
|
|
3272
|
+
* fraction, adapted from Numerical Recipes §6.4. Sufficient precision
|
|
3273
|
+
* for probability-value calculations.
|
|
3274
|
+
*/
|
|
3275
|
+
function incompleteBeta(x, a, b) {
|
|
3276
|
+
if (x <= 0) {
|
|
3277
|
+
return 0;
|
|
3278
|
+
}
|
|
3279
|
+
if (x >= 1) {
|
|
3280
|
+
return 1;
|
|
3281
|
+
}
|
|
3282
|
+
const bt = Math.exp(lnGamma(a + b) - lnGamma(a) - lnGamma(b) + a * Math.log(x) + b * Math.log(1 - x));
|
|
3283
|
+
const useFront = x < (a + 1) / (a + b + 2);
|
|
3284
|
+
const cf = (xx, aa, bb) => {
|
|
3285
|
+
const maxIt = 200;
|
|
3286
|
+
const eps = 3e-7;
|
|
3287
|
+
const qab = aa + bb;
|
|
3288
|
+
const qap = aa + 1;
|
|
3289
|
+
const qam = aa - 1;
|
|
3290
|
+
let c = 1;
|
|
3291
|
+
let d = 1 - (qab * xx) / qap;
|
|
3292
|
+
if (Math.abs(d) < 1e-30) {
|
|
3293
|
+
d = 1e-30;
|
|
3294
|
+
}
|
|
3295
|
+
d = 1 / d;
|
|
3296
|
+
let h = d;
|
|
3297
|
+
for (let m = 1; m <= maxIt; m++) {
|
|
3298
|
+
const m2 = 2 * m;
|
|
3299
|
+
let aa2 = (m * (bb - m) * xx) / ((qam + m2) * (aa + m2));
|
|
3300
|
+
d = 1 + aa2 * d;
|
|
3301
|
+
if (Math.abs(d) < 1e-30) {
|
|
3302
|
+
d = 1e-30;
|
|
3303
|
+
}
|
|
3304
|
+
c = 1 + aa2 / c;
|
|
3305
|
+
if (Math.abs(c) < 1e-30) {
|
|
3306
|
+
c = 1e-30;
|
|
3307
|
+
}
|
|
3308
|
+
d = 1 / d;
|
|
3309
|
+
h *= d * c;
|
|
3310
|
+
aa2 = (-(aa + m) * (qab + m) * xx) / ((aa + m2) * (qap + m2));
|
|
3311
|
+
d = 1 + aa2 * d;
|
|
3312
|
+
if (Math.abs(d) < 1e-30) {
|
|
3313
|
+
d = 1e-30;
|
|
3314
|
+
}
|
|
3315
|
+
c = 1 + aa2 / c;
|
|
3316
|
+
if (Math.abs(c) < 1e-30) {
|
|
3317
|
+
c = 1e-30;
|
|
3318
|
+
}
|
|
3319
|
+
d = 1 / d;
|
|
3320
|
+
const del = d * c;
|
|
3321
|
+
h *= del;
|
|
3322
|
+
if (Math.abs(del - 1) < eps) {
|
|
3323
|
+
break;
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
return h;
|
|
3327
|
+
};
|
|
3328
|
+
if (useFront) {
|
|
3329
|
+
return (bt * cf(x, a, b)) / a;
|
|
3330
|
+
}
|
|
3331
|
+
return 1 - (bt * cf(1 - x, b, a)) / b;
|
|
3332
|
+
}
|
|
3333
|
+
/**
|
|
3334
|
+
* T.TEST(array1, array2, tails, type) — tails in {1, 2}, type in {1, 2, 3}.
|
|
3335
|
+
* type 1: paired
|
|
3336
|
+
* type 2: two-sample, equal variance
|
|
3337
|
+
* type 3: two-sample, unequal variance (Welch's)
|
|
3338
|
+
*/
|
|
3339
|
+
const fnT_TEST = args => {
|
|
3340
|
+
const v1 = (0, _shared_1.flattenNumbers)([args[0]]);
|
|
3341
|
+
const e1 = (0, _shared_1.firstError)(v1);
|
|
3342
|
+
if (e1) {
|
|
3343
|
+
return e1;
|
|
3344
|
+
}
|
|
3345
|
+
const v2 = (0, _shared_1.flattenNumbers)([args[1]]);
|
|
3346
|
+
const e2 = (0, _shared_1.firstError)(v2);
|
|
3347
|
+
if (e2) {
|
|
3348
|
+
return e2;
|
|
3349
|
+
}
|
|
3350
|
+
const tailsV = (0, values_1.toNumberRV)((0, values_1.topLeft)(args[2]));
|
|
3351
|
+
if ((0, values_1.isError)(tailsV)) {
|
|
3352
|
+
return tailsV;
|
|
3353
|
+
}
|
|
3354
|
+
const typeV = (0, values_1.toNumberRV)((0, values_1.topLeft)(args[3]));
|
|
3355
|
+
if ((0, values_1.isError)(typeV)) {
|
|
3356
|
+
return typeV;
|
|
3357
|
+
}
|
|
3358
|
+
const tails = Math.trunc(tailsV.value);
|
|
3359
|
+
const type = Math.trunc(typeV.value);
|
|
3360
|
+
if (tails !== 1 && tails !== 2) {
|
|
3361
|
+
return values_1.ERRORS.NUM;
|
|
3362
|
+
}
|
|
3363
|
+
if (type !== 1 && type !== 2 && type !== 3) {
|
|
3364
|
+
return values_1.ERRORS.NUM;
|
|
3365
|
+
}
|
|
3366
|
+
const a1 = v1.map(v => v.value);
|
|
3367
|
+
const a2 = v2.map(v => v.value);
|
|
3368
|
+
const mean = (xs) => xs.reduce((s, v) => s + v, 0) / xs.length;
|
|
3369
|
+
const sampleVar = (xs, m) => {
|
|
3370
|
+
let ss = 0;
|
|
3371
|
+
for (const v of xs) {
|
|
3372
|
+
ss += (v - m) * (v - m);
|
|
3373
|
+
}
|
|
3374
|
+
return ss / (xs.length - 1);
|
|
3375
|
+
};
|
|
3376
|
+
let t;
|
|
3377
|
+
let df;
|
|
3378
|
+
if (type === 1) {
|
|
3379
|
+
// Paired
|
|
3380
|
+
if (a1.length !== a2.length) {
|
|
3381
|
+
return values_1.ERRORS.NA;
|
|
3382
|
+
}
|
|
3383
|
+
if (a1.length < 2) {
|
|
3384
|
+
return values_1.ERRORS.DIV0;
|
|
3385
|
+
}
|
|
3386
|
+
const diffs = a1.map((v, i) => v - a2[i]);
|
|
3387
|
+
const md = mean(diffs);
|
|
3388
|
+
const sd2 = sampleVar(diffs, md);
|
|
3389
|
+
if (sd2 === 0) {
|
|
3390
|
+
return values_1.ERRORS.DIV0;
|
|
3391
|
+
}
|
|
3392
|
+
t = md / Math.sqrt(sd2 / diffs.length);
|
|
3393
|
+
df = diffs.length - 1;
|
|
3394
|
+
}
|
|
3395
|
+
else if (type === 2) {
|
|
3396
|
+
// Two-sample, equal variance (pooled)
|
|
3397
|
+
const n1 = a1.length;
|
|
3398
|
+
const n2 = a2.length;
|
|
3399
|
+
if (n1 < 2 || n2 < 2) {
|
|
3400
|
+
return values_1.ERRORS.DIV0;
|
|
3401
|
+
}
|
|
3402
|
+
const m1 = mean(a1);
|
|
3403
|
+
const m2 = mean(a2);
|
|
3404
|
+
const s1 = sampleVar(a1, m1);
|
|
3405
|
+
const s2 = sampleVar(a2, m2);
|
|
3406
|
+
const sp2 = ((n1 - 1) * s1 + (n2 - 1) * s2) / (n1 + n2 - 2);
|
|
3407
|
+
if (sp2 === 0) {
|
|
3408
|
+
return values_1.ERRORS.DIV0;
|
|
3409
|
+
}
|
|
3410
|
+
t = (m1 - m2) / Math.sqrt(sp2 * (1 / n1 + 1 / n2));
|
|
3411
|
+
df = n1 + n2 - 2;
|
|
3412
|
+
}
|
|
3413
|
+
else {
|
|
3414
|
+
// Two-sample, unequal variance (Welch's)
|
|
3415
|
+
const n1 = a1.length;
|
|
3416
|
+
const n2 = a2.length;
|
|
3417
|
+
if (n1 < 2 || n2 < 2) {
|
|
3418
|
+
return values_1.ERRORS.DIV0;
|
|
3419
|
+
}
|
|
3420
|
+
const m1 = mean(a1);
|
|
3421
|
+
const m2 = mean(a2);
|
|
3422
|
+
const s1 = sampleVar(a1, m1);
|
|
3423
|
+
const s2 = sampleVar(a2, m2);
|
|
3424
|
+
if (s1 === 0 && s2 === 0) {
|
|
3425
|
+
return values_1.ERRORS.DIV0;
|
|
3426
|
+
}
|
|
3427
|
+
const se2 = s1 / n1 + s2 / n2;
|
|
3428
|
+
t = (m1 - m2) / Math.sqrt(se2);
|
|
3429
|
+
df = (se2 * se2) / (((s1 / n1) * (s1 / n1)) / (n1 - 1) + ((s2 / n2) * (s2 / n2)) / (n2 - 1));
|
|
3430
|
+
}
|
|
3431
|
+
// Two-tailed p = I_{df/(df + t^2)}(df/2, 1/2)
|
|
3432
|
+
const x = df / (df + t * t);
|
|
3433
|
+
const oneTail = 0.5 * incompleteBeta(x, df / 2, 0.5);
|
|
3434
|
+
return (0, values_1.rvNumber)(tails === 1 ? oneTail : 2 * oneTail);
|
|
3435
|
+
};
|
|
3436
|
+
exports.fnT_TEST = fnT_TEST;
|
|
3437
|
+
/**
|
|
3438
|
+
* CHISQ.TEST(actual_range, expected_range) — chi-square independence test.
|
|
3439
|
+
*
|
|
3440
|
+
* χ² = Σ (actual_i - expected_i)² / expected_i
|
|
3441
|
+
* df = (rows-1)(cols-1) for a contingency table, or n-1 for 1-D
|
|
3442
|
+
* p = 1 - CHISQ.DIST(χ², df, TRUE)
|
|
3443
|
+
*/
|
|
3444
|
+
const fnCHISQ_TEST = args => {
|
|
3445
|
+
if (args[0].kind !== 5 /* RVKind.Array */ || args[1].kind !== 5 /* RVKind.Array */) {
|
|
3446
|
+
return values_1.ERRORS.VALUE;
|
|
3447
|
+
}
|
|
3448
|
+
const a = args[0];
|
|
3449
|
+
const e = args[1];
|
|
3450
|
+
if (a.height !== e.height || a.width !== e.width) {
|
|
3451
|
+
return values_1.ERRORS.NA;
|
|
3452
|
+
}
|
|
3453
|
+
let chi2 = 0;
|
|
3454
|
+
for (let r = 0; r < a.height; r++) {
|
|
3455
|
+
for (let c = 0; c < a.width; c++) {
|
|
3456
|
+
const av = a.rows[r][c];
|
|
3457
|
+
const ev = e.rows[r][c];
|
|
3458
|
+
if (av.kind === 4 /* RVKind.Error */) {
|
|
3459
|
+
return av;
|
|
3460
|
+
}
|
|
3461
|
+
if (ev.kind === 4 /* RVKind.Error */) {
|
|
3462
|
+
return ev;
|
|
3463
|
+
}
|
|
3464
|
+
if (av.kind !== 1 /* RVKind.Number */ || ev.kind !== 1 /* RVKind.Number */) {
|
|
3465
|
+
return values_1.ERRORS.VALUE;
|
|
3466
|
+
}
|
|
3467
|
+
if (ev.value <= 0) {
|
|
3468
|
+
return values_1.ERRORS.NUM;
|
|
3469
|
+
}
|
|
3470
|
+
const diff = av.value - ev.value;
|
|
3471
|
+
chi2 += (diff * diff) / ev.value;
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
// df: (rows-1)(cols-1) for contingency tables, n-1 for 1-D.
|
|
3475
|
+
let df;
|
|
3476
|
+
if (a.height === 1 || a.width === 1) {
|
|
3477
|
+
df = a.height * a.width - 1;
|
|
3478
|
+
}
|
|
3479
|
+
else {
|
|
3480
|
+
df = (a.height - 1) * (a.width - 1);
|
|
3481
|
+
}
|
|
3482
|
+
if (df < 1) {
|
|
3483
|
+
return values_1.ERRORS.NA;
|
|
3484
|
+
}
|
|
3485
|
+
// p = 1 - CHISQ.DIST.CDF(chi2, df) = right tail
|
|
3486
|
+
return (0, values_1.rvNumber)(1 - gammaIncomplete(df / 2, chi2 / 2));
|
|
3487
|
+
};
|
|
3488
|
+
exports.fnCHISQ_TEST = fnCHISQ_TEST;
|