diff --git a/.config/karma/base.js b/.config/karma/base.js deleted file mode 100644 index d66811a35f..0000000000 --- a/.config/karma/base.js +++ /dev/null @@ -1,80 +0,0 @@ -const webpackConfigFactory = require('../../webpack.config'); - -module.exports.create = function(config) { - return { - // base path that will be used to resolve all patterns (eg. files, exclude) - basePath: '', - - client: { - clearContext: false, - spec: config.spec - }, - - // frameworks to use - // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['jasmine'], - - // list of files / patterns to load in the browser - files: [ - 'karma.starter.ts', - ], - - // list of files / patterns to exclude - exclude: [ ], - - // preprocess matching files before serving them to the browser - // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor - preprocessors: { - 'karma.starter.ts': ['webpack', 'sourcemap'], - }, - - // test results reporter to use - // possible values: 'dots', 'progress' - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['dots'], - - // web server port - port: 9876, - - // enable / disable colors in the output (reporters and logs) - colors: true, - - // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - logLevel: config.LOG_INFO, - - // enable / disable watching file and executing tests whenever any file changes - autoWatch: false, - - // start these browsers - // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: ['ChromeHeadless', 'FirefoxHeadless'], - - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits - singleRun: true, - - // Concurrency level - // how many browser should be started simultaneous - concurrency: Infinity, - - // Extending timeout fixes https://github.com/handsontable/hyperformula/issues/1430 - browserDisconnectTimeout : 60000, - - // Webpack's configuration for Karma - webpack: (function() { - // Take the second config from an array - full HF build. - const config = webpackConfigFactory('development')[1]; - - // Loaders are executed from bottom to top. Push ts-loader as a first loader. - config.module.rules[0].use.push({ - loader: 'ts-loader', - options: { - configFile: 'tsconfig.test.json' - } - }); - - return config; - }()), - }; -}; diff --git a/.config/karma/debug.js b/.config/karma/debug.js deleted file mode 100644 index e6c9fe092c..0000000000 --- a/.config/karma/debug.js +++ /dev/null @@ -1,13 +0,0 @@ -const configFactory = require('./base'); - -module.exports.create = function(config) { - const configBase = configFactory.create(config); - - return { - ...configBase, - browsers: ['Chrome'], - reporters: ['kjhtml'], - singleRun: false, - autoWatch: true, - } -} diff --git a/.eslintignore b/.eslintignore index 37928812fe..1639cebad7 100644 --- a/.eslintignore +++ b/.eslintignore @@ -9,20 +9,15 @@ src/interpreter/plugin/3rdparty # Configurations *.config.js -karma.* doc -test/unit/_setupFiles/*.js # Auto-generated directories commonjs -coverage dist doc es languages lib script -test-jasmine -test-jest typedoc typings diff --git a/.eslintrc.js b/.eslintrc.js index 27fe09a0cd..5727783ac3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -6,16 +6,12 @@ module.exports = { '@typescript-eslint', 'license-header', 'jsdoc', - 'jasmine', - 'jest', ], env: { - jasmine: true, - 'jest/globals': true, }, parserOptions: { tsconfigRootDir: __dirname, - project: './tsconfig.test.json', + project: './tsconfig.json', createDefaultProgram: true, }, extends: [ @@ -23,9 +19,6 @@ module.exports = { 'plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/recommended-requiring-type-checking', - 'plugin:jasmine/recommended', - 'plugin:jest/recommended', - 'plugin:jest/style', ], rules: { // Automatic fixers @@ -122,13 +115,6 @@ module.exports = { MethodDefinition: true, } }], - 'jest/no-jasmine-globals': 'off', - 'jest/no-alias-methods': 'off', - 'jest/no-conditional-expect': 'warn', - 'jest/no-standalone-expect': 'warn', - 'jest/no-test-prefixes': 'off', - 'jest/prefer-to-be': 'warn', - 'jest/prefer-to-have-length': 'off', }, overrides: [ { @@ -143,11 +129,5 @@ module.exports = { 'sort-keys': ['error', 'asc'], } }, - { - files: ['**/*.spec.ts'], - rules: { - '@typescript-eslint/no-non-null-assertion': 'off', - } - } ], } diff --git a/.github/codecov.yml b/.github/codecov.yml deleted file mode 100644 index d0bd2428bd..0000000000 --- a/.github/codecov.yml +++ /dev/null @@ -1,14 +0,0 @@ -codecov: - require_ci_to_pass: yes - -coverage: - range: 95..100 - round: down - precision: 2 - -comment: - layout: "reach, diff, flags, files" - behavior: new - require_changes: false - require_base: yes - require_head: yes diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml deleted file mode 100644 index f70b6b37de..0000000000 --- a/.github/workflows/performance.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: Performance -permissions: - contents: read - pull-requests: write - -on: - pull_request: - types: - - opened - - reopened - - synchronize # the head branch is updated from the base branch, new commits are pushed to the head branch, or the base branch is changed - -jobs: - performance-test: - strategy: - matrix: - node-version: [ '22' ] - os: [ 'ubuntu-latest' ] - name: Test performance - runs-on: ${{ matrix.os }} - steps: - - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@56899e050abffc08c2b3b61f3ec6a79a9dc3223d # https://github.com/actions/setup-node/releases/tag/v1.4.4 - with: - node-version: ${{ matrix.node-version }} - - - name: (base) Checkout - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # https://github.com/actions/checkout/releases/tag/v2.3.4 - with: - ref: ${{ github.event.pull_request.base.sha }} - - - name: (base) Install dependencies - run: | - npm ci - - - name: (base) Run performance tests - run: | - npm run benchmark:write-to-file base.json - - - name: (head) Checkout - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # https://github.com/actions/checkout/releases/tag/v2.3.4 - with: - clean: false - - - name: (head) Install dependencies - run: | - npm ci - - - name: (head) Run performance tests - run: | - npm run benchmark:write-to-file head.json - - - name: Compare the results - run: | - npm run benchmark:compare-benchmarks base.json head.json performance-report.md - - - name: Publish a comment - header - uses: marocchino/sticky-pull-request-comment@6804b5ad49d19c10c9ae7cf5057352f7ff333f31 # https://github.com/marocchino/sticky-pull-request-comment/tree/v1.6.0 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - message: | - ## Performance comparison of head (${{ github.event.pull_request.head.sha }}) vs base (${{ github.event.pull_request.base.sha }}) - - - name: Publish a comment - performance comparison report - uses: marocchino/sticky-pull-request-comment@6804b5ad49d19c10c9ae7cf5057352f7ff333f31 # https://github.com/marocchino/sticky-pull-request-comment/tree/v1.6.0 - with: - append: true - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - path: performance-report.md diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 51a87c303c..0000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Test -permissions: - contents: read - -on: - pull_request: - types: - - opened - - reopened - - synchronize # the head branch is updated from the base branch, new commits are pushed to the head branch, or the base branch is changed - push: - branches: - - 'master' - - 'develop' - - 'release/**' - -jobs: - unit-tests: - strategy: - matrix: - node-version: [ '22' ] - os: [ 'ubuntu-latest' ] - name: unit-tests - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@722adc63f1aa60a57ec37892e133b1d319cae598 # https://github.com/actions/checkout/releases/tag/v2.0.0 - - - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@56899e050abffc08c2b3b61f3ec6a79a9dc3223d # https://github.com/actions/setup-node/releases/tag/v1.4.4 - with: - node-version: ${{ matrix.node-version }} - - - name: Install dependencies - run: | - npm ci - - - name: Run tests - run: | - npm run test:unit.ci -- --coverage - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@6004246f47ab62d32be025ce173b241cd84ac58e # https://github.com/codecov/codecov-action/releases/tag/v1.0.13 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - - browser-tests: - strategy: - matrix: - node-version: [ '22' ] - os: [ 'ubuntu-latest' ] - name: browser-tests - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@722adc63f1aa60a57ec37892e133b1d319cae598 # https://github.com/actions/checkout/releases/tag/v2.0.0 - - - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@56899e050abffc08c2b3b61f3ec6a79a9dc3223d # https://github.com/actions/setup-node/releases/tag/v1.4.4 - with: - node-version: ${{ matrix.node-version }} - - - name: Install dependencies - run: | - npm ci - - - name: Run tests - run: | - npm run test:browser diff --git a/.gitignore b/.gitignore index d4bb1b0997..65a386ec94 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ .idea/ .vscode /commonjs/ -/coverage/ /dist/ /doc/ /docs/api/ @@ -13,8 +12,6 @@ /es/ /languages/ /lib/ -/test-jasmine/ -/test-jest/ node_modules/ /typings/ /storage/ diff --git a/.typedoc.ts b/.typedoc.ts index 0001ced876..8f2e9dba7b 100644 --- a/.typedoc.ts +++ b/.typedoc.ts @@ -1,6 +1,5 @@ module.exports = { "exclude": [ - "./test/**", "./src/interpreter/**", "./src/i18n/**", "./src/parser/**", diff --git a/Makefile b/Makefile index ea1c9af8db..73b87a5faa 100644 --- a/Makefile +++ b/Makefile @@ -7,18 +7,9 @@ setup: ## Setup project compile: ## Compile to javascript @npm run compile -test: ## Run tests - @npm run test +check: typecheck lint ## Check whether code is working correctly (types + lint) -unit: ## Run unit tests - @npm run test:unit - -test-ci: ## Separate test configuration for CI environment - @npm run test - -check: typecheck test ## Check whether code is working correctly (types + specs) - -full: check lint-fix ## Check whether code is ready to commit (types + specs + lint) +full: check lint-fix ## Check whether code is ready to commit (types + lint) lint: ## Show linting errors @npm run lint @@ -26,9 +17,6 @@ lint: ## Show linting errors lint-fix: ## Fix linting errors @npm run lint:fix -coverage: ## Run tests and show coverage - @npm run test:coverage - doc: ## Generate documentation @npm run typedoc:build @@ -65,6 +53,6 @@ verify-production-licenses: help: ## Show all make commands @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' -.PHONY: test coverage benchmark doc servedoc +.PHONY: doc servedoc .DEFAULT_GOAL := help diff --git a/docs/guide/building.md b/docs/guide/building.md index a781b05b8b..7e21c283d4 100644 --- a/docs/guide/building.md +++ b/docs/guide/building.md @@ -60,22 +60,6 @@ Most likely, you will want to document the code. You can use the following comma * `npm run docs:build` - builds the docs * `npm run docs:dev` - serves the development version of the docs locally -## Run the tests - -The tests are done with Jest and Karma. The same test suite should -pass in both of them because the library might be used -[server-side](server-side-installation) or in a browser, so you have -to be sure that both environments are fine. - -* `npm run test` - runs the linter and all tests -* `npm run test:unit` - runs unit tests - * To run a test suite that matches a word, add a Jest `-t` flag. For example: `npm run test:unit -- -t 'SUMIF'` runs only the tests that match the word `SUMIF` within `describe()` or `it()`. - * To run a specific test suite, pass the file name. For example: `npm run test:unit 'function-sumif.spec.ts'` runs only the unit tests from the file `function-sumif.spec.ts`. -* `npm run test:coverage` - runs unit tests and generates code coverage -* `npm run test:browser` - runs tests in **karma** once and closes all open browsers - * To run a specific `spec` file or a test suite you can add a Karma `--spec` flag. For example: `npm run test:browser.debug -- --spec=matrix.spec.ts` runs `matrix.spec.ts` browser tests only -* `npm run test:browser.debug` - runs test in **karma** only in Chrome until you exit the process. It watches changes in `src` and `test` directories and rebuilds them automatically. - ## Run the linter You can use the following commands to lint the code, so it meets the required standards. ESLint is used as the tool of choice in this case. diff --git a/jasmine.json b/jasmine.json deleted file mode 100644 index bb08a6049d..0000000000 --- a/jasmine.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "spec_dir": "test-jasmine/test", - "spec_files": [ - "**/*[sS]pec.js" - ], - "helpers": [ - "_setupFiles/babel.js", - "_setupFiles/bootstrap.js", - "_setupFiles/jsdom.js" - ], - "stopSpecOnExpectationFailure": false, - "random": true -} diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index f7573a049b..0000000000 --- a/jest.config.js +++ /dev/null @@ -1,60 +0,0 @@ -// For a detailed explanation regarding each configuration property, visit: -// https://jestjs.io/docs/en/configuration.html - -module.exports = { - // An array of glob patterns indicating a set of files for which coverage information should be collected - collectCoverageFrom: [ - 'src/**', - '!**/node_modules/**', - ], - - coverageProvider: 'babel', - - // The directory where Jest should output its coverage files - coverageDirectory: "coverage", - - // A path to a module which exports an async function that is triggered once before all test suites - globalSetup: '/test/unit/_setupFiles/globalSetup.ts', - - // A set of global variables that need to be available in all test environments - globals: { - "ts-jest": { - "tsconfig": "./test/unit/tsconfig.json" - } - }, - - // An array of file extensions your modules use - moduleFileExtensions: [ - "ts", - "tsx", - "js" - ], - - // The paths to modules that run some code to configure or set up the testing environment after each test - setupFilesAfterEnv: [ - '/test/unit/_setupFiles/bootstrap.ts', - '/test/unit/_setupFiles/jest/bootstrap.ts' - ], - - // The test environment that will be used for testing - testEnvironment: "node", - - // The glob patterns Jest uses to detect test files - testMatch: [ - "/test/unit/**/*spec.(ts|js)" - ], - - silent: true, - - // A map from regular expressions to paths to transformers - transform: { - "^.+\\.(ts|tsx)$": "ts-jest" - }, - - watchPathIgnorePatterns: [ - '/node_modules/', - '/dist/', - '/commonjs/', - '/es/', - ] -}; diff --git a/karma.conf.js b/karma.conf.js deleted file mode 100644 index e83c1eccca..0000000000 --- a/karma.conf.js +++ /dev/null @@ -1,8 +0,0 @@ -const env = process.env.NODE_ENV || 'base'; -const configFactory = require(`./.config/karma/${env}`); - -module.exports = function(config) { - config.set( - configFactory.create(config) - ); -}; diff --git a/karma.starter.ts b/karma.starter.ts deleted file mode 100644 index 3330b307e0..0000000000 --- a/karma.starter.ts +++ /dev/null @@ -1,17 +0,0 @@ -import './test/unit/_setupFiles/bootstrap'; - -//@ts-ignore -const specArg: string = __karma__.config.spec; - -// require all modules ending in ".spec.ts" from the -// './test' directory and all subdirectories -const testsContext = require.context('./test/unit', true, /.spec.ts$/); -let files = testsContext.keys(); - -if (specArg) { - const regEx = new RegExp(specArg); - - files = testsContext.keys().filter(key => key.match(regEx)); -} - -files.forEach(testsContext); diff --git a/package-lock.json b/package-lock.json index 2cf8736064..3aef2fda87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,10 +27,6 @@ "@babel/preset-typescript": "^7.26.0", "@babel/register": "^7.25.9", "@babel/runtime": "^7.26.0", - "@types/exceljs": "^0.5.3", - "@types/jasmine": "^5.1.4", - "@types/jest": "^26.0.24", - "@types/jsdom": "^21.1.7", "@types/node": "^17.0.45", "@types/webpack-env": "^1.18.5", "@typescript-eslint/eslint-plugin": "^5.62.0", @@ -43,12 +39,12 @@ "env-cmd": "^10.1.0", "eslint": "^8.57.1", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-jasmine": "^4.2.2", - "eslint-plugin-jest": "^27.9.0", "eslint-plugin-jsdoc": "^50.5.0", "eslint-plugin-license-header": "^0.6.1", "eslint-plugin-prettier": "^5.2.1", "esm": "^3.2.25", +<<<<<<< HEAD +======= "exceljs": "^4.4.0", "full-icu": "^1.5.0", "jasmine": "^5.4.0", @@ -61,6 +57,7 @@ "karma-jasmine-html-reporter": "^2.1.0", "karma-sourcemap-loader": "^0.4.0", "karma-webpack": "^4.0.2", +>>>>>>> develop "license-checker": "^25.0.1", "markdown-it-footnote": "^4.0.0", "markdown-it-regex": "^0.2.0", @@ -71,7 +68,6 @@ "string-replace-loader": "^2.3.0", "tar": "^7.4.3", "terser-webpack-plugin": "^4.2.3", - "ts-jest": "^26.5.6", "ts-loader": "^8.4.0", "ts-node": "^10.9.2", "typedoc": "^0.19.2", @@ -84,31 +80,10 @@ "webpackbar": "^6.0.1" } }, - "node_modules/@asamuzakjp/css-color": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", - "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@csstools/css-calc": "^2.1.3", - "@csstools/css-color-parser": "^3.0.9", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "lru-cache": "^10.4.3" - } - }, - "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/@babel/cli": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.3.tgz", - "integrity": "sha512-n1RU5vuCX0CsaqaXm9I0KUCNKNQMy5epmzl/xdSSm70bSqhg9GWhgeosypyQLc0bK24+Xpk1WGzZlI9pJtkZdg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.6.tgz", + "integrity": "sha512-6EUNcuBbNkj08Oj4gAZ+BUU8yLCgKzgVX4gaTh09Ya2C8ICM4P+G30g4m3akRxSYAp3A/gnWchrNst7px4/nUQ==", "dev": true, "license": "MIT", "dependencies": { @@ -146,13 +121,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -161,9 +136,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", "dev": true, "license": "MIT", "engines": { @@ -171,21 +146,21 @@ } }, "node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -202,14 +177,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -232,13 +207,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", + "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -249,18 +224,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz", - "integrity": "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", + "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.5", + "@babel/traverse": "^7.28.6", "semver": "^6.3.1" }, "engines": { @@ -289,17 +264,17 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", - "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.6.tgz", + "integrity": "sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "debug": "^4.4.1", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", "lodash.debounce": "^4.0.8", - "resolve": "^1.22.10" + "resolve": "^1.22.11" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -330,29 +305,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -375,9 +350,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true, "license": "MIT", "engines": { @@ -403,15 +378,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -465,42 +440,42 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz", - "integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", + "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.3", - "@babel/types": "^7.28.2" + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.5" + "@babel/types": "^7.28.6" }, "bin": { "parser": "bin/babel-parser.js" @@ -577,14 +552,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz", - "integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", + "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -612,15 +587,15 @@ } }, "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz", - "integrity": "sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.6.tgz", + "integrity": "sha512-RVdFPPyY9fCRAX68haPmOk2iyKW8PKJFthmm8NeSI3paNxKWGZIn99+VbIf0FrtCpFnPgnpF/L48tadi617ULg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-syntax-decorators": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-syntax-decorators": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -667,32 +642,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-class-properties": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", @@ -706,30 +655,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", - "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.28.6.tgz", + "integrity": "sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -752,13 +685,13 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", - "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", + "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -768,13 +701,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -783,40 +716,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -825,84 +732,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-private-property-in-object": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", @@ -919,30 +748,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -985,15 +798,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", - "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.6.tgz", + "integrity": "sha512-9knsChgsMzBV5Yh3kkhrZNxH3oCYAfMBkNNaVN4cP2RVlFPe8wYdwwcnOsAbkdDoV9UjFtOXWrWB52M8W4jNeA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1003,14 +816,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", + "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-remap-async-to-generator": "^7.27.1" }, "engines": { @@ -1037,13 +850,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz", - "integrity": "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", + "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1053,14 +866,14 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", - "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", + "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1070,14 +883,14 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz", - "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", + "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.3", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1087,18 +900,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz", - "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", + "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.4" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1108,14 +921,14 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", + "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/template": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1142,14 +955,14 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", - "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", + "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1175,14 +988,14 @@ } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.28.6.tgz", + "integrity": "sha512-5suVoXjC14lUN6ZL9OLKIHCNVWCrqGqlmEp/ixdXjvgnEl/kauLvvMO/Xw9NyMc95Joj1AeLVPVMvibBgSoFlA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1208,14 +1021,14 @@ } }, "node_modules/@babel/plugin-transform-explicit-resource-management": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", - "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", + "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1225,13 +1038,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.5.tgz", - "integrity": "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", + "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1292,13 +1105,13 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", - "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", + "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1324,13 +1137,13 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.5.tgz", - "integrity": "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", + "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1373,14 +1186,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", + "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1459,13 +1272,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", - "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", + "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1475,13 +1288,13 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", - "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", + "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1491,17 +1304,17 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz", - "integrity": "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", + "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.4" + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1528,13 +1341,13 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", - "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", + "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1544,13 +1357,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz", - "integrity": "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", + "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { @@ -1577,14 +1390,14 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", - "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", + "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1594,15 +1407,15 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", - "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", + "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1644,17 +1457,17 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", - "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", + "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-syntax-jsx": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1697,13 +1510,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz", - "integrity": "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.6.tgz", + "integrity": "sha512-eZhoEZHYQLL5uc1gS5e9/oTknS0sSSAtd5TkKMUp3J+S/CaUjagc0kOUPsEbDmMeva0nC3WWl4SxVY6+OBuxfw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1713,14 +1526,14 @@ } }, "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", - "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", + "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1783,13 +1596,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", + "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { @@ -1848,17 +1661,17 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.5.tgz", - "integrity": "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", + "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1" + "@babel/plugin-syntax-typescript": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1884,14 +1697,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", - "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", + "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1918,14 +1731,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", - "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", + "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1935,76 +1748,76 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.5.tgz", - "integrity": "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.6.tgz", + "integrity": "sha512-GaTI4nXDrs7l0qaJ6Rg06dtOXTBCG6TMDB44zbqofCIC4PqC7SEvmFFtpxzCDw9W5aJ7RKVshgXTLvLdBFV/qw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/compat-data": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.27.1", - "@babel/plugin-syntax-import-attributes": "^7.27.1", + "@babel/plugin-syntax-import-assertions": "^7.28.6", + "@babel/plugin-syntax-import-attributes": "^7.28.6", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-async-generator-functions": "^7.28.0", - "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.28.6", + "@babel/plugin-transform-async-to-generator": "^7.28.6", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.5", - "@babel/plugin-transform-class-properties": "^7.27.1", - "@babel/plugin-transform-class-static-block": "^7.28.3", - "@babel/plugin-transform-classes": "^7.28.4", - "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.6", + "@babel/plugin-transform-class-properties": "^7.28.6", + "@babel/plugin-transform-class-static-block": "^7.28.6", + "@babel/plugin-transform-classes": "^7.28.6", + "@babel/plugin-transform-computed-properties": "^7.28.6", "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-dotall-regex": "^7.28.6", "@babel/plugin-transform-duplicate-keys": "^7.27.1", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.28.6", "@babel/plugin-transform-dynamic-import": "^7.27.1", - "@babel/plugin-transform-explicit-resource-management": "^7.28.0", - "@babel/plugin-transform-exponentiation-operator": "^7.28.5", + "@babel/plugin-transform-explicit-resource-management": "^7.28.6", + "@babel/plugin-transform-exponentiation-operator": "^7.28.6", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", - "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.28.6", "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.28.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.28.6", "@babel/plugin-transform-modules-systemjs": "^7.28.5", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-new-target": "^7.27.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", - "@babel/plugin-transform-numeric-separator": "^7.27.1", - "@babel/plugin-transform-object-rest-spread": "^7.28.4", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", + "@babel/plugin-transform-numeric-separator": "^7.28.6", + "@babel/plugin-transform-object-rest-spread": "^7.28.6", "@babel/plugin-transform-object-super": "^7.27.1", - "@babel/plugin-transform-optional-catch-binding": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.28.5", + "@babel/plugin-transform-optional-catch-binding": "^7.28.6", + "@babel/plugin-transform-optional-chaining": "^7.28.6", "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/plugin-transform-private-methods": "^7.27.1", - "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-private-methods": "^7.28.6", + "@babel/plugin-transform-private-property-in-object": "^7.28.6", "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.28.4", - "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.28.6", + "@babel/plugin-transform-regexp-modifiers": "^7.28.6", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-spread": "^7.28.6", "@babel/plugin-transform-sticky-regex": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-typeof-symbol": "^7.27.1", "@babel/plugin-transform-unicode-escapes": "^7.27.1", - "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.28.6", "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", @@ -2089,9 +1902,9 @@ } }, "node_modules/@babel/register": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.28.3.tgz", - "integrity": "sha512-CieDOtd8u208eI49bYl4z1J22ySFw87IGwE+IswFEExH7e3rLgKb0WNQeumnacQ1+VoDJLYI5QFA3AJZuyZQfA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.28.6.tgz", + "integrity": "sha512-pgcbbEl/dWQYb6L6Yew6F94rdwygfuv+vJ/tXfwIOYAfPB6TNWpXUMEtEq3YuTeHRdvMIhvz13bkT9CNaS+wqA==", "dev": true, "license": "MIT", "dependencies": { @@ -2109,9 +1922,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", - "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "dev": true, "license": "MIT", "engines": { @@ -2119,33 +1932,33 @@ } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", "debug": "^4.3.1" }, "engines": { @@ -2153,9 +1966,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", "dev": true, "license": "MIT", "dependencies": { @@ -2166,46 +1979,12 @@ "node": ">=6.9.0" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@cnakazawa/watch": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", - "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - }, - "bin": { - "watch": "cli.js" - }, - "engines": { - "node": ">=0.1.95" - } - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -2224,121 +2003,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@csstools/color-helpers": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", - "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", - "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", - "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/color-helpers": "^5.1.0", - "@csstools/css-calc": "^2.1.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", - "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", - "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -2367,9 +2031,9 @@ } }, "node_modules/@es-joy/jsdoccomment/node_modules/@typescript-eslint/types": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.49.0.tgz", - "integrity": "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", + "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", "dev": true, "license": "MIT", "engines": { @@ -2381,9 +2045,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2443,51 +2107,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@fast-csv/format": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", - "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "^14.0.1", - "lodash.escaperegexp": "^4.1.2", - "lodash.isboolean": "^3.0.3", - "lodash.isequal": "^4.5.0", - "lodash.isfunction": "^3.0.9", - "lodash.isnil": "^4.0.0" - } - }, - "node_modules/@fast-csv/format/node_modules/@types/node": { - "version": "14.18.63", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", - "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@fast-csv/parse": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", - "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "^14.0.1", - "lodash.escaperegexp": "^4.1.2", - "lodash.groupby": "^4.6.0", - "lodash.isfunction": "^3.0.9", - "lodash.isnil": "^4.0.0", - "lodash.isundefined": "^3.0.1", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/@fast-csv/parse/node_modules/@types/node": { - "version": "14.18.63", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", - "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -2556,109 +2175,6 @@ "node": "20 || >=22" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -2672,6 +2188,8 @@ "node": ">=18.0.0" } }, +<<<<<<< HEAD +======= "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -3176,6 +2694,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, +>>>>>>> develop "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -3366,17 +2885,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@pkgr/core": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", @@ -3400,33 +2908,6 @@ "node": ">=6" } }, - "node_modules/@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", - "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "dev": true, - "license": "MIT" - }, "node_modules/@szmarczak/http-timer": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", @@ -3440,16 +2921,6 @@ "node": ">=6" } }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/@tsconfig/node10": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", @@ -3478,51 +2949,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, "node_modules/@types/body-parser": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", @@ -3555,16 +2981,6 @@ "@types/node": "*" } }, - "node_modules/@types/cors": { - "version": "2.8.19", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", - "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -3572,16 +2988,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/exceljs": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@types/exceljs/-/exceljs-0.5.3.tgz", - "integrity": "sha512-a0PLZEJGbA4kHHoSS8cQ20ynIv17vCBV1reqsrX5ksQQb077YYvhEKC82lEq6/+mMufhk68KJ5jwugL3DKG8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/express": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", @@ -3595,9 +3001,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", - "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", "dev": true, "license": "MIT", "dependencies": { @@ -3618,16 +3024,6 @@ "@types/node": "*" } }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/highlight.js": { "version": "9.12.4", "resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.12.4.tgz", @@ -3652,63 +3048,6 @@ "@types/node": "*" } }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jasmine": { - "version": "5.1.13", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.13.tgz", - "integrity": "sha512-MYCcDkruFc92LeYZux5BC0dmqo2jk+M5UIZ4/oFnAPCXN9mCcQhLyj7F3/Za7rocVyt5YRr1MmqJqFlvQ9LVcg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/jest": { - "version": "26.0.24", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.24.tgz", - "integrity": "sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-diff": "^26.0.0", - "pretty-format": "^26.0.0" - } - }, - "node_modules/@types/jsdom": { - "version": "21.1.7", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", - "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/tough-cookie": "*", - "parse5": "^7.0.0" - } - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -3769,20 +3108,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/q": { "version": "1.5.8", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz", @@ -3839,13 +3164,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/tapable": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.12.tgz", @@ -3853,13 +3171,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/tough-cookie": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/uglify-js": { "version": "3.17.5", "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.5.tgz", @@ -3928,23 +3239,6 @@ "node": ">= 12" } }, - "node_modules/@types/yargs": { - "version": "15.0.20", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.20.tgz", - "integrity": "sha512-KIkX+/GgfFitlASYCGoSF+T4XRXhOubJLhkLVtSfsRTe9jWMmuM2g28zQ41BtPTG7TRBb2xHW+LCNVE9QR/vsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", @@ -4415,55 +3709,42 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.25", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.25.tgz", - "integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==", + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.27.tgz", + "integrity": "sha512-gnSBQjZA+//qDZen+6a2EdHqJ68Z7uybrMf3SPjEGgG4dicklwDVmMC1AeIHxtLVPT7sn6sH1KOO+tS6gwOUeQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.28.5", - "@vue/shared": "3.5.25", - "entities": "^4.5.0", + "@vue/shared": "3.5.27", + "entities": "^7.0.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, - "node_modules/@vue/compiler-core/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/@vue/compiler-dom": { - "version": "3.5.25", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz", - "integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==", + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.27.tgz", + "integrity": "sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.25", - "@vue/shared": "3.5.25" + "@vue/compiler-core": "3.5.27", + "@vue/shared": "3.5.27" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.25", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz", - "integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==", + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.27.tgz", + "integrity": "sha512-sHZu9QyDPeDmN/MRoshhggVOWE5WlGFStKFwu8G52swATgSny27hJRWteKDSUUzUH+wp+bmeNbhJnEAel/auUQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.28.5", - "@vue/compiler-core": "3.5.25", - "@vue/compiler-dom": "3.5.25", - "@vue/compiler-ssr": "3.5.25", - "@vue/shared": "3.5.25", + "@vue/compiler-core": "3.5.27", + "@vue/compiler-dom": "3.5.27", + "@vue/compiler-ssr": "3.5.27", + "@vue/shared": "3.5.27", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", @@ -4471,14 +3752,14 @@ } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.25", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz", - "integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==", + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.27.tgz", + "integrity": "sha512-Sj7h+JHt512fV1cTxKlYhg7qxBvack+BGncSpH+8vnN+KN95iPIcqB5rsbblX40XorP+ilO7VIKlkuu3Xq2vjw==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.25", - "@vue/shared": "3.5.25" + "@vue/compiler-dom": "3.5.27", + "@vue/shared": "3.5.27" } }, "node_modules/@vue/component-compiler-utils": { @@ -4562,9 +3843,9 @@ "license": "ISC" }, "node_modules/@vue/shared": { - "version": "3.5.25", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.25.tgz", - "integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==", + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.27.tgz", + "integrity": "sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ==", "dev": true, "license": "MIT" }, @@ -4631,19 +3912,6 @@ "node": ">=6" } }, - "node_modules/@vuepress/core/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@vuepress/core/node_modules/anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", @@ -4749,23 +4017,6 @@ "node": ">=8" } }, - "node_modules/@vuepress/core/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@vuepress/core/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, "node_modules/@vuepress/core/node_modules/consola": { "version": "2.15.3", "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", @@ -4947,13 +4198,6 @@ "node": ">=0.10.0" } }, - "node_modules/@vuepress/core/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@vuepress/core/node_modules/js-yaml": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", @@ -5017,22 +4261,6 @@ "node": ">=4" } }, - "node_modules/@vuepress/core/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, "node_modules/@vuepress/core/node_modules/readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", @@ -5048,13 +4276,6 @@ "node": ">=0.10" } }, - "node_modules/@vuepress/core/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, "node_modules/@vuepress/core/node_modules/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", @@ -5098,16 +4319,6 @@ "ci-info": "^3.1.1" } }, - "node_modules/@vuepress/core/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/@vuepress/core/node_modules/string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -5630,6 +4841,16 @@ "node": ">=0.10.0" } }, + "node_modules/@vuepress/shared-utils/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/@vuepress/shared-utils/node_modules/micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -5706,6 +4927,16 @@ "node": ">=0.10.0" } }, + "node_modules/@vuepress/shared-utils/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/@vuepress/theme-default": { "version": "1.9.10", "resolved": "https://registry.npmjs.org/@vuepress/theme-default/-/theme-default-1.9.10.tgz", @@ -5983,14 +5214,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -6012,34 +5235,20 @@ "node": ">= 0.6" } }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true, "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" + "node": ">= 0.6" } }, - "node_modules/acorn-globals/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -6049,16 +5258,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-globals/node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -6082,16 +5281,6 @@ "node": ">=0.4.0" } }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, "node_modules/agentkeepalive": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-2.2.0.tgz", @@ -6287,19 +5476,16 @@ } }, "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=4" } }, "node_modules/anymatch": { @@ -6323,87 +5509,6 @@ "dev": true, "license": "ISC" }, - "node_modules/archiver": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", - "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "archiver-utils": "^2.1.0", - "async": "^3.2.4", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", - "readdir-glob": "^1.1.2", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/archiver-utils/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/archiver-utils/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/archiver-utils/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/archiver-utils/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/are-docs-informative": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", @@ -6738,13 +5843,6 @@ "immediate": "^3.2.3" } }, - "node_modules/autocomplete.js/node_modules/immediate": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", - "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==", - "dev": true, - "license": "MIT" - }, "node_modules/autoprefixer": { "version": "9.8.8", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", @@ -6826,6 +5924,8 @@ "dev": true, "license": "MIT" }, +<<<<<<< HEAD +======= "node_modules/babel-jest": { "version": "26.6.3", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", @@ -6876,6 +5976,7 @@ "node": ">=8" } }, +>>>>>>> develop "node_modules/babel-loader": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.4.1.tgz", @@ -7009,65 +6110,15 @@ "object.assign": "^4.1.0" } }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", - "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", - "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.15.tgz", + "integrity": "sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.7", - "@babel/helper-define-polyfill-provider": "^0.6.5", + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.6", "semver": "^6.3.1" }, "peerDependencies": { @@ -7089,13 +6140,13 @@ } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", - "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.6.tgz", + "integrity": "sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5" + "@babel/helper-define-polyfill-provider": "^0.6.6" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -7108,50 +6159,6 @@ "dev": true, "license": "MIT" }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/babel-preset-jest": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", - "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^26.6.2", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": ">= 10.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -7226,20 +6233,10 @@ ], "license": "MIT" }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, "node_modules/baseline-browser-mapping": { - "version": "2.9.7", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.7.tgz", - "integrity": "sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==", + "version": "2.9.18", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.18.tgz", + "integrity": "sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -7263,16 +6260,6 @@ "tweetnacl": "^0.14.3" } }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "dev": true, - "license": "Unlicense", - "engines": { - "node": ">=0.6" - } - }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -7283,26 +6270,13 @@ "node": "*" } }, - "node_modules/binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" - }, - "engines": { - "node": "*" - } - }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "license": "MIT", + "optional": true, "engines": { "node": ">=8" }, @@ -7321,22 +6295,10 @@ "file-uri-to-path": "1.0.0" } }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/bluebird": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true, "license": "MIT" }, @@ -7389,6 +6351,22 @@ "dev": true, "license": "MIT" }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/bonjour": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", @@ -7434,6 +6412,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/boxen/node_modules/chalk": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", @@ -7448,6 +6442,49 @@ "node": ">=8" } }, + "node_modules/boxen/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/boxen/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/boxen/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/boxen/node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -7489,13 +6526,6 @@ "dev": true, "license": "MIT" }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/browserify-aes": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", @@ -7572,53 +6602,6 @@ "node": ">= 0.10" } }, - "node_modules/browserify-sign/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/browserify-sign/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/browserify-sign/node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/browserify-sign/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/browserify-sign/node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, "node_modules/browserify-zlib": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", @@ -7663,62 +6646,16 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "license": "MIT", "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" } }, "node_modules/buffer-from": { @@ -7735,16 +6672,6 @@ "dev": true, "license": "MIT" }, - "node_modules/buffer-indexof-polyfill": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", - "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, "node_modules/buffer-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/buffer-json/-/buffer-json-2.0.0.tgz", @@ -7759,14 +6686,12 @@ "dev": true, "license": "MIT" }, - "node_modules/buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "node_modules/buffer/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true, - "engines": { - "node": ">=0.2.0" - } + "license": "MIT" }, "node_modules/builtin-status-codes": { "version": "3.0.0", @@ -7919,6 +6844,7 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { @@ -8054,10 +6980,26 @@ "node": ">=8" } }, - "node_modules/cacheable-request/node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==", + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==", "dev": true, "license": "MIT" }, @@ -8229,9 +7171,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001760", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz", - "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==", + "version": "1.0.30001766", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz", + "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==", "dev": true, "funding": [ { @@ -8249,19 +7191,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "license": "ISC", - "dependencies": { - "rsvp": "^4.8.4" - }, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -8269,19 +7198,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", - "dev": true, - "license": "MIT/X11", - "dependencies": { - "traverse": ">=0.3.0 <0.4" - }, - "engines": { - "node": "*" - } - }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -8295,6 +7211,8 @@ }, "engines": { "node": ">=4" +<<<<<<< HEAD +======= } }, "node_modules/chalk/node_modules/ansi-styles": { @@ -8368,6 +7286,7 @@ "license": "MIT", "engines": { "node": ">=10" +>>>>>>> develop } }, "node_modules/chevrotain": { @@ -8385,6 +7304,7 @@ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -8446,13 +7366,6 @@ "node": ">= 0.10" } }, - "node_modules/cjs-module-lexer": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", - "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", - "dev": true, - "license": "MIT" - }, "node_modules/class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -8506,30 +7419,85 @@ } }, "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "license": "ISC", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" } }, "node_modules/cliui/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" } }, "node_modules/clone-deep": { @@ -8560,17 +7528,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, "node_modules/coa": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", @@ -8586,6 +7543,8 @@ "node": ">= 4.0" } }, +<<<<<<< HEAD +======= "node_modules/collect-v8-coverage": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", @@ -8593,6 +7552,7 @@ "dev": true, "license": "MIT" }, +>>>>>>> develop "node_modules/collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -8619,22 +7579,19 @@ } }, "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "color-name": "1.1.3" } }, "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true, "license": "MIT" }, @@ -8649,23 +7606,6 @@ "simple-swizzle": "^0.2.2" } }, - "node_modules/color/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", @@ -8720,22 +7660,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/compress-commons": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", - "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -8785,16 +7709,6 @@ "dev": true, "license": "MIT" }, - "node_modules/compression/node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -8818,46 +7732,6 @@ "typedarray": "^0.0.6" } }, - "node_modules/concat-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/concat-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/configstore": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", @@ -8892,22 +7766,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, "node_modules/connect-history-api-fallback": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", @@ -8918,23 +7776,6 @@ "node": ">=0.8" } }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/connect/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", @@ -9103,13 +7944,6 @@ "node": ">=0.10.0" } }, - "node_modules/copy-webpack-plugin/node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true, - "license": "MIT" - }, "node_modules/copy-webpack-plugin/node_modules/cacache": { "version": "12.0.4", "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", @@ -9330,9 +8164,9 @@ } }, "node_modules/core-js": { - "version": "3.47.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", - "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -9342,13 +8176,13 @@ } }, "node_modules/core-js-compat": { - "version": "3.47.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.47.0.tgz", - "integrity": "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==", + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.48.0.tgz", + "integrity": "sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.28.0" + "browserslist": "^4.28.1" }, "funding": { "type": "opencollective", @@ -9356,26 +8190,12 @@ } }, "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true, "license": "MIT" }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/cosmiconfig": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", @@ -9440,37 +8260,10 @@ "node": ">=4" } }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/crc32-stream": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", - "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", "dev": true, "license": "MIT", "dependencies": { @@ -10035,34 +8828,6 @@ "dev": true, "license": "CC0-1.0" }, - "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true, - "license": "MIT" - }, - "node_modules/cssstyle": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", - "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@asamuzakjp/css-color": "^3.2.0", - "rrweb-cssom": "^0.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/cssstyle/node_modules/rrweb-cssom": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", - "dev": true, - "license": "MIT" - }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -10070,13 +8835,6 @@ "dev": true, "license": "MIT" }, - "node_modules/custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", - "dev": true, - "license": "MIT" - }, "node_modules/cyclist": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.2.tgz", @@ -10097,20 +8855,6 @@ "node": ">=0.10" } }, - "node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -10165,23 +8909,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/date-format": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", - "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/dayjs": { - "version": "1.11.19", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", - "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", - "dev": true, - "license": "MIT" - }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", @@ -10228,13 +8955,6 @@ "node": ">=0.10.0" } }, - "node_modules/decimal.js": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", - "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", - "dev": true, - "license": "MIT" - }, "node_modules/decode-uri-component": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", @@ -10297,9 +9017,9 @@ "license": "MIT" }, "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", + "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", "dev": true, "license": "MIT", "engines": { @@ -10320,134 +9040,6 @@ "node": ">=6" } }, - "node_modules/default-gateway/node_modules/cross-spawn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/default-gateway/node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/default-gateway/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/default-gateway/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-gateway/node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/default-gateway/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/default-gateway/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/default-gateway/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-gateway/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-gateway/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/defer-to-connect": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", @@ -10629,16 +9221,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/detect-node": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", @@ -10657,33 +9239,16 @@ "wrappy": "1" } }, - "node_modules/di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", - "dev": true, - "license": "MIT" - }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, - "node_modules/diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.14.2" - } - }, "node_modules/diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -10761,29 +9326,6 @@ "zepto": "^1.2.0" } }, - "node_modules/docsearch.js/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/docsearch.js/node_modules/stack-utils": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz", - "integrity": "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -10807,19 +9349,6 @@ "utila": "~0.4" } }, - "node_modules/dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, "node_modules/dom-serializer": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", @@ -10878,30 +9407,6 @@ "dev": true, "license": "BSD-2-Clause" }, - "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "deprecated": "Use your platform's native DOMException instead", - "dev": true, - "license": "MIT", - "dependencies": { - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=8" - } - }, "node_modules/domhandler": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", @@ -10970,62 +9475,12 @@ "node": ">= 0.4" } }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "node_modules/duplexer3": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", + "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "readable-stream": "^2.0.2" - } - }, - "node_modules/duplexer2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/duplexer2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/duplexer2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/duplexer2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/duplexer3": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", - "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", - "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause" }, "node_modules/duplexify": { "version": "3.7.1", @@ -11040,53 +9495,6 @@ "stream-shift": "^1.0.0" } }, - "node_modules/duplexify/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/duplexify/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/duplexify/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/duplexify/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -11106,9 +9514,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.267", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", - "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "version": "1.5.279", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.279.tgz", + "integrity": "sha512-0bblUU5UNdOt5G7XqGiJtpZMONma6WAfq9vsFmtn9x1+joAObr6x1chfqyxFSDCAFwFhCQDrqeAr6MYdpwJ9Hg==", "dev": true, "license": "ISC" }, @@ -11135,19 +9543,6 @@ "dev": true, "license": "MIT" }, - "node_modules/emittery": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", - "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -11166,9 +9561,9 @@ } }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "dev": true, "license": "MIT", "engines": { @@ -11185,77 +9580,6 @@ "once": "^1.4.0" } }, - "node_modules/engine.io": { - "version": "6.6.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", - "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.7.2", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/enhanced-resolve": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", @@ -11270,26 +9594,10 @@ "node": ">=6.9.0" } }, - "node_modules/ent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.2.tgz", - "integrity": "sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "punycode": "^1.4.1", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -11822,48 +10130,13 @@ "license": "MIT" }, "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" + "node": ">=0.8.0" } }, "node_modules/eslint": { @@ -11936,43 +10209,6 @@ "eslint": ">=7.0.0" } }, - "node_modules/eslint-plugin-jasmine": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jasmine/-/eslint-plugin-jasmine-4.2.2.tgz", - "integrity": "sha512-nALbewRk63uz28UGNhUTJyd6GofXxVNFpWFNAwr9ySc6kpSRIoO4suwZqIYz3cfJmCacilmjp7+1Ocjr7zRagA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8", - "npm": ">=6" - } - }, - "node_modules/eslint-plugin-jest": { - "version": "27.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz", - "integrity": "sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/utils": "^5.10.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0 || ^7.0.0", - "eslint": "^7.0.0 || ^8.0.0", - "jest": "*" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } - } - }, "node_modules/eslint-plugin-jsdoc": { "version": "50.8.0", "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.8.0.tgz", @@ -11998,6 +10234,19 @@ "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" } }, + "node_modules/eslint-plugin-jsdoc/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", @@ -12053,14 +10302,14 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", - "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", + "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", "dev": true, "license": "MIT", "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.11.7" + "prettier-linter-helpers": "^1.0.1", + "synckit": "^0.11.12" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -12110,6 +10359,25 @@ "url": "https://opencollective.com/eslint" } }, +<<<<<<< HEAD + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, +======= +>>>>>>> develop "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12127,28 +10395,64 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", +<<<<<<< HEAD + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "color-name": "~1.1.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=7.0.0" } }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, +======= +>>>>>>> develop + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -12167,6 +10471,32 @@ "node": ">=10.13.0" } }, +<<<<<<< HEAD + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, +======= +>>>>>>> develop "node_modules/esm": { "version": "3.2.25", "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", @@ -12210,9 +10540,9 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -12330,65 +10660,96 @@ "safe-buffer": "^5.1.1" } }, - "node_modules/exceljs": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz", - "integrity": "sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==", + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "dev": true, "license": "MIT", "dependencies": { - "archiver": "^5.0.0", - "dayjs": "^1.8.34", - "fast-csv": "^4.3.1", - "jszip": "^3.10.1", - "readable-stream": "^3.6.0", - "saxes": "^5.0.1", - "tmp": "^0.2.0", - "unzipper": "^0.10.11", - "uuid": "^8.3.0" + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" }, "engines": { - "node": ">=8.3.0" + "node": ">=6" } }, - "node_modules/exec-sh": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", - "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", + "node_modules/execa/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } }, - "node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "node_modules/execa/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/execa/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" + "shebang-regex": "^1.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "node_modules/execa/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" } }, "node_modules/expand-brackets": { @@ -12427,24 +10788,6 @@ "dev": true, "license": "MIT" }, - "node_modules/expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, "node_modules/express": { "version": "4.22.1", "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", @@ -12509,35 +10852,6 @@ "ms": "2.0.0" } }, - "node_modules/express/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/express/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -12545,14 +10859,20 @@ "dev": true, "license": "MIT" }, - "node_modules/express/node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "node_modules/express/node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/extend": { @@ -12632,20 +10952,6 @@ ], "license": "MIT" }, - "node_modules/fast-csv": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", - "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@fast-csv/format": "4.3.5", - "@fast-csv/parse": "4.3.6" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -12702,9 +11008,9 @@ } }, "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "dev": true, "license": "ISC", "dependencies": { @@ -12724,26 +11030,6 @@ "node": ">=0.8.0" } }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } - }, "node_modules/figgy-pudding": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", @@ -12768,16 +11054,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -12873,18 +11149,18 @@ } }, "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", "dev": true, "license": "MIT", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "~2.0.2", "unpipe": "~1.0.0" }, "engines": { @@ -12908,19 +11184,6 @@ "dev": true, "license": "MIT" }, - "node_modules/finalhandler/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "dev": true, - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/find-cache-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", @@ -13013,50 +11276,10 @@ "readable-stream": "^2.3.6" } }, - "node_modules/flush-write-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/flush-write-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/flush-write-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/flush-write-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "dev": true, "funding": [ { @@ -13107,36 +11330,6 @@ "dev": true, "license": "MIT" }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -13148,20 +11341,18 @@ } }, "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", + "combined-stream": "^1.0.6", "mime-types": "^2.1.12" }, "engines": { - "node": ">= 6" + "node": ">= 0.12" } }, "node_modules/forwarded": { @@ -13208,66 +11399,20 @@ "readable-stream": "^2.0.0" } }, - "node_modules/from2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/from2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/from2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/from2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true, - "license": "MIT" - }, "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "license": "MIT", "dependencies": { + "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=10" } }, "node_modules/fs-minipass": { @@ -13324,46 +11469,6 @@ "readable-stream": "1 || 2" } }, - "node_modules/fs-write-stream-atomic/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/fs-write-stream-atomic/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/fs-write-stream-atomic/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/fs-write-stream-atomic/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -13386,52 +11491,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - }, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/fstream/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/full-icu": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/full-icu/-/full-icu-1.5.0.tgz", - "integrity": "sha512-BxB2otKUSFyvENjbI8EtQscpiPOEnhrf5V4MVpa6PjzsrLmdKKUUhulbydsfKS4ve6cGXNVRLlrOjizby/ZfDA==", - "dev": true, - "hasInstallScript": true, - "license": "Unicode-DFS-2016", - "dependencies": { - "yauzl": "^2.10.0" - }, - "bin": { - "full-icu": "node-full-icu.js", - "node-full-icu-path": "node-icu-data.js" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -13528,16 +11587,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -13553,19 +11602,16 @@ } }, "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "license": "MIT", "dependencies": { "pump": "^3.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, "node_modules/get-symbol-description": { @@ -13775,19 +11821,6 @@ "node": ">=8.6" } }, - "node_modules/got/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -13842,14 +11875,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -13928,13 +11953,13 @@ } }, "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/has-property-descriptors": { @@ -14217,84 +12242,24 @@ "wbuf": "^1.1.0" } }, - "node_modules/hpack.js/node_modules/isarray": { + "node_modules/hsl-regex": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha512-M5ezZw4LzXbBKMruP+BNANf0k+19hDQMgpzBIYnya//Al+fjNct9Wf3b1WedLqdEs2hKBvxq/jh+DsHJLj0F9A==", "dev": true, "license": "MIT" }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA==", "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } + "license": "MIT" }, - "node_modules/hpack.js/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha512-M5ezZw4LzXbBKMruP+BNANf0k+19hDQMgpzBIYnya//Al+fjNct9Wf3b1WedLqdEs2hKBvxq/jh+DsHJLj0F9A==", - "dev": true, - "license": "MIT" - }, - "node_modules/hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA==", - "dev": true, - "license": "MIT" - }, - "node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-encoding": "^3.1.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/html-entities": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", - "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "node_modules/html-entities": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==", "dev": true, "license": "MIT" }, @@ -14469,16 +12434,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/http-parser-js": { "version": "0.5.10", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", @@ -14501,20 +12456,6 @@ "node": ">=8.0.0" } }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/http-proxy-middleware": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz", @@ -14555,30 +12496,6 @@ "dev": true, "license": "MIT" }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8.12.0" - } - }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -14676,9 +12593,9 @@ } }, "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", + "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==", "dev": true, "license": "MIT" }, @@ -15067,6 +12984,7 @@ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -15227,22 +13145,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -15289,16 +13191,6 @@ "node": ">=8" } }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/is-generator-function": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", @@ -15494,13 +13386,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT" - }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -15557,16 +13442,13 @@ } }, "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, "node_modules/is-string": { @@ -15684,16 +13566,13 @@ } }, "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", "dev": true, "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/is-yarn-global": { @@ -15710,19 +13589,6 @@ "dev": true, "license": "MIT" }, - "node_modules/isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -15747,189 +13613,6 @@ "dev": true, "license": "MIT" }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/istanbul-lib-report/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jasmine": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.13.0.tgz", - "integrity": "sha512-oLCXIhEb5e0zzjn9GyuvcuisvLBwUjmgz7a0RNGWKwQtJCDld4m+vwKUpAIJVLB5vbmQFdtKhT86/tIZlJ5gYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob": "^10.2.2", - "jasmine-core": "~5.13.0" - }, - "bin": { - "jasmine": "bin/jasmine.js" - } - }, - "node_modules/jasmine-core": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.13.0.tgz", - "integrity": "sha512-vsYjfh7lyqvZX5QgqKc4YH8phs7g96Z8bsdIFNEU3VqXhlHaq+vov/Fgn/sr6MiUczdZkyXRC3TX369Ll4Nzbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/jasmine/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/jasmine/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jasmine/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/javascript-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz", @@ -15937,6 +13620,8 @@ "dev": true, "license": "MIT" }, +<<<<<<< HEAD +======= "node_modules/jest": { "version": "26.6.3", "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", @@ -17074,6 +14759,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, +>>>>>>> develop "node_modules/jest-worker": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", @@ -17089,14 +14775,37 @@ "node": ">= 10.13.0" } }, - "node_modules/js-tokens": { + "node_modules/jest-worker/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", @@ -17126,60 +14835,6 @@ "node": ">=12.0.0" } }, - "node_modules/jsdom": { - "version": "25.0.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", - "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssstyle": "^4.1.0", - "data-urls": "^5.0.0", - "decimal.js": "^10.4.3", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.5", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.12", - "parse5": "^7.1.2", - "rrweb-cssom": "^0.7.1", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^5.0.0", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0", - "ws": "^8.18.0", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^2.11.2" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "license": "ISC", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -17256,11 +14911,14 @@ } }, "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "dev": true, "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -17281,496 +14939,400 @@ "node": ">=0.6.0" } }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "license": "(MIT OR GPL-3.0-or-later)", + "license": "MIT", "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" + "json-buffer": "3.0.1" } }, - "node_modules/jszip/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "node_modules/killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/jszip/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/jszip/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/jszip/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/last-call-webpack-plugin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz", + "integrity": "sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==", "dev": true, "license": "MIT", "dependencies": { - "safe-buffer": "~5.1.0" + "lodash": "^4.17.5", + "webpack-sources": "^1.1.0" } }, - "node_modules/karma": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", - "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", + "node_modules/latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", "dev": true, "license": "MIT", "dependencies": { - "@colors/colors": "1.5.0", - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.5.1", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.7", - "graceful-fs": "^4.2.6", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "log4js": "^6.4.1", - "mime": "^2.5.2", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.5", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^4.7.2", - "source-map": "^0.6.1", - "tmp": "^0.2.1", - "ua-parser-js": "^0.7.30", - "yargs": "^16.1.1" - }, - "bin": { - "karma": "bin/karma" + "package-json": "^6.3.0" }, "engines": { - "node": ">= 10" + "node": ">=8" } }, - "node_modules/karma-chrome-launcher": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz", - "integrity": "sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { - "which": "^1.2.1" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/karma-chrome-launcher/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/license-checker": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz", + "integrity": "sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g==", "dev": true, - "license": "ISC", + "license": "BSD-3-Clause", "dependencies": { - "isexe": "^2.0.0" + "chalk": "^2.4.1", + "debug": "^3.1.0", + "mkdirp": "^0.5.1", + "nopt": "^4.0.1", + "read-installed": "~4.0.3", + "semver": "^5.5.0", + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0", + "spdx-satisfies": "^4.0.0", + "treeify": "^1.1.0" }, "bin": { - "which": "bin/which" + "license-checker": "bin/license-checker" } }, - "node_modules/karma-firefox-launcher": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-2.1.3.tgz", - "integrity": "sha512-LMM2bseebLbYjODBOVt7TCPP9OI2vZIXCavIXhkO9m+10Uj5l7u/SKoeRmYx8FYHTVGZSpk6peX+3BMHC1WwNw==", + "node_modules/license-checker/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", "dependencies": { - "is-wsl": "^2.2.0", - "which": "^3.0.0" + "ms": "^2.1.1" } }, - "node_modules/karma-firefox-launcher/node_modules/which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "node_modules/license-checker/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "semver": "bin/semver" } }, - "node_modules/karma-jasmine": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", - "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==", + "node_modules/license-checker/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "license": "MIT", "dependencies": { - "jasmine-core": "^4.1.0" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "karma": "^6.0.0" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/karma-jasmine-html-reporter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.1.0.tgz", - "integrity": "sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==", +<<<<<<< HEAD +======= + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", "dev": true, "license": "MIT", - "peerDependencies": { - "jasmine-core": "^4.0.0 || ^5.0.0", - "karma": "^6.0.0", - "karma-jasmine": "^5.0.0" + "dependencies": { + "immediate": "~3.0.5" } }, - "node_modules/karma-jasmine/node_modules/jasmine-core": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.6.1.tgz", - "integrity": "sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==", + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true, "license": "MIT" }, - "node_modules/karma-sourcemap-loader": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/karma-sourcemap-loader/-/karma-sourcemap-loader-0.4.0.tgz", - "integrity": "sha512-xCRL3/pmhAYF3I6qOrcn0uhbQevitc2DERMPH82FMnG+4WReoGcGFZb1pURf2a5apyrOHRdvD+O6K7NljqKHyA==", +>>>>>>> develop + "node_modules/linkify-it": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", + "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.10" + "uc.micro": "^1.0.1" } }, - "node_modules/karma-webpack": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-4.0.2.tgz", - "integrity": "sha512-970/okAsdUOmiMOCY8sb17A2I8neS25Ad9uhyK3GHgmRSIFJbDcNEFE8dqqUhNe9OHiCC9k3DMrSmtd/0ymP1A==", + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, "license": "MIT", "dependencies": { - "clone-deep": "^4.0.1", - "loader-utils": "^1.1.0", - "neo-async": "^2.6.1", - "schema-utils": "^1.0.0", - "source-map": "^0.7.3", - "webpack-dev-middleware": "^3.7.0" + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" }, "engines": { - "node": ">= 8.9.0" - }, - "peerDependencies": { - "webpack": "^4.0.0" - } - }, - "node_modules/karma-webpack/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" + "node": ">=4" } }, - "node_modules/karma-webpack/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, "engines": { - "node": ">=4.0.0" + "node": ">=4" } }, - "node_modules/karma-webpack/node_modules/schema-utils": { + "node_modules/load-script": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", + "integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==", "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - }, - "engines": { - "node": ">= 4" - } + "license": "MIT" }, - "node_modules/karma-webpack/node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "node_modules/loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "engines": { - "node": ">= 12" + "node": ">=4.3.0 <5.0.0 || >=5.10" } }, - "node_modules/karma/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" } }, - "node_modules/karma/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "glob": "^7.1.3" + "p-locate": "^5.0.0" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/karma/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } + "license": "MIT" }, - "node_modules/karma/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==", "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } + "license": "MIT" }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "node_modules/lodash.chunk": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz", + "integrity": "sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w==", "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } + "license": "MIT" }, - "node_modules/killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } + "license": "MIT" }, - "node_modules/last-call-webpack-plugin": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz", - "integrity": "sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==", + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.5", - "webpack-sources": "^1.1.0" - } + "license": "MIT" }, - "node_modules/latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "deprecated": "This package is deprecated. Use https://socket.dev/npm/package/eta instead.", "dev": true, "license": "MIT", "dependencies": { - "package-json": "^6.3.0" - }, - "engines": { - "node": ">=8" + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" } }, - "node_modules/lazystream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "node_modules/lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", "dev": true, "license": "MIT", "dependencies": { - "readable-stream": "^2.0.5" - }, - "engines": { - "node": ">= 0.6.3" + "lodash._reinterpolate": "^3.0.0" } }, - "node_modules/lazystream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "dev": true, "license": "MIT" }, - "node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", "dev": true, "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" } }, - "node_modules/lazystream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", "dev": true, "license": "MIT" }, - "node_modules/lazystream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true, "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", "dependencies": { - "safe-buffer": "~5.1.0" + "yallist": "^3.0.2" } }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "pify": "^4.0.1", + "semver": "^5.6.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=6" } }, - "node_modules/license-checker": { - "version": "25.0.1", - "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz", - "integrity": "sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "chalk": "^2.4.1", - "debug": "^3.1.0", - "mkdirp": "^0.5.1", - "nopt": "^4.0.1", - "read-installed": "~4.0.3", - "semver": "^5.5.0", - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0", - "spdx-satisfies": "^4.0.0", - "treeify": "^1.1.0" - }, - "bin": { - "license-checker": "bin/license-checker" - } - }, - "node_modules/license-checker/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/license-checker/node_modules/semver": { + "node_modules/make-dir/node_modules/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", @@ -17780,3646 +15342,2139 @@ "semver": "bin/semver" } }, - "node_modules/license-checker/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", - "dev": true, - "license": "MIT", - "dependencies": { - "uc.micro": "^1.0.1" - } - }, - "node_modules/listenercount": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", - "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true, "license": "ISC" - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/load-script": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", - "integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==", - "dev": true, - "license": "MIT" - }, - "node_modules/loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.chunk": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz", - "integrity": "sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.groupby": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", - "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isfunction": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", - "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isnil": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", - "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isundefined": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", - "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.padstart": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", - "integrity": "sha512-sW73O6S8+Tg66eY56DBk85aQzzUJDtpoXFBgELMd5P/SotAguo+1kYO6RuYgXxA4HJH3LFTFPASX6ET6bjfriw==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.template": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", - "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", - "deprecated": "This package is deprecated. Use https://socket.dev/npm/package/eta instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "node_modules/lodash.templatesettings": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash._reinterpolate": "^3.0.0" - } - }, - "node_modules/lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/log4js": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", - "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "flatted": "^3.2.7", - "rfdc": "^1.3.0", - "streamroller": "^3.1.5" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/loglevel": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", - "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - }, - "funding": { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/loglevel" - } - }, - "node_modules/lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/markdown-it": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", - "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "entities": "~1.1.1", - "linkify-it": "^2.0.0", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/markdown-it-anchor": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz", - "integrity": "sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==", - "dev": true, - "license": "Unlicense", - "peerDependencies": { - "markdown-it": "*" - } - }, - "node_modules/markdown-it-chain": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/markdown-it-chain/-/markdown-it-chain-1.3.0.tgz", - "integrity": "sha512-XClV8I1TKy8L2qsT9iX3qiV+50ZtcInGXI80CA+DP62sMs7hXlyV/RM3hfwy5O3Ad0sJm9xIwQELgANfESo8mQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "webpack-chain": "^4.9.0" - }, - "engines": { - "node": ">=6.9" - }, - "peerDependencies": { - "markdown-it": ">=5.0.0" - } - }, - "node_modules/markdown-it-chain/node_modules/deepmerge": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", - "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/markdown-it-chain/node_modules/javascript-stringify": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-1.6.0.tgz", - "integrity": "sha512-fnjC0up+0SjEJtgmmG+teeel68kutkvzfctO/KxE3qJlbunkJYAshgH3boU++gSBHP8z5/r0ts0qRIrHf0RTQQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/markdown-it-chain/node_modules/webpack-chain": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-4.12.1.tgz", - "integrity": "sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "deepmerge": "^1.5.2", - "javascript-stringify": "^1.6.0" - } - }, - "node_modules/markdown-it-container": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-it-container/-/markdown-it-container-2.0.0.tgz", - "integrity": "sha512-IxPOaq2LzrGuFGyYq80zaorXReh2ZHGFOB1/Hen429EJL1XkPI3FJTpx9TsJeua+j2qTru4h3W1TiCRdeivMmA==", - "dev": true, - "license": "MIT" - }, - "node_modules/markdown-it-emoji": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz", - "integrity": "sha512-QCz3Hkd+r5gDYtS2xsFXmBYrgw6KuWcJZLCEkdfAuwzZbShCmCfta+hwAMq4NX/4xPzkSHduMKgMkkPUJxSXNg==", - "dev": true, - "license": "MIT" - }, - "node_modules/markdown-it-footnote": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/markdown-it-footnote/-/markdown-it-footnote-4.0.0.tgz", - "integrity": "sha512-WYJ7urf+khJYl3DqofQpYfEYkZKbmXmwxQV8c8mO/hGIhgZ1wOe7R4HLFNwqx7TjILbnC98fuyeSsin19JdFcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/markdown-it-regex": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/markdown-it-regex/-/markdown-it-regex-0.2.2.tgz", - "integrity": "sha512-Db/QKAwTuDZT0wVNA5WNI2lZOSDyA6xcOax9KxWlsVqGJKw/hd6s8WRycwzvAvjaMy/0EYg0GmrMvIGEiBRwyQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/markdown-it-table-of-contents": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/markdown-it-table-of-contents/-/markdown-it-table-of-contents-0.4.4.tgz", - "integrity": "sha512-TAIHTHPwa9+ltKvKPWulm/beozQU41Ab+FIefRaQV1NRnpzwcV9QOe6wXQS5WLivm5Q/nlo0rl6laGkMDZE7Gw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">6.4.0" - } - }, - "node_modules/markdown-it/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/markdown-it/node_modules/entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/markdown-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", - "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "repeat-string": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/marked": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.9.tgz", - "integrity": "sha512-H8lIX2SvyitGX+TRdtS06m1jHMijKN/XjfH6Ooii9fvxMlh8QdqBfBDkGUpMWH2kQNrtixjzYUa3SH8ROTgRRw==", - "dev": true, - "license": "MIT", - "bin": { - "marked": "bin/marked" - }, - "engines": { - "node": ">= 8.16.2" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true, - "license": "MIT" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", - "dev": true, - "license": "MIT", - "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - }, - "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" - } - }, - "node_modules/memory-fs/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/memory-fs/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/memory-fs/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/memory-fs/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "license": "MIT", - "dependencies": { - "source-map": "^0.6.1" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "bin": { - "miller-rabin": "bin/miller-rabin" - } - }, - "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/min-document": { - "version": "2.19.2", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.2.tgz", - "integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "dom-walk": "^0.1.0" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.6.0.tgz", - "integrity": "sha512-79q5P7YGI6rdnVyIAV4NXpBQJFWdkzJxCim3Kog4078fM0piAaFlwocqbejdWtLW1cEzCexPrh6EdyFsPgVdAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "loader-utils": "^1.1.0", - "normalize-url": "^2.0.1", - "schema-utils": "^1.0.0", - "webpack-sources": "^1.1.0" - }, - "engines": { - "node": ">= 6.9.0" - }, - "peerDependencies": { - "webpack": "^4.4.0" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true, - "license": "ISC" - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "dev": true, - "license": "MIT" - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-flush/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, - "node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mixin-deep/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", - "dependencies": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - } - }, - "node_modules/move-concurrently/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/nan": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", - "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanomatch/node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanomatch/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanomatch/node_modules/is-descriptor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", - "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/nanomatch/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true, - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "lower-case": "^1.1.1" - } - }, - "node_modules/node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", - "dev": true, - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.1", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" - } - }, - "node_modules/node-libs-browser/node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "node_modules/node-libs-browser/node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/node-libs-browser/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-libs-browser/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/node-libs-browser/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-libs-browser/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/node-notifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz", - "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "growly": "^1.3.0", - "is-wsl": "^2.2.0", - "semver": "^7.3.2", - "shellwords": "^0.1.1", - "uuid": "^8.3.0", - "which": "^2.0.2" - } - }, - "node_modules/node-notifier/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "1", - "osenv": "^0.1.4" - }, - "bin": { - "nopt": "bin/nopt.js" - } - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", "dev": true, "license": "MIT", + "dependencies": { + "object-visit": "^1.0.0" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/normalize-url": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", - "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", + "node_modules/markdown-it": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", + "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", "dev": true, "license": "MIT", "dependencies": { - "prepend-http": "^2.0.0", - "query-string": "^5.0.1", - "sort-keys": "^2.0.0" + "argparse": "^1.0.7", + "entities": "~1.1.1", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" }, - "engines": { - "node": ">=4" + "bin": { + "markdown-it": "bin/markdown-it.js" } }, - "node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "node_modules/markdown-it-anchor": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz", + "integrity": "sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==", "dev": true, - "license": "ISC" + "license": "Unlicense", + "peerDependencies": { + "markdown-it": "*" + } }, - "node_modules/npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "node_modules/markdown-it-chain": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/markdown-it-chain/-/markdown-it-chain-1.3.0.tgz", + "integrity": "sha512-XClV8I1TKy8L2qsT9iX3qiV+50ZtcInGXI80CA+DP62sMs7hXlyV/RM3hfwy5O3Ad0sJm9xIwQELgANfESo8mQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" - }, - "bin": { - "npm-run-all": "bin/npm-run-all/index.js", - "run-p": "bin/run-p/index.js", - "run-s": "bin/run-s/index.js" + "webpack-chain": "^4.9.0" }, "engines": { - "node": ">= 4" + "node": ">=6.9" + }, + "peerDependencies": { + "markdown-it": ">=5.0.0" } }, - "node_modules/npm-run-all/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/markdown-it-chain/node_modules/javascript-stringify": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-1.6.0.tgz", + "integrity": "sha512-fnjC0up+0SjEJtgmmG+teeel68kutkvzfctO/KxE3qJlbunkJYAshgH3boU++gSBHP8z5/r0ts0qRIrHf0RTQQ==", "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } + "license": "MIT" }, - "node_modules/npm-run-all/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/markdown-it-chain/node_modules/webpack-chain": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-4.12.1.tgz", + "integrity": "sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, - "license": "MIT", + "license": "MPL-2.0", "dependencies": { - "color-name": "1.1.3" + "deepmerge": "^1.5.2", + "javascript-stringify": "^1.6.0" } }, - "node_modules/npm-run-all/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "node_modules/markdown-it-container": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-container/-/markdown-it-container-2.0.0.tgz", + "integrity": "sha512-IxPOaq2LzrGuFGyYq80zaorXReh2ZHGFOB1/Hen429EJL1XkPI3FJTpx9TsJeua+j2qTru4h3W1TiCRdeivMmA==", "dev": true, "license": "MIT" }, - "node_modules/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "node_modules/markdown-it-emoji": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz", + "integrity": "sha512-QCz3Hkd+r5gDYtS2xsFXmBYrgw6KuWcJZLCEkdfAuwzZbShCmCfta+hwAMq4NX/4xPzkSHduMKgMkkPUJxSXNg==", "dev": true, - "license": "MIT", - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } + "license": "MIT" }, - "node_modules/npm-run-all/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "node_modules/markdown-it-footnote": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-footnote/-/markdown-it-footnote-4.0.0.tgz", + "integrity": "sha512-WYJ7urf+khJYl3DqofQpYfEYkZKbmXmwxQV8c8mO/hGIhgZ1wOe7R4HLFNwqx7TjILbnC98fuyeSsin19JdFcQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } + "license": "MIT" }, - "node_modules/npm-run-all/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/markdown-it-regex": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/markdown-it-regex/-/markdown-it-regex-0.2.2.tgz", + "integrity": "sha512-Db/QKAwTuDZT0wVNA5WNI2lZOSDyA6xcOax9KxWlsVqGJKw/hd6s8WRycwzvAvjaMy/0EYg0GmrMvIGEiBRwyQ==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } + "license": "MIT" }, - "node_modules/npm-run-all/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "node_modules/markdown-it-table-of-contents": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/markdown-it-table-of-contents/-/markdown-it-table-of-contents-0.4.4.tgz", + "integrity": "sha512-TAIHTHPwa9+ltKvKPWulm/beozQU41Ab+FIefRaQV1NRnpzwcV9QOe6wXQS5WLivm5Q/nlo0rl6laGkMDZE7Gw==", "dev": true, "license": "MIT", - "dependencies": { - "shebang-regex": "^1.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">6.4.0" } }, - "node_modules/npm-run-all/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "node_modules/markdown-it/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "sprintf-js": "~1.0.2" } }, - "node_modules/npm-run-all/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/markdown-it/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", "dev": true, - "license": "ISC", + "license": "BSD-2-Clause" + }, + "node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" + "repeat-string": "^1.0.0" }, - "bin": { - "which": "bin/which" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "node_modules/marked": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.9.tgz", + "integrity": "sha512-H8lIX2SvyitGX+TRdtS06m1jHMijKN/XjfH6Ooii9fvxMlh8QdqBfBDkGUpMWH2kQNrtixjzYUa3SH8ROTgRRw==", "dev": true, "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" + "bin": { + "marked": "bin/marked" }, "engines": { - "node": ">=8" + "node": ">= 8.16.2" } }, - "node_modules/nprogress": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", - "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==", + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "node_modules/nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "boolbase": "~1.0.0" + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" } }, - "node_modules/num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==", + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", "dev": true, - "license": "MIT" + "license": "CC0-1.0" }, - "node_modules/nwsapi": { - "version": "2.2.23", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", - "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true, "license": "MIT" }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "node_modules/memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", "dev": true, "license": "MIT", "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" + "errno": "^0.1.3", + "readable-stream": "^2.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=4.3.0 <5.0.0 || >=5.10" } }, - "node_modules/object-copy/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", "dev": true, - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.10.0" } }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" - }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "node_modules/merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "source-map": "^0.6.1" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">= 8" } }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true, "license": "MIT", - "dependencies": { - "isobject": "^3.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8.6" } }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.9.tgz", - "integrity": "sha512-mt8YM6XwsTTovI+kdZdHSxoyF2DI59up034orlC9NfweclcWOt7CVascNNLp6U+bjFVCVCIh9PwS76tDM/rH8g==", + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "dev": true, "license": "MIT", "dependencies": { - "array.prototype.reduce": "^1.0.8", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-object-atoms": "^1.1.1", - "gopd": "^1.2.0", - "safe-array-concat": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" + "bn.js": "^4.0.0", + "brorand": "^1.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "miller-rabin": "bin/miller-rabin" } }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" + "bin": { + "mime": "cli.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=4.0.0" } }, - "node_modules/object.values": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", - "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.6" } }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true, - "license": "MIT" - }, - "node_modules/on-build-webpack": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/on-build-webpack/-/on-build-webpack-0.1.0.tgz", - "integrity": "sha512-kBBhiDBQP+2wEqUe467fGw4V1aUP93ed7L1eG2TG15M6uOL4IKeW4zIU0yc6MCGmUkPVf883vPkhLEeYubkfng==", - "dev": true, - "license": "MIT" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "license": "MIT", "dependencies": { - "ee-first": "1.1.1" + "mime-db": "1.52.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, - "node_modules/on-headers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", - "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=4" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/min-document": { + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.2.tgz", + "integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "wrappy": "1" + "dom-walk": "^0.1.0" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "node_modules/mini-css-extract-plugin": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.6.0.tgz", + "integrity": "sha512-79q5P7YGI6rdnVyIAV4NXpBQJFWdkzJxCim3Kog4078fM0piAaFlwocqbejdWtLW1cEzCexPrh6EdyFsPgVdAw==", "dev": true, "license": "MIT", "dependencies": { - "mimic-fn": "^2.1.0" + "loader-utils": "^1.1.0", + "normalize-url": "^2.0.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" }, "engines": { - "node": ">=6" + "node": ">= 6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "webpack": "^4.4.0" } }, - "node_modules/opencollective-postinstall": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", - "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "node_modules/mini-css-extract-plugin/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, "bin": { - "opencollective-postinstall": "index.js" + "json5": "lib/cli.js" } }, - "node_modules/opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "node_modules/mini-css-extract-plugin/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "license": "MIT", "dependencies": { - "is-wsl": "^1.1.0" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" }, "engines": { - "node": ">=4" + "node": ">=4.0.0" } }, - "node_modules/opn/node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", "dev": true, "license": "MIT", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, "engines": { - "node": ">=4" + "node": ">= 4" } }, - "node_modules/optimize-css-assets-webpack-plugin": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.8.tgz", - "integrity": "sha512-mgFS1JdOtEGzD8l+EuISqL57cKO+We9GcoiQEmdCWRqqck+FGNmYJtx9qfAPzEz+lRrlThWMuGDaRkI/yWNx/Q==", + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "dev": true, - "license": "MIT", - "dependencies": { - "cssnano": "^4.1.10", - "last-call-webpack-plugin": "^3.0.0" - }, - "peerDependencies": { - "webpack": "^4.0.0" - } + "license": "ISC" }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 0.8.0" + "node": "*" } }, - "node_modules/os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "license": "MIT" + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "license": "MIT", + "license": "ISC", "engines": { - "node": ">=0.10.0" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/os-tmpdir": { + "node_modules/minipass-collect": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, - "node_modules/osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "deprecated": "This package is no longer supported.", + "node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, "license": "ISC", "dependencies": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "node_modules/minipass-collect/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, - "license": "MIT", + "license": "ISC" + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "license": "ISC", "dependencies": { - "get-intrinsic": "^1.2.6", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" + "minipass": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 8" } }, - "node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/p-each-series": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", - "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, - "license": "MIT", + "license": "ISC" + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/p-limit": { + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minizlib": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4.0.0" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "license": "MIT", "dependencies": { - "aggregate-error": "^3.0.0" + "is-plain-object": "^2.0.4" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/p-retry": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", - "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "license": "MIT", "dependencies": { - "retry": "^0.12.0" + "minimist": "^1.2.6" }, - "engines": { - "node": ">=6" + "bin": { + "mkdirp": "bin/cmd.js" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": "*" } }, - "node_modules/package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "node_modules/move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==", + "deprecated": "This package is no longer supported.", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "engines": { - "node": ">=8" + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "node_modules/move-concurrently/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "license": "BlueOak-1.0.0" + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "license": "(MIT AND Zlib)" + "license": "MIT" }, - "node_modules/parallel-transform": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", - "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "node_modules/multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", "dev": true, "license": "MIT", "dependencies": { - "cyclist": "^1.0.1", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" } }, - "node_modules/parallel-transform/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "node_modules/multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==", "dev": true, "license": "MIT" }, - "node_modules/parallel-transform/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/nan": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.25.0.tgz", + "integrity": "sha512-0M90Ag7Xn5KMLLZ7zliPWP3rT90P6PN+IzVFS0VqmnPktBk3700xUVv8Ikm9EUaUE5SDWdp/BIxdENzVznpm1g==", "dev": true, "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "optional": true + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/parallel-transform/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/parallel-transform/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/nanomatch/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "license": "MIT", "dependencies": { - "safe-buffer": "~5.1.0" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", + "node_modules/nanomatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", "dev": true, "license": "MIT", "dependencies": { - "no-case": "^2.2.0" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/nanomatch/node_modules/is-descriptor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", "dev": true, "license": "MIT", "dependencies": { - "callsites": "^3.0.0" + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" }, "engines": { - "node": ">=6" + "node": ">= 0.4" } }, - "node_modules/parse-asn1": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.9.tgz", - "integrity": "sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==", + "node_modules/nanomatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "asn1.js": "^4.10.1", - "browserify-aes": "^1.2.0", - "evp_bytestokey": "^1.0.3", - "pbkdf2": "^3.1.5", - "safe-buffer": "^5.2.1" + "is-plain-object": "^2.0.4" }, "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" } }, - "node_modules/parse-imports-exports": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", - "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true, - "license": "MIT", - "dependencies": { - "parse-statements": "1.0.11" - } + "license": "MIT" }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", "dev": true, "license": "MIT", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, "engines": { - "node": ">=4" + "node": ">= 0.6" } }, - "node_modules/parse-statements": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", - "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true, "license": "MIT" }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "node_modules/no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", "dev": true, "license": "MIT", "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "lower-case": "^1.1.1" } }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", "dev": true, - "license": "MIT", + "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { - "node": ">= 0.8" + "node": ">= 6.0.0" } }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "node_modules/node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node_modules/node-libs-browser/node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=0.8.x" } }, - "node_modules/path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "node_modules/node-libs-browser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", "dev": true, "license": "MIT" }, - "node_modules/path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "dev": true, "license": "MIT" }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "license": "ISC", + "dependencies": { + "abbrev": "1", + "osenv": "^0.1.4" + }, + "bin": { + "nopt": "bin/nopt.js" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "(WTFPL OR MIT)" + "license": "ISC", + "bin": { + "semver": "bin/semver" + } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "node_modules/normalize-url": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", + "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "prepend-http": "^2.0.0", + "query-string": "^5.0.1", + "sort-keys": "^2.0.0" }, "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=4" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", "dev": true, "license": "ISC" }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", "dev": true, "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, "engines": { - "node": ">=8" + "node": ">= 4" } }, - "node_modules/pbkdf2": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz", - "integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==", +<<<<<<< HEAD +======= + "node_modules/npm-run-all/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "license": "MIT", - "dependencies": { - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "ripemd160": "^2.0.3", - "safe-buffer": "^5.2.1", - "sha.js": "^2.4.12", - "to-buffer": "^1.2.1" + "dependencies": { + "color-convert": "^1.9.0" }, "engines": { - "node": ">= 0.10" + "node": ">=4" } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "node_modules/npm-run-all/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "node_modules/npm-run-all/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true, "license": "MIT" }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", +>>>>>>> develop + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=4" } }, - "node_modules/pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "MIT", + "license": "ISC", "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" + "semver": "bin/semver" } }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "pinkie": "^2.0.0" + "isexe": "^2.0.0" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "which": "bin/which" } }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "dev": true, "license": "MIT", + "dependencies": { + "path-key": "^2.0.0" + }, "engines": { - "node": ">= 6" + "node": ">=4" } }, - "node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "node_modules/npm-run-path/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "license": "MIT", - "dependencies": { - "find-up": "^3.0.0" - }, "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "node_modules/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "locate-path": "^3.0.0" - }, + "boolbase": "~1.0.0" + } + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=6" + "node": "*" } }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, "license": "MIT", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", "dev": true, "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^2.0.0" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/pkg-dir/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/portfinder": { - "version": "1.0.38", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.38.tgz", - "integrity": "sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==", + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dev": true, "license": "MIT", "dependencies": { - "async": "^3.2.6", - "debug": "^4.3.6" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" }, "engines": { - "node": ">= 10.12" - } - }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" } }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "isobject": "^3.0.0" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=0.10.0" } }, - "node_modules/postcss-calc": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", - "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, "license": "MIT", "dependencies": { - "postcss": "^7.0.27", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss-calc/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true, - "license": "ISC" - }, - "node_modules/postcss-calc/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.9.tgz", + "integrity": "sha512-mt8YM6XwsTTovI+kdZdHSxoyF2DI59up034orlC9NfweclcWOt7CVascNNLp6U+bjFVCVCIh9PwS76tDM/rH8g==", "dev": true, "license": "MIT", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" + "array.prototype.reduce": "^1.0.8", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "gopd": "^1.2.0", + "safe-array-concat": "^1.1.3" }, "engines": { - "node": ">=6.0.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss-colormin": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", - "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.0.0", - "color": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "isobject": "^3.0.1" }, "engines": { - "node": ">=6.9.0" + "node": ">=0.10.0" } }, - "node_modules/postcss-colormin/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true, - "license": "ISC" - }, - "node_modules/postcss-colormin/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, "license": "MIT", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss-colormin/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true, "license": "MIT" }, - "node_modules/postcss-convert-values": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", - "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "node_modules/on-build-webpack": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/on-build-webpack/-/on-build-webpack-0.1.0.tgz", + "integrity": "sha512-kBBhiDBQP+2wEqUe467fGw4V1aUP93ed7L1eG2TG15M6uOL4IKeW4zIU0yc6MCGmUkPVf883vPkhLEeYubkfng==", + "dev": true, + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "license": "MIT", "dependencies": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "ee-first": "1.1.1" }, "engines": { - "node": ">=6.9.0" + "node": ">= 0.8" } }, - "node_modules/postcss-convert-values/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "dev": true, - "license": "ISC" + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "node_modules/postcss-convert-values/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true, + "license": "MIT", + "bin": { + "opencollective-postinstall": "index.js" + } + }, + "node_modules/opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", "dev": true, "license": "MIT", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" + "is-wsl": "^1.1.0" }, "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": ">=4" } }, - "node_modules/postcss-convert-values/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "node_modules/optimize-css-assets-webpack-plugin": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.8.tgz", + "integrity": "sha512-mgFS1JdOtEGzD8l+EuISqL57cKO+We9GcoiQEmdCWRqqck+FGNmYJtx9qfAPzEz+lRrlThWMuGDaRkI/yWNx/Q==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "cssnano": "^4.1.10", + "last-call-webpack-plugin": "^3.0.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } }, - "node_modules/postcss-discard-comments": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", - "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { - "postcss": "^7.0.0" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { - "node": ">=6.9.0" + "node": ">= 0.8.0" } }, - "node_modules/postcss-discard-comments/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/postcss-discard-comments/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", "dev": true, "license": "MIT", - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": ">=0.10.0" } }, - "node_modules/postcss-discard-duplicates": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", - "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, "license": "MIT", - "dependencies": { - "postcss": "^7.0.0" - }, "engines": { - "node": ">=6.9.0" + "node": ">=0.10.0" } }, - "node_modules/postcss-discard-duplicates/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "node_modules/osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "deprecated": "This package is no longer supported.", "dev": true, - "license": "ISC" + "license": "ISC", + "dependencies": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } }, - "node_modules/postcss-discard-duplicates/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, "license": "MIT", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss-discard-empty": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", - "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", "dev": true, "license": "MIT", - "dependencies": { - "postcss": "^7.0.0" - }, "engines": { - "node": ">=6.9.0" + "node": ">=6" } }, - "node_modules/postcss-discard-empty/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true, - "license": "ISC" - }, - "node_modules/postcss-discard-empty/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "dev": true, "license": "MIT", - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": ">=4" } }, - "node_modules/postcss-discard-overridden": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", - "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { - "postcss": "^7.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/postcss-discard-overridden/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true, - "license": "ISC" - }, - "node_modules/postcss-discard-overridden/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=6.0.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/postcss-load-config": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.2.tgz", - "integrity": "sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==", + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "license": "MIT", "dependencies": { - "cosmiconfig": "^5.0.0", - "import-cwd": "^2.0.0" + "aggregate-error": "^3.0.0" }, "engines": { - "node": ">= 4" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/postcss-loader": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", - "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "node_modules/p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", "dev": true, "license": "MIT", "dependencies": { - "loader-utils": "^1.1.0", - "postcss": "^7.0.0", - "postcss-load-config": "^2.0.0", - "schema-utils": "^1.0.0" + "retry": "^0.12.0" }, "engines": { - "node": ">= 6" + "node": ">=6" } }, - "node_modules/postcss-loader/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" + "engines": { + "node": ">=6" } }, - "node_modules/postcss-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "node_modules/package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", "dev": true, "license": "MIT", "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" }, "engines": { - "node": ">=4.0.0" + "node": ">=8" } }, - "node_modules/postcss-loader/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, - "license": "ISC" + "license": "BlueOak-1.0.0" }, - "node_modules/postcss-loader/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", "dev": true, "license": "MIT", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" } }, - "node_modules/postcss-loader/node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "node_modules/param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "no-case": "^2.2.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" }, "engines": { - "node": ">= 4" + "node": ">=6" } }, - "node_modules/postcss-merge-longhand": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", - "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "node_modules/parse-asn1": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.9.tgz", + "integrity": "sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "css-color-names": "0.0.4", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "stylehacks": "^4.0.0" + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "pbkdf2": "^3.1.5", + "safe-buffer": "^5.2.1" }, "engines": { - "node": ">=6.9.0" + "node": ">= 0.10" } }, - "node_modules/postcss-merge-longhand/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "node_modules/parse-imports-exports": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", + "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "parse-statements": "1.0.11" + } }, - "node_modules/postcss-merge-longhand/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, "license": "MIT", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" }, "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": ">=4" } }, - "node_modules/postcss-merge-longhand/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "node_modules/parse-statements": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", + "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", "dev": true, "license": "MIT" }, - "node_modules/postcss-merge-rules": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", - "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true, "license": "MIT", - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "cssnano-util-same-parent": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0", - "vendors": "^1.0.0" - }, "engines": { - "node": ">=6.9.0" + "node": ">= 0.8" } }, - "node_modules/postcss-merge-rules/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true, - "license": "ISC" - }, - "node_modules/postcss-merge-rules/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", "dev": true, "license": "MIT", - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": ">=0.10.0" } }, - "node_modules/postcss-merge-rules/node_modules/postcss-selector-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", - "dependencies": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - }, "engines": { "node": ">=8" } }, - "node_modules/postcss-minify-font-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", - "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "license": "MIT", - "dependencies": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, "engines": { - "node": ">=6.9.0" + "node": ">=0.10.0" } }, - "node_modules/postcss-minify-font-values/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", "dev": true, - "license": "ISC" + "license": "(WTFPL OR MIT)" }, - "node_modules/postcss-minify-font-values/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": ">=8" } }, - "node_modules/postcss-minify-font-values/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, "license": "MIT" }, - "node_modules/postcss-minify-gradients": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", - "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "cssnano-util-get-arguments": "^4.0.0", - "is-color-stop": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=6.9.0" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/postcss-minify-gradients/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true, - "license": "ISC" - }, - "node_modules/postcss-minify-gradients/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.2.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", + "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", "dev": true, - "license": "MIT", - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, + "license": "BlueOak-1.0.0", "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": "20 || >=22" } }, - "node_modules/postcss-minify-gradients/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "dev": true, "license": "MIT" }, - "node_modules/postcss-minify-params": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", - "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, "license": "MIT", - "dependencies": { - "alphanum-sort": "^1.0.0", - "browserslist": "^4.0.0", - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "uniqs": "^2.0.0" + "engines": { + "node": ">=8" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz", + "integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "ripemd160": "^2.0.3", + "safe-buffer": "^5.2.1", + "sha.js": "^2.4.12", + "to-buffer": "^1.2.1" }, "engines": { - "node": ">=6.9.0" + "node": ">= 0.10" } }, - "node_modules/postcss-minify-params/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, - "node_modules/postcss-minify-params/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, "engines": { - "node": ">=6.0.0" + "node": ">=8.6" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/postcss-minify-params/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss-minify-selectors": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", - "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", "dev": true, "license": "MIT", - "dependencies": { - "alphanum-sort": "^1.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" + "bin": { + "pidtree": "bin/pidtree.js" }, "engines": { - "node": ">=6.9.0" + "node": ">=0.10" } }, - "node_modules/postcss-minify-selectors/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, - "license": "ISC" + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "node_modules/postcss-minify-selectors/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", "dev": true, "license": "MIT", - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": ">=0.10.0" } }, - "node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", "dev": true, "license": "MIT", "dependencies": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" + "pinkie": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/postcss-modules-extract-imports": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", - "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "dev": true, - "license": "ISC", - "dependencies": { - "postcss": "^7.0.5" - }, + "license": "MIT", "engines": { "node": ">= 6" } }, - "node_modules/postcss-modules-extract-imports/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true, - "license": "ISC" - }, - "node_modules/postcss-modules-extract-imports/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "dev": true, "license": "MIT", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" + "find-up": "^3.0.0" }, "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": ">=6" } }, - "node_modules/postcss-modules-local-by-default": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz", - "integrity": "sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "license": "MIT", "dependencies": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0", - "postcss-value-parser": "^3.3.1" + "locate-path": "^3.0.0" }, "engines": { - "node": ">= 6" + "node": ">=6" } }, - "node_modules/postcss-modules-local-by-default/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } }, - "node_modules/postcss-modules-local-by-default/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" + "p-try": "^2.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=6" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/postcss-modules-local-by-default/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss-modules-scope": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", - "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0" + "p-limit": "^2.0.0" }, "engines": { - "node": ">= 6" + "node": ">=6" } }, - "node_modules/postcss-modules-scope/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, - "license": "ISC" + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "node_modules/postcss-modules-scope/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/portfinder": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.38.tgz", + "integrity": "sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==", "dev": true, "license": "MIT", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" + "async": "^3.2.6", + "debug": "^4.3.6" }, "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": ">= 10.12" } }, - "node_modules/postcss-modules-values": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz", - "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==", + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", "dev": true, - "license": "ISC", - "dependencies": { - "icss-replace-symbols": "^1.1.0", - "postcss": "^7.0.6" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/postcss-modules-values/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, - "license": "ISC" + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "node_modules/postcss-modules-values/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-normalize-charset": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", - "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "node_modules/postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", "dev": true, "license": "MIT", "dependencies": { - "postcss": "^7.0.0" - }, - "engines": { - "node": ">=6.9.0" + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" } }, - "node_modules/postcss-normalize-charset/node_modules/picocolors": { + "node_modules/postcss-calc/node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/postcss-normalize-charset/node_modules/postcss": { + "node_modules/postcss-calc/node_modules/postcss": { "version": "7.0.39", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", @@ -21437,14 +17492,16 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-normalize-display-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", - "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "node_modules/postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", "dev": true, "license": "MIT", "dependencies": { - "cssnano-util-get-match": "^4.0.0", + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0" }, @@ -21452,14 +17509,14 @@ "node": ">=6.9.0" } }, - "node_modules/postcss-normalize-display-values/node_modules/picocolors": { + "node_modules/postcss-colormin/node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/postcss-normalize-display-values/node_modules/postcss": { + "node_modules/postcss-colormin/node_modules/postcss": { "version": "7.0.39", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", @@ -21477,22 +17534,20 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-normalize-display-values/node_modules/postcss-value-parser": { + "node_modules/postcss-colormin/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true, "license": "MIT" }, - "node_modules/postcss-normalize-positions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", - "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "node_modules/postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", "dev": true, "license": "MIT", "dependencies": { - "cssnano-util-get-arguments": "^4.0.0", - "has": "^1.0.0", "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0" }, @@ -21500,14 +17555,14 @@ "node": ">=6.9.0" } }, - "node_modules/postcss-normalize-positions/node_modules/picocolors": { + "node_modules/postcss-convert-values/node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/postcss-normalize-positions/node_modules/postcss": { + "node_modules/postcss-convert-values/node_modules/postcss": { "version": "7.0.39", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", @@ -21525,37 +17580,34 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-normalize-positions/node_modules/postcss-value-parser": { + "node_modules/postcss-convert-values/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true, "license": "MIT" }, - "node_modules/postcss-normalize-repeat-style": { + "node_modules/postcss-discard-comments": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", - "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", "dev": true, "license": "MIT", "dependencies": { - "cssnano-util-get-arguments": "^4.0.0", - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "postcss": "^7.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/postcss-normalize-repeat-style/node_modules/picocolors": { + "node_modules/postcss-discard-comments/node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/postcss-normalize-repeat-style/node_modules/postcss": { + "node_modules/postcss-discard-comments/node_modules/postcss": { "version": "7.0.39", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", @@ -21573,36 +17625,27 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-normalize-repeat-style/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss-normalize-string": { + "node_modules/postcss-discard-duplicates": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", - "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", "dev": true, "license": "MIT", "dependencies": { - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "postcss": "^7.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/postcss-normalize-string/node_modules/picocolors": { + "node_modules/postcss-discard-duplicates/node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/postcss-normalize-string/node_modules/postcss": { + "node_modules/postcss-discard-duplicates/node_modules/postcss": { "version": "7.0.39", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", @@ -21620,36 +17663,27 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-normalize-string/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", - "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "node_modules/postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", "dev": true, "license": "MIT", "dependencies": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "postcss": "^7.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/postcss-normalize-timing-functions/node_modules/picocolors": { + "node_modules/postcss-discard-empty/node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/postcss-normalize-timing-functions/node_modules/postcss": { + "node_modules/postcss-discard-empty/node_modules/postcss": { "version": "7.0.39", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", @@ -21667,36 +17701,27 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-normalize-timing-functions/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss-normalize-unicode": { + "node_modules/postcss-discard-overridden": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", - "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "postcss": "^7.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/postcss-normalize-unicode/node_modules/picocolors": { + "node_modules/postcss-discard-overridden/node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/postcss-normalize-unicode/node_modules/postcss": { + "node_modules/postcss-discard-overridden/node_modules/postcss": { "version": "7.0.39", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", @@ -21714,93 +17739,76 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-normalize-unicode/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss-normalize-url": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", - "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "node_modules/postcss-load-config": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.2.tgz", + "integrity": "sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==", "dev": true, "license": "MIT", "dependencies": { - "is-absolute-url": "^2.0.0", - "normalize-url": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" }, "engines": { - "node": ">=6.9.0" + "node": ">= 4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-normalize-url/node_modules/normalize-url": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", - "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "node_modules/postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", "dev": true, "license": "MIT", + "dependencies": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + }, "engines": { - "node": ">=6" + "node": ">= 6" } }, - "node_modules/postcss-normalize-url/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true, - "license": "ISC" - }, - "node_modules/postcss-normalize-url/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "node_modules/postcss-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "license": "MIT", "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" + "minimist": "^1.2.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "bin": { + "json5": "lib/cli.js" } }, - "node_modules/postcss-normalize-url/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss-normalize-whitespace": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", - "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "node_modules/postcss-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dev": true, "license": "MIT", "dependencies": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" }, "engines": { - "node": ">=6.9.0" + "node": ">=4.0.0" } }, - "node_modules/postcss-normalize-whitespace/node_modules/picocolors": { + "node_modules/postcss-loader/node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/postcss-normalize-whitespace/node_modules/postcss": { + "node_modules/postcss-loader/node_modules/postcss": { "version": "7.0.39", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", @@ -21818,36 +17826,45 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-normalize-whitespace/node_modules/postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "node_modules/postcss-loader/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } }, - "node_modules/postcss-ordered-values": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", - "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "node_modules/postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", "dev": true, "license": "MIT", "dependencies": { - "cssnano-util-get-arguments": "^4.0.0", + "css-color-names": "0.0.4", "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/postcss-ordered-values/node_modules/picocolors": { + "node_modules/postcss-merge-longhand/node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/postcss-ordered-values/node_modules/postcss": { + "node_modules/postcss-merge-longhand/node_modules/postcss": { "version": "7.0.39", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", @@ -21865,37 +17882,39 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-ordered-values/node_modules/postcss-value-parser": { + "node_modules/postcss-merge-longhand/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true, "license": "MIT" }, - "node_modules/postcss-reduce-initial": { + "node_modules/postcss-merge-rules": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", - "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", "dev": true, "license": "MIT", "dependencies": { "browserslist": "^4.0.0", "caniuse-api": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0" + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/postcss-reduce-initial/node_modules/picocolors": { + "node_modules/postcss-merge-rules/node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/postcss-reduce-initial/node_modules/postcss": { + "node_modules/postcss-merge-rules/node_modules/postcss": { "version": "7.0.39", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", @@ -21913,15 +17932,28 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-reduce-transforms": { + "node_modules/postcss-merge-rules/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss-minify-font-values": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", - "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", "dev": true, "license": "MIT", "dependencies": { - "cssnano-util-get-match": "^4.0.0", - "has": "^1.0.0", "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0" }, @@ -21929,14 +17961,14 @@ "node": ">=6.9.0" } }, - "node_modules/postcss-reduce-transforms/node_modules/picocolors": { + "node_modules/postcss-minify-font-values/node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/postcss-reduce-transforms/node_modules/postcss": { + "node_modules/postcss-minify-font-values/node_modules/postcss": { "version": "7.0.39", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", @@ -21954,34 +17986,37 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-reduce-transforms/node_modules/postcss-value-parser": { + "node_modules/postcss-minify-font-values/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true, "license": "MIT" }, - "node_modules/postcss-safe-parser": { + "node_modules/postcss-minify-gradients": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz", - "integrity": "sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g==", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", "dev": true, "license": "MIT", "dependencies": { - "postcss": "^7.0.26" + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" } }, - "node_modules/postcss-safe-parser/node_modules/picocolors": { + "node_modules/postcss-minify-gradients/node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/postcss-safe-parser/node_modules/postcss": { + "node_modules/postcss-minify-gradients/node_modules/postcss": { "version": "7.0.39", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", @@ -21999,43 +18034,39 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "node_modules/postcss-minify-gradients/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } + "license": "MIT" }, - "node_modules/postcss-svgo": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz", - "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==", + "node_modules/postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", "dev": true, "license": "MIT", "dependencies": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", "postcss": "^7.0.0", "postcss-value-parser": "^3.0.0", - "svgo": "^1.0.0" + "uniqs": "^2.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/postcss-svgo/node_modules/picocolors": { + "node_modules/postcss-minify-params/node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/postcss-svgo/node_modules/postcss": { + "node_modules/postcss-minify-params/node_modules/postcss": { "version": "7.0.39", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", @@ -22053,36 +18084,37 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-svgo/node_modules/postcss-value-parser": { + "node_modules/postcss-minify-params/node_modules/postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true, "license": "MIT" }, - "node_modules/postcss-unique-selectors": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", - "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "node_modules/postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", "dev": true, "license": "MIT", "dependencies": { "alphanum-sort": "^1.0.0", + "has": "^1.0.0", "postcss": "^7.0.0", - "uniqs": "^2.0.0" + "postcss-selector-parser": "^3.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/postcss-unique-selectors/node_modules/picocolors": { + "node_modules/postcss-minify-selectors/node_modules/picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/postcss-unique-selectors/node_modules/postcss": { + "node_modules/postcss-minify-selectors/node_modules/postcss": { "version": "7.0.39", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", @@ -22100,2025 +18132,2189 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/prettier": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", - "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", - "dev": true, - "license": "MIT", - "peer": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/pretty-error": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", - "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^2.0.4" - } - }, - "node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" }, "engines": { - "node": ">= 10" + "node": ">=8" } }, - "node_modules/pretty-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", - "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", + "node_modules/postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "postcss": "^7.0.5" + }, "engines": { - "node": ">=4" + "node": ">= 6" } }, - "node_modules/printable-characters": { - "version": "1.0.42", - "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz", - "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", + "node_modules/postcss-modules-extract-imports/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, - "license": "Unlicense" + "license": "ISC" }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "node_modules/postcss-modules-extract-imports/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "license": "MIT", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, "engines": { - "node": ">=6" + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "node_modules/postcss-modules-local-by-default": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz", + "integrity": "sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==", "dev": true, "license": "MIT", + "dependencies": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0", + "postcss-value-parser": "^3.3.1" + }, "engines": { - "node": ">= 0.6.0" + "node": ">= 6" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "node_modules/postcss-modules-local-by-default/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "node_modules/postcss-modules-local-by-default/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "license": "MIT", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, "engines": { - "node": ">=0.4.0" + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "node_modules/postcss-modules-local-by-default/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "node_modules/postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" }, "engines": { "node": ">= 6" } }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "node_modules/postcss-modules-scope/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss-modules-scope/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "license": "MIT", "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "engines": { - "node": ">= 0.10" + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "node_modules/postcss-modules-values": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz", + "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==", "dev": true, - "license": "MIT" + "license": "ISC", + "dependencies": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^7.0.6" + } }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "node_modules/postcss-modules-values/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, "license": "ISC" }, - "node_modules/psl": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", - "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "node_modules/postcss-modules-values/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "license": "MIT", "dependencies": { - "punycode": "^2.3.1" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, - "funding": { - "url": "https://github.com/sponsors/lupomontero" - } - }, - "node_modules/psl/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", "engines": { - "node": ">=6" + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "node_modules/postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", "dev": true, "license": "MIT", "dependencies": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "node_modules/postcss-normalize-charset/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "node_modules/postcss-normalize-charset/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "license": "MIT", "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "node_modules/postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", "dev": true, "license": "MIT", "dependencies": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/pumpify/node_modules/pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "node_modules/postcss-normalize-display-values/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss-normalize-display-values/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "license": "MIT", "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "node_modules/postcss-normalize-display-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true, "license": "MIT" }, - "node_modules/pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "node_modules/postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", "dev": true, "license": "MIT", "dependencies": { - "escape-goat": "^2.0.0" + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "node_modules/postcss-normalize-positions/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } + "license": "ISC" }, - "node_modules/qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "node_modules/postcss-normalize-positions/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.9" - } - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "dev": true, - "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "engines": { - "node": ">=0.6" + "node": ">=6.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/query-string": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "node_modules/postcss-normalize-positions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", "dev": true, "license": "MIT", "dependencies": { - "decode-uri-component": "^0.2.0", - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6.9.0" } }, - "node_modules/querystring-es3": { + "node_modules/postcss-normalize-repeat-style/node_modules/picocolors": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, - "engines": { - "node": ">=0.4.x" - } + "license": "ISC" }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "node_modules/postcss-normalize-repeat-style/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "node_modules/postcss-normalize-repeat-style/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "license": "MIT" }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "node_modules/postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", "dev": true, "license": "MIT", "dependencies": { - "safe-buffer": "^5.1.0" + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "node_modules/postcss-normalize-string/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, - "license": "MIT", - "dependencies": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } + "license": "ISC" }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "node_modules/postcss-normalize-string/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "license": "MIT", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, "engines": { - "node": ">= 0.6" + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "node_modules/postcss-normalize-string/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", "dev": true, "license": "MIT", "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">=6.9.0" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "node_modules/postcss-normalize-timing-functions/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "license": "ISC" + }, + "node_modules/postcss-normalize-timing-functions/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "license": "MIT", "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, - "bin": { - "rc": "cli.js" + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "node_modules/postcss-normalize-timing-functions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", "dev": true, "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=6.9.0" } }, - "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "node_modules/postcss-normalize-unicode/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/read-installed": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz", - "integrity": "sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ==", - "deprecated": "This package is no longer supported.", + "node_modules/postcss-normalize-unicode/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "debuglog": "^1.0.1", - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "slide": "~1.1.3", - "util-extend": "^1.0.1" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, - "optionalDependencies": { - "graceful-fs": "^4.1.2" + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/read-installed/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/postcss-normalize-unicode/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } + "license": "MIT" }, - "node_modules/read-package-json": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", - "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", - "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", + "node_modules/postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "glob": "^7.1.1", - "json-parse-even-better-errors": "^2.3.0", - "normalize-package-data": "^2.0.0", - "npm-normalize-package-bin": "^1.0.0" + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "node_modules/postcss-normalize-url/node_modules/normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", "dev": true, "license": "MIT", - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "node_modules/postcss-normalize-url/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss-normalize-url/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "license": "MIT", "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "engines": { - "node": ">=8" + "node": ">=6.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/postcss-normalize-url/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^4.1.0" + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/postcss-normalize-whitespace/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss-normalize-whitespace/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "engines": { - "node": ">=6" + "node": ">=6.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/postcss-normalize-whitespace/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/read-pkg-up/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/postcss-ordered-values/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss-ordered-values/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "engines": { - "node": ">=8" + "node": ">=6.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/read-pkg-up/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "node_modules/postcss-ordered-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", "dev": true, "license": "MIT", "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/read-pkg-up/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "node_modules/postcss-reduce-initial/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } + "license": "ISC" }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "node_modules/postcss-reduce-initial/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, - "license": "(MIT OR CC0-1.0)", + "license": "MIT", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, "engines": { - "node": ">=8" + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "node_modules/postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", "dev": true, "license": "MIT", "dependencies": { - "pify": "^3.0.0" + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=6.9.0" } }, - "node_modules/read-pkg/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "node_modules/postcss-reduce-transforms/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } + "license": "ISC" }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/postcss-reduce-transforms/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "engines": { - "node": ">= 6" + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/readdir-glob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "node_modules/postcss-reduce-transforms/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.1.0" - } + "license": "MIT" }, - "node_modules/readdir-glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "node_modules/postcss-safe-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz", + "integrity": "sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/readdir-glob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" + "postcss": "^7.0.26" }, "engines": { - "node": ">=10" + "node": ">=6.0.0" } }, - "node_modules/readdir-scoped-modules": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", - "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", - "deprecated": "This functionality has been moved to @npmcli/fs", + "node_modules/postcss-safe-parser/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true, - "license": "ISC", - "dependencies": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - } + "license": "ISC" }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/postcss-safe-parser/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "license": "MIT", "dependencies": { - "picomatch": "^2.2.1" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "engines": { - "node": ">=8.10.0" + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, + "license": "MIT", "dependencies": { - "resolve": "^1.1.6" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, "engines": { - "node": ">= 0.10" + "node": ">=4" } }, - "node_modules/reduce": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/reduce/-/reduce-1.0.3.tgz", - "integrity": "sha512-0Dtt3Bgj34/yKFzE5N9V6/HYyP3gb+E3TLs/hMr/wGgkCIzYa+7G4hNrE/P+en52OJT+pLUgmba9DQF3AB+2LQ==", + "node_modules/postcss-svgo": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz", + "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==", "dev": true, "license": "MIT", "dependencies": { - "object-keys": "^1.1.1" + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" } }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "node_modules/postcss-svgo/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss-svgo/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "engines": { - "node": ">= 0.4" + "node": ">=6.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "node_modules/postcss-svgo/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true, "license": "MIT" }, - "node_modules/regenerate-unicode-properties": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", - "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", + "node_modules/postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", "dev": true, "license": "MIT", "dependencies": { - "regenerate": "^1.4.2" + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=6.9.0" } }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "node_modules/postcss-unique-selectors/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss-unique-selectors/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "license": "MIT", "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, - "node_modules/regex-not/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/regex-not/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", "dev": true, "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4" - }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/regexp-to-ast": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.4.0.tgz", - "integrity": "sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw==", - "license": "MIT" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/regexpu-core": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", - "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", + "node_modules/prettier-linter-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", "dev": true, "license": "MIT", "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.2", - "regjsgen": "^0.8.0", - "regjsparser": "^0.13.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.2.1" + "fast-diff": "^1.1.2" }, "engines": { - "node": ">=4" + "node": ">=6.0.0" } }, - "node_modules/registry-auth-token": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", - "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==", + "node_modules/pretty-error": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", + "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", "dev": true, "license": "MIT", "dependencies": { - "rc": "1.2.8" - }, - "engines": { - "node": ">=6.0.0" + "lodash": "^4.17.20", + "renderkid": "^2.0.4" } }, - "node_modules/registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "node_modules/pretty-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", + "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", "dev": true, "license": "MIT", - "dependencies": { - "rc": "^1.2.8" - }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "node_modules/printable-characters": { + "version": "1.0.42", + "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz", + "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", "dev": true, - "license": "MIT" + "license": "Unlicense" }, - "node_modules/regjsparser": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz", - "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.1.0" - }, - "bin": { - "regjsparser": "bin/parser" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">= 0.6.0" } }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/renderkid": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz", - "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==", + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, "license": "MIT", - "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^3.0.1" + "engines": { + "node": ">=0.4.0" } }, - "node_modules/renderkid/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "license": "ISC" }, - "node_modules/renderkid/node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "engines": { + "node": ">= 0.10" } }, - "node_modules/renderkid/node_modules/css-what": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", - "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" + "license": "MIT" + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" }, "funding": { - "url": "https://github.com/sponsors/fb55" + "url": "https://github.com/sponsors/lupomontero" } }, - "node_modules/renderkid/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, "license": "MIT", "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" } }, - "node_modules/renderkid/node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" + "license": "MIT" }, - "node_modules/renderkid/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "node_modules/renderkid/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "license": "MIT", + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/pumpify/node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "node_modules/renderkid/node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "node_modules/renderkid/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "node_modules/pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^2.0.0" + "escape-goat": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=0.6.0", + "teleport": ">=0.2.0" } }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "engines": { - "node": ">=0.10" + "node": ">=0.6" } }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "node_modules/query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "license": "MIT", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" + "safe-buffer": "^5.1.0" } }, - "node_modules/request/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" } }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "engines": { - "node": ">=0.6" + "node": ">= 0.6" } }, - "node_modules/request/node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" }, "engines": { - "node": ">=0.8" + "node": ">= 0.8" } }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, - "license": "MIT", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, "bin": { - "uuid": "bin/uuid" + "rc": "cli.js" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "node_modules/read-installed": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz", + "integrity": "sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ==", + "deprecated": "This package is no longer supported.", "dev": true, - "license": "ISC" + "license": "ISC", + "dependencies": { + "debuglog": "^1.0.1", + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "slide": "~1.1.3", + "util-extend": "^1.0.1" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.2" + } }, - "node_modules/requireindex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", - "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "node_modules/read-installed/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.5" + "license": "ISC", + "bin": { + "semver": "bin/semver" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "node_modules/read-package-json": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", + "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", + "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", "dev": true, - "license": "MIT" + "license": "ISC", + "dependencies": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0" + } }, - "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/resolve-cwd": { + "node_modules/read-pkg/node_modules/path-type": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "license": "MIT", "dependencies": { - "resolve-from": "^5.0.0" + "pify": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "node_modules/read-pkg/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=4" + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true, "license": "MIT" }, - "node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/readdir-scoped-modules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "license": "ISC", "dependencies": { - "lowercase-keys": "^1.0.0" + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" } }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", + "optional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, "engines": { - "node": ">=0.12" + "node": ">=8.10.0" } }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "dev": true, - "license": "MIT", + "dependencies": { + "resolve": "^1.1.6" + }, "engines": { - "node": ">= 4" + "node": ">= 0.10" } }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "node_modules/reduce": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/reduce/-/reduce-1.0.3.tgz", + "integrity": "sha512-0Dtt3Bgj34/yKFzE5N9V6/HYyP3gb+E3TLs/hMr/wGgkCIzYa+7G4hNrE/P+en52OJT+pLUgmba9DQF3AB+2LQ==", "dev": true, "license": "MIT", + "dependencies": { + "object-keys": "^1.1.1" + }, "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha512-gDK5mkALDFER2YLqH6imYvK6g02gpNGM4ILDZ472EwWfXZnC2ZEpoB2ECXTyOVUKuk/bPJZMzwQPBYICzP+D3w==", + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "dev": true, "license": "MIT" }, - "node_modules/rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==", + "node_modules/regenerate-unicode-properties": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/rimraf": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.2.tgz", - "integrity": "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==", + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "glob": "^13.0.0", - "package-json-from-dist": "^1.0.1" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" }, "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.10.0" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", - "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "node_modules/regex-not/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "minimatch": "^10.1.1", - "minipass": "^7.1.2", - "path-scurry": "^2.0.0" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" }, "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.10.0" } }, - "node_modules/rimraf/node_modules/lru-cache": { - "version": "11.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", - "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "node_modules/regex-not/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, "engines": { - "node": "20 || >=22" + "node": ">=0.10.0" } }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "node_modules/regexp-to-ast": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.4.0.tgz", + "integrity": "sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw==", + "license": "MIT" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" }, "engines": { - "node": "20 || >=22" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/rimraf/node_modules/path-scurry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", - "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "node_modules/regexpu-core": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.2", + "regjsgen": "^0.8.0", + "regjsparser": "^0.13.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.2.1" }, "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=4" } }, - "node_modules/ripemd160": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz", - "integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==", + "node_modules/registry-auth-token": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", + "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==", "dev": true, "license": "MIT", "dependencies": { - "hash-base": "^3.1.2", - "inherits": "^2.0.4" + "rc": "1.2.8" }, "engines": { - "node": ">= 0.8" + "node": ">=6.0.0" } }, - "node_modules/ripemd160/node_modules/hash-base": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.2.tgz", - "integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==", + "node_modules/registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^2.3.8", - "safe-buffer": "^5.2.1", - "to-buffer": "^1.2.1" + "rc": "^1.2.8" }, "engines": { - "node": ">= 0.8" + "node": ">=8" } }, - "node_modules/ripemd160/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", "dev": true, "license": "MIT" }, - "node_modules/ripemd160/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/regjsparser": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz", + "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "jsesc": "~3.1.0" + }, + "bin": { + "regjsparser": "bin/parser" } }, - "node_modules/ripemd160/node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/ripemd160/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", "dev": true, "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" + "engines": { + "node": ">= 0.10" } }, - "node_modules/ripemd160/node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/rrweb-cssom": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", - "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "node_modules/renderkid": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz", + "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^3.0.1" + } }, - "node_modules/rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "node_modules/renderkid/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "license": "MIT", "engines": { - "node": "6.* || >= 7.*" + "node": ">=0.10.0" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/renderkid/node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "queue-microtask": "^1.2.2" + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==", + "node_modules/renderkid/node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", "dev": true, - "license": "ISC", - "dependencies": { - "aproba": "^1.1.1" + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "node_modules/renderkid/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "node_modules/renderkid/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "dev": true, "funding": [ { "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" + "url": "https://github.com/sponsors/fb55" } ], - "license": "MIT" + "license": "BSD-2-Clause" }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "node_modules/renderkid/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" }, - "engines": { - "node": ">= 0.4" + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "node_modules/renderkid/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "license": "MIT", "dependencies": { - "ret": "~0.1.10" + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "deprecated": "some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added", + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", "dev": true, "license": "MIT", - "dependencies": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "bin": { - "sane": "src/cli.js" - }, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=0.10" } }, - "node_modules/sane/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", "dev": true, - "license": "ISC", + "license": "Apache-2.0", "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/sane/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "license": "MIT", - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/sane/node_modules/cross-spawn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true, - "license": "MIT", - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } + "license": "ISC" }, - "node_modules/sane/node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "node_modules/requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", "dev": true, "license": "MIT", - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, "engines": { - "node": ">=0.10.0" + "node": ">=0.10.5" } }, - "node_modules/sane/node_modules/execa": { + "node_modules/requires-port": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "license": "MIT", "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sane/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "license": "MIT", "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "resolve-from": "^5.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/sane/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/sane/node_modules/is-descriptor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", - "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, "engines": { - "node": ">= 0.4" + "node": ">=4" } }, - "node_modules/sane/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", "dev": true, "license": "MIT", "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" + "lowercase-keys": "^1.0.0" } }, - "node_modules/sane/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true, "license": "MIT", - "dependencies": { - "kind-of": "^3.0.2" - }, "engines": { - "node": ">=0.10.0" + "node": ">=0.12" } }, - "node_modules/sane/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true, - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 4" } }, - "node_modules/sane/node_modules/is-stream": { + "node_modules/reusify": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", "engines": { + "iojs": ">=1.0.0", "node": ">=0.10.0" } }, - "node_modules/sane/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "node_modules/rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha512-gDK5mkALDFER2YLqH6imYvK6g02gpNGM4ILDZ472EwWfXZnC2ZEpoB2ECXTyOVUKuk/bPJZMzwQPBYICzP+D3w==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.2.tgz", + "integrity": "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==", + "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "glob": "^13.0.0", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": ">=0.10.0" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/sane/node_modules/micromatch/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "node_modules/rimraf/node_modules/glob": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", + "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/sane/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "remove-trailing-separator": "^1.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/sane/node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "node_modules/ripemd160": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz", + "integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==", "dev": true, "license": "MIT", "dependencies": { - "path-key": "^2.0.0" + "hash-base": "^3.1.2", + "inherits": "^2.0.4" }, "engines": { - "node": ">=4" + "node": ">= 0.8" } }, - "node_modules/sane/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "node_modules/ripemd160/node_modules/hash-base": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.2.tgz", + "integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==", "dev": true, "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.1" + }, "engines": { - "node": ">=4" + "node": ">= 0.8" } }, - "node_modules/sane/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==", "dev": true, "license": "ISC", - "bin": { - "semver": "bin/semver" + "dependencies": { + "aproba": "^1.1.1" } }, - "node_modules/sane/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, "license": "MIT", "dependencies": { - "shebang-regex": "^1.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sane/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "node_modules/sane/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, "license": "MIT", "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "es-errors": "^1.3.0", + "isarray": "^2.0.5" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sane/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" + "ret": "~0.1.10" } }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true, - "license": "ISC" - }, - "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "xmlchars": "^2.2.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true, + "license": "ISC" + }, "node_modules/schema-utils": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", @@ -24193,9 +20389,9 @@ } }, "node_modules/send": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.1.tgz", - "integrity": "sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", "dev": true, "license": "MIT", "dependencies": { @@ -24205,13 +20401,13 @@ "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "range-parser": "~1.2.1", - "statuses": "2.0.1" + "statuses": "~2.0.2" }, "engines": { "node": ">= 0.8.0" @@ -24234,33 +20430,6 @@ "dev": true, "license": "MIT" }, - "node_modules/send/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/send/node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -24274,16 +20443,6 @@ "node": ">=4" } }, - "node_modules/send/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/serialize-javascript": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", @@ -24295,22 +20454,26 @@ } }, "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.2.tgz", + "integrity": "sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==", "dev": true, "license": "MIT", "dependencies": { - "accepts": "~1.3.4", + "accepts": "~1.3.8", "batch": "0.6.1", "debug": "2.6.9", "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" + "http-errors": "~1.8.0", + "mime-types": "~2.1.35", + "parseurl": "~1.3.3" }, "engines": { "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/serve-index/node_modules/debug": { @@ -24334,28 +20497,22 @@ } }, "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "dev": true, "license": "MIT", "dependencies": { "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" }, "engines": { "node": ">= 0.6" } }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true, - "license": "ISC" - }, "node_modules/serve-index/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -24363,131 +20520,32 @@ "dev": true, "license": "MIT" }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-static/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-static/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/serve-static/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serve-static/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serve-static/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true, "license": "MIT", - "bin": { - "mime": "cli.js" - }, "engines": { - "node": ">=4" + "node": ">= 0.6" } }, - "node_modules/serve-static/node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", "dev": true, "license": "MIT", "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "parseurl": "~1.3.3", + "send": "~0.19.1" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/serve-static/node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serve-static/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -24662,14 +20720,6 @@ "node": ">=4" } }, - "node_modules/shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -24770,12 +20820,22 @@ "dev": true, "license": "MIT" }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "node_modules/sitemap": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-3.2.2.tgz", + "integrity": "sha512-TModL/WU4m2q/mQcrDgNANn0P4LwprM9MMvG4hu5zP4c6IIKs2YLTu6nXXnNr8ODW/WFtxKggiJ1EGn2W0GNmg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "lodash.chunk": "^4.2.0", + "lodash.padstart": "^4.6.1", + "whatwg-url": "^7.0.0", + "xmlbuilder": "^13.0.0" + }, + "engines": { + "node": ">=6.0.0", + "npm": ">=4.0.0" + } }, "node_modules/sitemap": { "version": "3.2.2", @@ -24945,154 +21005,34 @@ "is-buffer": "^1.1.5" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/snapdragon/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/snapdragon/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/socket.io": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", - "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.6.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", - "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "~4.3.4", - "ws": "~8.17.1" - } - }, - "node_modules/socket.io-adapter/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=0.10.0" } }, - "node_modules/socket.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" - }, + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=0.10.0" } }, "node_modules/sockjs": { @@ -25137,6 +21077,16 @@ "ms": "^2.1.1" } }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/sort-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", @@ -25353,6 +21303,21 @@ "wbuf": "^1.7.3" } }, + "node_modules/spdy-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -25468,16 +21433,16 @@ "license": "MIT" }, "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz", + "integrity": "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ==", "dev": true, "license": "MIT", "dependencies": { "escape-string-regexp": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, "node_modules/stack-utils/node_modules/escape-string-regexp": { @@ -25505,13 +21470,13 @@ } }, "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/std-env": { @@ -25546,46 +21511,6 @@ "readable-stream": "^2.0.2" } }, - "node_modules/stream-browserify/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/stream-browserify/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/stream-browserify/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/stream-browserify/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/stream-each": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", @@ -25611,46 +21536,6 @@ "xtend": "^4.0.0" } }, - "node_modules/stream-http/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/stream-http/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/stream-http/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/stream-http/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/stream-shift": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", @@ -25658,21 +21543,6 @@ "dev": true, "license": "MIT" }, - "node_modules/streamroller": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", - "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "fs-extra": "^8.1.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", @@ -25684,28 +21554,21 @@ } }, "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "license": "MIT", "dependencies": { - "safe-buffer": "~5.2.0" + "safe-buffer": "~5.1.0" } }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } + "license": "MIT" }, "node_modules/string-replace-loader": { "version": "2.3.0", @@ -25764,22 +21627,6 @@ "node": ">=8" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/string.prototype.padend": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", @@ -25871,28 +21718,14 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/strip-bom-string": { @@ -25915,16 +21748,6 @@ "node": ">=0.10.0" } }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -26100,30 +21923,16 @@ } }, "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -26198,6 +22007,8 @@ "js-yaml": "bin/js-yaml.js" } }, +<<<<<<< HEAD +======= "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -26205,10 +22016,11 @@ "dev": true, "license": "MIT" }, +>>>>>>> develop "node_modules/synckit": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", - "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -26232,9 +22044,9 @@ } }, "node_modules/tar": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", - "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", + "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -26248,23 +22060,6 @@ "node": ">=18" } }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/tar/node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", @@ -26279,25 +22074,8 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, + "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -26306,9 +22084,9 @@ } }, "node_modules/terser": { - "version": "5.44.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", - "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", + "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -26474,6 +22252,8 @@ "url": "https://opencollective.com/webpack" } }, +<<<<<<< HEAD +======= "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -26489,6 +22269,7 @@ "node": ">=8" } }, +>>>>>>> develop "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -26496,13 +22277,6 @@ "dev": true, "license": "MIT" }, - "node_modules/throat": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", - "dev": true, - "license": "MIT" - }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -26521,46 +22295,6 @@ "xtend": "~4.0.1" } }, - "node_modules/through2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/through2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/through2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/through2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -26594,43 +22328,6 @@ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", "license": "MIT" }, - "node_modules/tldts": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", - "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tldts-core": "^6.1.86" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/tldts-core": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", - "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tmp": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.14" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -26805,49 +22502,27 @@ "license": "MIT" }, "node_modules/tough-cookie": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", - "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "tldts": "^6.1.32" + "psl": "^1.1.28", + "punycode": "^2.1.1" }, "engines": { - "node": ">=16" + "node": ">=0.8" } }, "node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", "dev": true, "license": "MIT", "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tr46/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", - "dev": true, - "license": "MIT/X11", - "engines": { - "node": "*" + "punycode": "^2.1.0" } }, "node_modules/treeify": { @@ -26860,61 +22535,6 @@ "node": ">=0.6" } }, - "node_modules/ts-jest": { - "version": "26.5.6", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.6.tgz", - "integrity": "sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "0.x", - "buffer-from": "1.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^26.1.0", - "json5": "2.x", - "lodash": "4.x", - "make-error": "1.x", - "mkdirp": "1.x", - "semver": "7.x", - "yargs-parser": "20.x" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": ">= 10" - }, - "peerDependencies": { - "jest": ">=26 <27", - "typescript": ">=3.8 <5.0" - } - }, - "node_modules/ts-jest/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/ts-loader": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.4.0.tgz", @@ -26936,6 +22556,25 @@ "webpack": "*" } }, +<<<<<<< HEAD + "node_modules/ts-loader/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, +======= +>>>>>>> develop "node_modules/ts-loader/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -26953,6 +22592,39 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, +<<<<<<< HEAD + "node_modules/ts-loader/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ts-loader/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ts-loader/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, +======= +>>>>>>> develop "node_modules/ts-loader/node_modules/semver": { "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", @@ -26966,6 +22638,19 @@ "node": ">=10" } }, + "node_modules/ts-loader/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -27073,16 +22758,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -27257,76 +22932,8 @@ "engines": { "node": ">= 8.0.0" }, - "peerDependencies": { - "typedoc": ">=0.17.0" - } - }, - "node_modules/typedoc-plugin-markdown/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/typedoc-plugin-markdown/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/typedoc-plugin-markdown/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/typedoc/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/typedoc/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "peerDependencies": { + "typedoc": ">=0.17.0" } }, "node_modules/typedoc/node_modules/semver": { @@ -27342,16 +22949,6 @@ "node": ">=10" } }, - "node_modules/typedoc/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/typescript": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.8.tgz", @@ -27366,33 +22963,6 @@ "node": ">=4.2.0" } }, - "node_modules/ua-parser-js": { - "version": "0.7.41", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.41.tgz", - "integrity": "sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "license": "MIT", - "bin": { - "ua-parser-js": "script/cli.js" - }, - "engines": { - "node": "*" - } - }, "node_modules/uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", @@ -27541,13 +23111,13 @@ } }, "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "license": "MIT", "engines": { - "node": ">= 4.0.0" + "node": ">= 10.0.0" } }, "node_modules/unpipe": { @@ -27626,65 +23196,6 @@ "dev": true, "license": "MIT" }, - "node_modules/unzipper": { - "version": "0.10.14", - "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", - "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "big-integer": "^1.6.17", - "binary": "~0.3.0", - "bluebird": "~3.4.1", - "buffer-indexof-polyfill": "~1.0.0", - "duplexer2": "~0.1.4", - "fstream": "^1.0.12", - "graceful-fs": "^4.2.2", - "listenercount": "~1.0.1", - "readable-stream": "~2.3.6", - "setimmediate": "~1.0.4" - } - }, - "node_modules/unzipper/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/unzipper/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/unzipper/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/unzipper/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", @@ -27697,9 +23208,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz", - "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -27755,6 +23266,22 @@ "url": "https://github.com/yeoman/update-notifier?sponsor=1" } }, + "node_modules/update-notifier/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/update-notifier/node_modules/chalk": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", @@ -27769,6 +23296,49 @@ "node": ">=8" } }, + "node_modules/update-notifier/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/update-notifier/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-notifier/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/update-notifier/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/upper-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", @@ -27786,16 +23356,6 @@ "punycode": "^2.1.0" } }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", @@ -27903,6 +23463,29 @@ "node": ">=4" } }, + "node_modules/url/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/url/node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -27978,13 +23561,14 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", "dev": true, "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "bin/uuid" } }, "node_modules/v8-compile-cache-lib": { @@ -27994,38 +23578,6 @@ "dev": true, "license": "MIT" }, - "node_modules/v8-to-istanbul": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz", - "integrity": "sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==", - "dev": true, - "license": "ISC", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true, - "license": "MIT" - }, - "node_modules/v8-to-istanbul/node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -28084,6 +23636,13 @@ "extsprintf": "^1.2.0" } }, + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, "node_modules/vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -28091,16 +23650,6 @@ "dev": true, "license": "MIT" }, - "node_modules/void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/vue": { "version": "2.7.16", "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz", @@ -28201,23 +23750,75 @@ "source-map": "0.5.6" } }, +<<<<<<< HEAD + "node_modules/vue-server-renderer/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, +======= +>>>>>>> develop "node_modules/vue-server-renderer/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, +<<<<<<< HEAD + "node_modules/vue-server-renderer/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/vue-server-renderer/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue-server-renderer/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=8" } }, +======= +>>>>>>> develop "node_modules/vue-server-renderer/node_modules/hash-sum": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", @@ -28245,6 +23846,19 @@ "node": ">=0.10.0" } }, + "node_modules/vue-server-renderer/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/vue-style-loader": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", @@ -28471,40 +24085,6 @@ "smoothscroll-polyfill": "^0.4.3" } }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, "node_modules/watchpack": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", @@ -28764,14 +24344,6 @@ "node": ">=0.10.0" } }, - "node_modules/watchpack-chokidar2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/watchpack-chokidar2/node_modules/micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -28813,23 +24385,6 @@ "node": ">=0.10.0" } }, - "node_modules/watchpack-chokidar2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, "node_modules/watchpack-chokidar2/node_modules/readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", @@ -28846,25 +24401,6 @@ "node": ">=0.10" } }, - "node_modules/watchpack-chokidar2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/watchpack-chokidar2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/watchpack-chokidar2/node_modules/to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", @@ -28891,14 +24427,11 @@ } }, "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - } + "license": "BSD-2-Clause" }, "node_modules/webpack": { "version": "4.47.0", @@ -28965,16 +24498,6 @@ "node": ">=8" } }, - "node_modules/webpack-chain/node_modules/deepmerge": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", - "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/webpack-cli": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", @@ -29091,13 +24614,6 @@ "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/webpack-dev-middleware/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, "node_modules/webpack-dev-middleware/node_modules/memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", @@ -29109,39 +24625,6 @@ "readable-stream": "^2.0.1" } }, - "node_modules/webpack-dev-middleware/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/webpack-dev-middleware/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/webpack-dev-middleware/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/webpack-dev-server": { "version": "3.11.3", "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz", @@ -29208,19 +24691,6 @@ "node": ">=0.10.0" } }, - "node_modules/webpack-dev-server/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/webpack-dev-server/node_modules/anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", @@ -29300,58 +24770,6 @@ "fsevents": "^1.2.7" } }, - "node_modules/webpack-dev-server/node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/webpack-dev-server/node_modules/cliui/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/cliui/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/webpack-dev-server/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, "node_modules/webpack-dev-server/node_modules/define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", @@ -29366,13 +24784,6 @@ "node": ">=0.10.0" } }, - "node_modules/webpack-dev-server/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true, - "license": "MIT" - }, "node_modules/webpack-dev-server/node_modules/fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -29389,19 +24800,6 @@ "node": ">=0.10.0" } }, - "node_modules/webpack-dev-server/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/webpack-dev-server/node_modules/fsevents": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", @@ -29446,16 +24844,6 @@ "node": ">=0.10.0" } }, - "node_modules/webpack-dev-server/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", @@ -29539,16 +24927,6 @@ "node": ">=0.10.0" } }, - "node_modules/webpack-dev-server/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/webpack-dev-server/node_modules/is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -29561,39 +24939,18 @@ "engines": { "node": ">=0.10.0" } - }, - "node_modules/webpack-dev-server/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/webpack-dev-server/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + }, + "node_modules/webpack-dev-server/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "is-buffer": "^1.1.5" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, "node_modules/webpack-dev-server/node_modules/micromatch": { @@ -29635,61 +24992,6 @@ "node": ">=0.10.0" } }, - "node_modules/webpack-dev-server/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/webpack-dev-server/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack-dev-server/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, "node_modules/webpack-dev-server/node_modules/readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", @@ -29728,13 +25030,6 @@ "node": ">=4" } }, - "node_modules/webpack-dev-server/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, "node_modules/webpack-dev-server/node_modules/schema-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", @@ -29750,54 +25045,6 @@ "node": ">= 4" } }, - "node_modules/webpack-dev-server/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/webpack-dev-server/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/string-width/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/string-width/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/webpack-dev-server/node_modules/strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -29838,84 +25085,6 @@ "node": ">=0.10.0" } }, - "node_modules/webpack-dev-server/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", - "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", - "dev": true, - "license": "MIT", - "dependencies": { - "async-limiter": "~1.0.0" - } - }, - "node_modules/webpack-dev-server/node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "node_modules/webpack-dev-server/node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, "node_modules/webpack-log": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", @@ -29930,17 +25099,6 @@ "node": ">= 6" } }, - "node_modules/webpack-log/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/webpack-merge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", @@ -29975,13 +25133,6 @@ "node": ">=0.4.0" } }, - "node_modules/webpack/node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true, - "license": "MIT" - }, "node_modules/webpack/node_modules/braces": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", @@ -30132,23 +25283,6 @@ "node": ">=0.10.0" } }, - "node_modules/webpack/node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, "node_modules/webpack/node_modules/json5": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", @@ -30227,22 +25361,6 @@ "node": ">=0.10.0" } }, - "node_modules/webpack/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, "node_modules/webpack/node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -30257,13 +25375,6 @@ "rimraf": "bin.js" } }, - "node_modules/webpack/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, "node_modules/webpack/node_modules/schema-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", @@ -30299,16 +25410,6 @@ "figgy-pudding": "^3.5.1" } }, - "node_modules/webpack/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/webpack/node_modules/terser": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", @@ -30382,12 +25483,31 @@ "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=14.21.3" + "node": ">=14.21.3" + }, + "peerDependencies": { + "webpack": "3 || 4 || 5" + } + }, +<<<<<<< HEAD + "node_modules/webpackbar/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" }, - "peerDependencies": { - "webpack": "3 || 4 || 5" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, +======= +>>>>>>> develop "node_modules/webpackbar/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -30405,79 +25525,87 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", +<<<<<<< HEAD + "node_modules/webpackbar/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" + "color-name": "~1.1.4" }, "engines": { - "node": ">=0.8.0" + "node": ">=7.0.0" } }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "node_modules/webpackbar/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT" + }, + "node_modules/webpackbar/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=0.8.0" + "node": ">=8" } }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "node_modules/webpackbar/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { - "iconv-lite": "0.6.3" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", +======= +>>>>>>> develop + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.8.0" } }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=0.8.0" } }, "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", "dev": true, "license": "MIT", "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" } }, "node_modules/when": { @@ -30577,9 +25705,9 @@ "license": "ISC" }, "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -30663,25 +25791,42 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -30703,25 +25848,13 @@ } }, "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", + "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "dependencies": { + "async-limiter": "~1.0.0" } }, "node_modules/xdg-basedir": { @@ -30734,16 +25867,18 @@ "node": ">=8" } }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "node_modules/xmlbuilder": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", + "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=6.0" } }, +<<<<<<< HEAD +======= "node_modules/xmlbuilder": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", @@ -30761,6 +25896,7 @@ "dev": true, "license": "MIT" }, +>>>>>>> develop "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -30786,63 +25922,87 @@ "license": "ISC" }, "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, "license": "MIT", "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", + "cliui": "^5.0.0", + "find-up": "^3.0.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^4.2.0", + "string-width": "^3.0.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" + "yargs-parser": "^13.1.2" } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=6" } }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true, + "license": "MIT" + }, "node_modules/yargs/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "locate-path": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" } }, "node_modules/yargs/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^4.1.0" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" } }, "node_modules/yargs/node_modules/p-limit": { @@ -30862,41 +26022,54 @@ } }, "node_modules/yargs/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" + "p-limit": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "node_modules/yargs/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, - "license": "ISC", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "license": "MIT", "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" }, "engines": { "node": ">=6" } }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "node_modules/yargs/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "license": "MIT", "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" } }, "node_modules/yn": { @@ -30928,43 +26101,6 @@ "integrity": "sha512-C1x6lfvBICFTQIMgbt3JqMOno3VOtkWat/xEakLTOurskYIHPmzJrzd1e8BnmtdDVJlGuk5D+FxyCA8MPmkIyA==", "dev": true, "license": "MIT" - }, - "node_modules/zip-stream": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", - "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "archiver-utils": "^3.0.4", - "compress-commons": "^4.1.2", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/zip-stream/node_modules/archiver-utils": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", - "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob": "^7.2.3", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } } } } diff --git a/package.json b/package.json index 230f7ab6c3..4983c4c18c 100644 --- a/package.json +++ b/package.json @@ -75,28 +75,13 @@ "verify:cjs": "node script/check-file.js commonjs", "verify:publish-package": "npm pack | node script/check-publish-package.js", "verify:typings": "tsc --noEmit", - "test": "npm-run-all lint test:unit test:browser test:compatibility", - "test:unit": "cross-env NODE_ICU_DATA=node_modules/full-icu jest", - "test:watch": "cross-env NODE_ICU_DATA=node_modules/full-icu jest --watch", - "test:tdd": "cross-env NODE_ICU_DATA=node_modules/full-icu jest --watch function-value", - "test:coverage": "npm run test:unit -- --coverage", - "test:logMemory": "cross-env NODE_ICU_DATA=node_modules/full-icu jest --runInBand --logHeapUsage", - "test:unit.ci": "cross-env NODE_ICU_DATA=node_modules/full-icu node --expose-gc ./node_modules/jest/bin/jest --forceExit", - "test:browser": "cross-env-shell BABEL_ENV=dist env-cmd -f ht.config.js karma start", - "test:browser.debug": "cross-env-shell BABEL_ENV=dist NODE_ENV=debug env-cmd -f ht.config.js karma start", - "test:performance": "npm run benchmark:basic && npm run benchmark:cruds", - "test:compatibility": "bash test/compatibility/test-compatibility.sh", "typedoc:build-api": "cross-env NODE_OPTIONS=--openssl-legacy-provider typedoc --options .typedoc.md.ts", - "benchmark:basic": "npm run tsnode test/performance/run-basic-benchmark.ts", - "benchmark:cruds": "npm run tsnode test/performance/run-cruds-benchmark.ts", - "benchmark:write-to-file": "npm run tsnode test/performance/write-to-file.ts", - "benchmark:compare-benchmarks": "npm run tsnode test/performance/compare-benchmarks.ts", "lint": "eslint . --ext .js,.ts", "lint:fix": "eslint . --ext .js,.ts --fix", "audit": "npm audit --omit=dev", - "clean": "rimraf coverage/ commonjs/ dist/ es/ languages/ lib/ typings/ test-jasmine/", + "clean": "rimraf commonjs/ dist/ es/ languages/ lib/ typings/", "compile": "tsc", - "check:licenses": "license-checker --production --excludePackages=\"hyperformula@3.0.1\" --onlyAllow=\"MIT; Apache-2.0; BSD-3-Clause; BSD-2-Clause; ISC; BSD; Unlicense\"", + "check:licenses": "license-checker --production --excludePackages=\"hyperformula@3.1.1\" --onlyAllow=\"MIT; Apache-2.0; BSD-3-Clause; BSD-2-Clause; ISC; BSD; Unlicense\"", "tsnode": "ts-node --transpile-only -O {\\\"module\\\":\\\"commonjs\\\"}" }, "dependencies": { @@ -118,10 +103,6 @@ "@babel/preset-typescript": "^7.26.0", "@babel/register": "^7.25.9", "@babel/runtime": "^7.26.0", - "@types/exceljs": "^0.5.3", - "@types/jasmine": "^5.1.4", - "@types/jest": "^26.0.24", - "@types/jsdom": "^21.1.7", "@types/node": "^17.0.45", "@types/webpack-env": "^1.18.5", "@typescript-eslint/eslint-plugin": "^5.62.0", @@ -134,24 +115,10 @@ "env-cmd": "^10.1.0", "eslint": "^8.57.1", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-jasmine": "^4.2.2", - "eslint-plugin-jest": "^27.9.0", "eslint-plugin-jsdoc": "^50.5.0", "eslint-plugin-license-header": "^0.6.1", "eslint-plugin-prettier": "^5.2.1", "esm": "^3.2.25", - "exceljs": "^4.4.0", - "full-icu": "^1.5.0", - "jasmine": "^5.4.0", - "jest": "^26.6.3", - "jsdom": "^25.0.1", - "karma": "^6.4.4", - "karma-chrome-launcher": "^3.2.0", - "karma-firefox-launcher": "^2.1.3", - "karma-jasmine": "^5.1.0", - "karma-jasmine-html-reporter": "^2.1.0", - "karma-sourcemap-loader": "^0.4.0", - "karma-webpack": "^4.0.2", "license-checker": "^25.0.1", "markdown-it-footnote": "^4.0.0", "markdown-it-regex": "^0.2.0", @@ -162,7 +129,6 @@ "string-replace-loader": "^2.3.0", "tar": "^7.4.3", "terser-webpack-plugin": "^4.2.3", - "ts-jest": "^26.5.6", "ts-loader": "^8.4.0", "ts-node": "^10.9.2", "typedoc": "^0.19.2", diff --git a/test/compatibility/README.md b/test/compatibility/README.md deleted file mode 100644 index 2bf23dc1dc..0000000000 --- a/test/compatibility/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# Compatibility Testing - -This directory contains tools for testing HyperFormula's compatibility with Excel by comparing evaluation results. - -## Overview - -This compatibiloty testing tool performs the following steps for each .xlsx in `test_data/` directory: -1. read both formulas and calculated values from the XLSX file -2. evaluate the formulas using HyperFormula -3. compare the HyperFormula's results against the original values -4. report differences - -## Structure - -- **`test-compatibility.sh`** - Main test runner that processes all Excel files in `test_data/` -- **`compare-evaluation-results.ts`** - Core comparison tool -- **`test_data/`** - Collection of Excel test files covering various features - -## Running - -Run all compatibility tests: -```bash -npm run test:compatibility -``` - -Test a specific Excel file: -```bash -ts-node --transpile-only -O '{"module":"commonjs"}' test/compatibility/compare-evaluation-results.ts test/compatibility/test_data/your-file.xlsx -``` - -## Adding Test Cases - -1. Create the XLSX file with both formulas and calculated values using MS Excel or Google Sheets -2. Put the file into `test_data/` directory diff --git a/test/compatibility/compare-evaluation-results.ts b/test/compatibility/compare-evaluation-results.ts deleted file mode 100644 index 02dc205e9a..0000000000 --- a/test/compatibility/compare-evaluation-results.ts +++ /dev/null @@ -1,275 +0,0 @@ -import { CellValue, Workbook, Worksheet } from 'exceljs' -import { ConfigParams, DetailedCellError, HyperFormula, RawCellContent, SerializedNamedExpression, Sheets } from '../../src' - -const HF_CONFIG: Partial = { - licenseKey: 'gpl-v3', - dateFormats: ['MM/DD/YYYY', 'MM/DD/YY', 'YYYY/MM/DD'], - currencySymbol: ['$', 'USD'], - localeLang: 'en-US', - accentSensitive: true, - useArrayArithmetic: true, - ignoreWhiteSpace: 'any', - evaluateNullToZero: true, - leapYear1900: true, - nullDate: { year: 1899, month: 12, day: 31 }, -} - -const NAMED_EXPRESSIONS: SerializedNamedExpression[] = [ - { name: 'TRUE', expression: '=TRUE()' }, - { name: 'FALSE', expression: '=FALSE()' }, -] - -interface CellDiff { - row: number, - col: number, - hf: RawCellContent, - source: RawCellContent, -} - -interface SheetsDiff { - [sheetName: string]: CellDiff[], -} - -/** - * Utility class for comparing values with proper equality logic - */ -class ValueComparator { - private readonly nullLikeValues: unknown[] = [0, '', false, null, undefined] - - /** - * Creates an instance of ValueComparator. - * @param {number} [epsilon=Number.EPSILON] - The tolerance used for floating point comparisons. - */ - constructor(private epsilon: number = Number.EPSILON) {} - - /** - * Determines if two values are equal - * @param {unknown} rawValA - First value to compare - * @param {unknown} rawValB - Second value to compare - * @returns {boolean} true if values are equal, false otherwise - */ - areEqual(rawValA: unknown, rawValB: unknown): boolean { - const valA = this.normalize(rawValA) - const valB = this.normalize(rawValB) - - // Handle number comparison with potential floating point precision issues - if (typeof valA === 'number' && typeof valB === 'number') { - if (isNaN(valA) && isNaN(valB)) return true - return Math.abs(valA - valB) < this.epsilon - } - - // Handle object comparison (dates, etc.) - if (typeof valA === 'object' && typeof valB === 'object') { - return JSON.stringify(valA) === JSON.stringify(valB) - } - - return valA === valB - } - - /** - * Normalizes values for comparison (converts null-like values to 0, extracts error values) - * @param {unknown} val - Value to normalize - * @returns {unknown} Normalized value - */ - normalize(val: unknown): unknown { - if (val instanceof DetailedCellError) { - // console.error('HF Error:', val.value, val.message) - return val.value - } - - if (this.nullLikeValues.includes(val)) { - return 0 - } - - return val - } -} - -/** - * Main function to read Excel file and process it with HyperFormula - */ -async function run(): Promise { - try { - const valueComparator = new ValueComparator(0.000000001) - const filename = process.argv[2] - - if (!filename) { - console.error('Usage: ts-node read-excel-file.ts ') - process.exit(1) - } - - const [readFormulas, readValues] = await readFormulasAndValuesFromXlsxFile(filename) - // console.error('Read formulas:', readFormulas) - const [hfFormulas, hfValues] = evaluateSheet(readFormulas) - - const diffFormulas = compareSheets(hfFormulas, readFormulas) - const diffValues = compareSheets(hfValues, readValues, valueComparator) - - const hasFormulaDiffs = Object.keys(diffFormulas).length > 0 - const hasValueDiffs = Object.keys(diffValues).length > 0 - - if (hasFormulaDiffs) { - console.log('Diff formulas:', diffFormulas) - } - - if (hasValueDiffs) { - console.log('Diff values:', diffValues) - } - - if (hasFormulaDiffs || hasValueDiffs) { - process.exit(1) - } - - process.exit(0) - } catch (error: unknown) { - console.error('Error:', error) - process.exit(1) - } -} - -/** - * Compares two sheets and returns the differences - */ -function compareSheets(hfCollection: Sheets, sourceCollection: Sheets, comparator?: ValueComparator): SheetsDiff { - const allSheetNames = new Set([...Object.keys(hfCollection), ...Object.keys(sourceCollection)]) - - const allSheetsDiff = Array.from(allSheetNames).reduce((acc, sheetName: string) => { - const sheetDiff = compareSingleSheet(hfCollection[sheetName] || [], sourceCollection[sheetName] || [], comparator) - - if (sheetDiff.length > 0) { - acc[sheetName] = sheetDiff - } - - return acc - }, {}) - - return allSheetsDiff -} - -/** - * Compares two single sheet data arrays and returns the differences - */ -function compareSingleSheet(hfData: RawCellContent[][], sourceData: RawCellContent[][], comparator?: ValueComparator): CellDiff[] { - const compare = comparator ? comparator.areEqual.bind(comparator) : (a: unknown, b: unknown) => a === b - - const maxRows = Math.max(hfData.length, sourceData.length) - const sheetDiff = [] as CellDiff[] - - for (let row = 0; row < maxRows; row++) { - const maxCols = Math.max(hfData[row]?.length ?? 0, sourceData[row]?.length ?? 0) - - for (let col = 0; col < maxCols; col++) { - const hfCell = hfData[row]?.[col] ?? null - const sourceCell = sourceData[row]?.[col] ?? null - - if (!compare(hfCell, sourceCell)) { - sheetDiff.push({ - row, - col, - hf: hfCell, - source: sourceCell - }) - } - } - } - - return sheetDiff -} - -/** - * Evaluates formulas using HyperFormula and returns computed values - */ -function evaluateSheet(formulas: Sheets): [Sheets, Sheets] { - const hf = HyperFormula.buildFromSheets(formulas, HF_CONFIG, NAMED_EXPRESSIONS) - return [hf.getAllSheetsSerialized() as Sheets, hf.getAllSheetsValues() as Sheets] -} - -/** - * Clears formulas from _xlfn. prefix - */ -function clearFormulas(formulas: Sheets): Sheets { - return Object.entries(formulas).reduce((acc, [sheetName, sheet]) => { - acc[sheetName] = sheet.map((row) => row.map((cell) => typeof cell === 'string' ? cell.replace('_xlfn.', '') : cell)) - return acc - }, {}) -} - -/** - * Reads formulas and values from an Excel file - */ -async function readFormulasAndValuesFromXlsxFile(filename: string): Promise<[Sheets, Sheets]> { - const workbook = await readXlsxWorkbookFromFile(filename) - const [formulas, values] = convertXlsxWorkbookToFormulasAndValuesArrays(workbook) - const cleanFormulas = clearFormulas(formulas) - return [cleanFormulas, values] -} - -/** - * Reads an Excel workbook from file - */ -async function readXlsxWorkbookFromFile(filename: string): Promise { - const workbook = new Workbook() - await workbook.xlsx.readFile(filename) - return workbook -} - -/** - * Converts Excel workbook to JavaScript arrays format - */ -function convertXlsxWorkbookToFormulasAndValuesArrays(workbook: Workbook): [Sheets, Sheets] { - const workbookData: Sheets = {} - const readValues: Sheets = {} - - workbook.eachSheet((worksheet: Worksheet) => { - const sheetData: RawCellContent[][] = [] - const sheetReadValues: RawCellContent[][] = [] - - const dimensions = worksheet.dimensions - if (!dimensions) { - workbookData[worksheet.name] = sheetData - readValues[worksheet.name] = sheetReadValues - return - } - - for (let rowNum = dimensions.top; rowNum <= dimensions.bottom; rowNum++) { - const rowData: RawCellContent[] = [] - const rowReadValues: RawCellContent[] = [] - - for (let colNum = dimensions.left; colNum <= dimensions.right; colNum++) { - const cell = worksheet.getCell(rowNum, colNum) - - const cellData = cell.formula ? `=${cell.formula}` : cell.value as RawCellContent - const cellValue = readCellValue(cell.value) - - rowData.push(cellData) - rowReadValues.push(cellValue) - } - - sheetData.push(rowData) - sheetReadValues.push(rowReadValues) - } - - workbookData[worksheet.name] = sheetData - readValues[worksheet.name] = sheetReadValues - }) - - return [workbookData, readValues] -} - -/** - * Extracts the actual cell value from an Excel cell value object - * @param {CellValue} cellValueObject - The Excel cell value object to extract from - * @returns {RawCellContent} The extracted cell value - */ -function readCellValue(cellValueObject: CellValue): RawCellContent { - if (!cellValueObject) { - return null - } - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - // eslint-disable-next-line - return cellValueObject.result?.error ?? cellValueObject.result ?? (cellValueObject.formula != null ? 0 : cellValueObject) -} - -void run() diff --git a/test/compatibility/test-compatibility.sh b/test/compatibility/test-compatibility.sh deleted file mode 100755 index 81e273da14..0000000000 --- a/test/compatibility/test-compatibility.sh +++ /dev/null @@ -1,13 +0,0 @@ -for file in test/compatibility/test_data/*.xlsx; do - [ ! -f "$file" ] && continue - filename=$(basename "$file") - [[ "$filename" =~ ^~ ]] && continue - echo -e "\033[34mProcessing: $filename\033[0m" - ts-node --transpile-only -O {\"module\":\"commonjs\"} test/compatibility/compare-evaluation-results.ts "$file" - if [ $? -ne 0 ]; then - exit 1 - fi - echo -e "\033[34mDone: $filename\033[0m" -done - -exit 0 diff --git a/test/compatibility/test_data/basic-arithmetic.xlsx b/test/compatibility/test_data/basic-arithmetic.xlsx deleted file mode 100644 index f6d1aece03..0000000000 Binary files a/test/compatibility/test_data/basic-arithmetic.xlsx and /dev/null differ diff --git a/test/compatibility/test_data/logical-conditions.xlsx b/test/compatibility/test_data/logical-conditions.xlsx deleted file mode 100644 index 0c12788a44..0000000000 Binary files a/test/compatibility/test_data/logical-conditions.xlsx and /dev/null differ diff --git a/test/compatibility/test_data/lookup-basic.xlsx b/test/compatibility/test_data/lookup-basic.xlsx deleted file mode 100644 index 9834464f4e..0000000000 Binary files a/test/compatibility/test_data/lookup-basic.xlsx and /dev/null differ diff --git a/test/compatibility/test_data/sample_file.xlsx b/test/compatibility/test_data/sample_file.xlsx deleted file mode 100644 index 4c2f03cb71..0000000000 Binary files a/test/compatibility/test_data/sample_file.xlsx and /dev/null differ diff --git a/test/compatibility/test_data/text-functions.xlsx b/test/compatibility/test_data/text-functions.xlsx deleted file mode 100644 index 5dbee91580..0000000000 Binary files a/test/compatibility/test_data/text-functions.xlsx and /dev/null differ diff --git a/test/performance/basic-benchmark.ts b/test/performance/basic-benchmark.ts deleted file mode 100644 index 324a66c448..0000000000 --- a/test/performance/basic-benchmark.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {batch, benchmark, BenchmarkResult} from './benchmark' -import {expectedValues as expectedValuesT, sheet as sheetTGenerator} from './sheets/05-sheet-t' -import {expectedValues as expectedValuesA, sheet as sheetAGenerator} from './sheets/09-sheet-a' -import {expectedValues as expectedValuesB, sheet as sheetBGenerator} from './sheets/10-sheet-b' -import {sheet as columnRangesGenerator} from './sheets/column-ranges' - -export function runBasicBenchmark(): BenchmarkResult[] { - const result: BenchmarkResult[] = [] - const sheetA = sheetAGenerator() - const sheetB = sheetBGenerator() - const sheetT = sheetTGenerator() - const infiniteRanges = columnRangesGenerator() - - batch(result, - () => benchmark('Sheet A', sheetA, expectedValuesA(sheetA), { numberOfRuns: 100 }), - () => benchmark('Sheet B', sheetB, expectedValuesB(sheetB), { numberOfRuns: 100 }), - () => benchmark('Sheet T', sheetT, expectedValuesT(sheetT), { numberOfRuns: 100 }), - () => benchmark('Column ranges', infiniteRanges, [{ - address: 'AX50', - value: 1.04519967355127e+63 - }], { expectedTime: 1000, numberOfRuns: 100 }) - ) - - return result -} diff --git a/test/performance/benchmark.ts b/test/performance/benchmark.ts deleted file mode 100644 index 077cef3749..0000000000 --- a/test/performance/benchmark.ts +++ /dev/null @@ -1,169 +0,0 @@ -import {CellValue, ConfigParams, HyperFormula, Sheet} from '../../src' -import {Maybe} from '../../src/Maybe' -import { - average, - EnrichedStatType, - enrichStatistics, - measureCruds, - reduceStats, - Stats, - statsTreePrint, - statsTreePrintCruds -} from './utils/stats' - -export interface Config { - expectedTime?: number, - numberOfRuns: number, - engineConfig?: Partial, -} - -export const defaultConfig: Config = { - numberOfRuns: 1, -} - -export const defaultEngineConfig: Partial = { - useStats: true, - licenseKey: 'gpl-v3' -} - -export interface ExpectedValue { - address: string, - value: CellValue, -} - -export interface BenchmarkResult { - name: string, - engine: HyperFormula, - totalTime: number, - statistics: Stats, -} - -export function benchmark( - name: string, - sheet: Sheet, - expectedValues: ExpectedValue[], - config: Partial = defaultConfig -): Maybe { - const runEngine = (engineConfig?: Partial) => HyperFormula.buildFromArray(sheet, engineConfig) - return benchmarkBuild(name, runEngine, expectedValues, config) -} - -export function benchmarkCruds( - name: string, - sheet: Sheet, - cruds: (engine: HyperFormula) => void, - expectedValues: ExpectedValue[], - userConfig: Partial = defaultConfig, -): Maybe { - console.info(`=== Benchmark - ${name} === `) - - const config = Object.assign({}, defaultConfig, userConfig) - const engineConfig = Object.assign({}, config.engineConfig, defaultEngineConfig) - - const statistics: Stats[] = [] - - let currentRun = 0 - let engine: HyperFormula | undefined - - do { - engine = HyperFormula.buildFromArray(sheet, engineConfig) - statistics.push(enrichStatistics(measureCruds(engine, name, cruds))) - - currentRun++ - - if (!validate(engine, expectedValues)) { - console.error('Sheet validation error') - if (process.exit) { - process.exit(1) - } - return - } - } while (currentRun < config.numberOfRuns) - - const averages = reduceStats(statistics, average) - const totalTime = averages.get(EnrichedStatType.CRUDS_TOTAL) || 0 - statsTreePrintCruds(averages) - - return { - name: name, - engine: engine, - totalTime: totalTime, - statistics: averages, - } -} - -export function batch(stats: BenchmarkResult[], ...benchmarks: (() => Maybe)[]): void { - for (const benchmark of benchmarks) { - const result = benchmark() - if (result !== undefined) { - stats.push(result) - } - } -} - -function benchmarkBuild( - name: string, - runEngine: (engineConfig?: Partial) => HyperFormula, - expectedValues: ExpectedValue[], - userConfig: Partial = defaultConfig, -): Maybe { - console.info(`=== Benchmark - ${name} === `) - - const config = Object.assign({}, defaultConfig, userConfig) - const engineConfig = Object.assign({}, config.engineConfig, defaultEngineConfig) - - const statistics: Stats[] = [] - - let currentRun = 0 - let engine: HyperFormula - - do { - engine = runEngine(engineConfig) - statistics.push(enrichStatistics(engine.getStats())) - - currentRun++ - - if (!validate(engine, expectedValues)) { - console.error('Sheet validation error') - if (process.exit) { - process.exit(1) - } - return - } - } while (currentRun < config.numberOfRuns) - - const averages = reduceStats(statistics, average) - const totalTime = averages.get(EnrichedStatType.BUILD_ENGINE_TOTAL) || 0 - statsTreePrint(averages) - - return { - name: name, - engine: engine, - statistics: averages, - totalTime: totalTime - } -} - -function validate(engine: HyperFormula, expectedValues: ExpectedValue[]) { - let valid = true - - for (let i = 0; i < expectedValues.length; ++i) { - let expectedValue = expectedValues[i].value - - const address = engine.simpleCellAddressFromString(expectedValues[i].address, 0)! - let actualValue = engine.getCellValue(address) - - if (typeof expectedValue === 'number' && typeof actualValue === 'number') { - expectedValue = expectedValue.toPrecision(8) - actualValue = actualValue.toPrecision(8) - } - - if (actualValue !== expectedValue) { - console.error(`expected ${expectedValue} but got`, actualValue) - valid = false - break - } - } - - return valid -} diff --git a/test/performance/compare-benchmarks.ts b/test/performance/compare-benchmarks.ts deleted file mode 100644 index 0503ae32a2..0000000000 --- a/test/performance/compare-benchmarks.ts +++ /dev/null @@ -1,93 +0,0 @@ -import fs = require('fs') -import asTable = require('as-table') - -interface ResultSuite { - suiteName: string, - results: { testName: string, time: number }[], -} - -(() => { - try { - const args = process.argv.slice(2) - - if (!args || args.length !== 3) { - console.log('Usage:\n$ npm run benchmark:compare-benchmarks base-benchmarks.json current-change-benchmarks.json output-file.md') - return - } - - const inputFilenames = args.slice(0, 2) - const outputFilename = args[2] - - const resultSuites: ResultSuite[] = inputFilenames.map(readResultSuiteFromFile) - const benchmarkTable = buildBenchmarkTable(resultSuites) - writeTableToFile(benchmarkTable, outputFilename) - } catch (err) { - console.error(err) - } -})() - -function writeTableToFile(tableData: { [key: string]: string | number }[], filename: string): void { - const tableRenderer = asTable.configure({ delimiter: ' | ', right: true }) - const renderedTable = `\n\n\`\`\`\n${tableRenderer(tableData)}\n\`\`\`\n` - - try { - fs.writeFileSync(filename, renderedTable) - console.log(`Output written to file ${filename}`) - } catch (err) { - throw new Error(`Cannot write data to file ${filename}\n${err}`) - } -} - -function readResultSuiteFromFile(filename: string): ResultSuite { - const suiteName = filenameWithNoExtension(filename) - const resultsFromFile = readResultsFromFile(filename) - const results = resultsFromFile.map(item => ({ testName: item.name, time: item.totalTime })) - return { suiteName, results } -} - -function filenameWithNoExtension(filename: string): string { - return filename.replace(/\.[^/.]+$/, '') -} - -function readResultsFromFile(filename: string): { name: string, totalTime: number }[] { - try { - const rawFileContent = fs.readFileSync(filename) - return JSON.parse(rawFileContent.toString()) - } catch (err) { - throw new Error(`Cannot read results from ${filename}\n${err}`) - } -} - -function buildBenchmarkTable(resultSuites: ResultSuite[]): { [key: string]: string | number }[] { - const testNames = resultSuites[0].results.map(bm => bm.testName) - return testNames.map(testName => buildBenchmarkTableRow(testName, resultSuites)) -} - -function buildBenchmarkTableRow(testName: string, resultSuites: ResultSuite[]): { [key: string]: string | number } { - const testResultsEntries = resultSuites.map(resultSuite => findTestResultInSuite(testName, resultSuite)) - const changeEntries = calculateChangeFromBase(testResultsEntries) - const testResultsObj = (Object as any).fromEntries([ ...testResultsEntries, ...changeEntries ]) - return { testName, ...testResultsObj } -} - -function calculateChangeFromBase(testResultsEntries: [string, number][]): [string, string][] { - const [baseSuiteName, baseSuiteResult] = testResultsEntries[0] - const resultsWithoutBase = testResultsEntries.filter(([currSuiteName, _]) => currSuiteName !== baseSuiteName) - return resultsWithoutBase.map(currResultEntry => buildChangeEntry(currResultEntry, baseSuiteResult)) -} - -function buildChangeEntry([_, currSuiteResult]: [string, number], baseSuiteResult: number): [string, string] { - const change = (currSuiteResult - baseSuiteResult) / baseSuiteResult * 100.0 - const formattedChange = `${change > 0.0 ? '+' : ''}${change.toFixed(2)}%` - return ['change', formattedChange] -} - -function findTestResultInSuite(testName: string, resultSuite: ResultSuite): [string, number] { - const resultItem = resultSuite.results.find(res => res.testName === testName) - - if (!resultItem) { - throw new Error(`Cannot find result for test '${testName}' in suite ${resultSuite.suiteName}`) - } - - return [resultSuite.suiteName, resultItem.time] -} diff --git a/test/performance/cruds-benchmark.ts b/test/performance/cruds-benchmark.ts deleted file mode 100644 index 5993009c72..0000000000 --- a/test/performance/cruds-benchmark.ts +++ /dev/null @@ -1,67 +0,0 @@ -import {HyperFormula} from '../../src' -import {batch, benchmarkCruds, BenchmarkResult} from './benchmark' -import {sheet as sheetAGenerator} from './sheets/09-sheet-a' -import {sheet as sheetBGenerator} from './sheets/10-sheet-b' -import {sheet as columnRangesGenerator} from './sheets/column-ranges' -import {adr} from './utils/utils' - -export function runCrudsBenchmark(): BenchmarkResult[] { - const result: BenchmarkResult[] = [] - const sheetA = sheetAGenerator(10000) - const sheetB = sheetBGenerator(5000) - const columnRanges = columnRangesGenerator() - - batch(result, - () => benchmarkCruds('Sheet A: change value, add/remove row/column', sheetA, (engine: HyperFormula) => { - engine.setCellContents(adr('A1'), '123') - engine.addRows(0, [5000, 1]) - engine.removeRows(0, [8000, 1]) - engine.addColumns(0, [0, 1]) - engine.removeColumns(0, [0, 1]) - }, [ - {address: 'E7000', value: -1.17344394901827e+23}, - ], { numberOfRuns: 100 }), - - () => benchmarkCruds('Sheet B: change value, add/remove row/column', sheetB, (engine: HyperFormula) => { - engine.setCellContents(adr('A1'), '123') - engine.addRows(0, [2000, 1]) - engine.removeRows(0, [3000, 1]) - engine.addColumns(0, [0, 1]) - engine.removeColumns(0, [0, 1]) - }, [ - {address: 'E50', value: 1347}, - {address: 'E2002', value: 2001122}, - ], { numberOfRuns: 100 }), - - () => benchmarkCruds('Column ranges - add column', columnRanges, (engine: HyperFormula) => { - engine.addColumns(0, [1, 1]) - engine.setCellContents(adr('A1'), 5) - }, [ - {address: 'AY50', value: 3.47832712968835e+63}, - ], { numberOfRuns: 100 }), - - () => benchmarkCruds('Column ranges - without batch', columnRanges, (engine: HyperFormula) => { - engine.setCellContents(adr('A1'), 1) - engine.setCellContents(adr('A1'), 2) - engine.setCellContents(adr('A1'), 3) - engine.setCellContents(adr('A1'), 4) - engine.setCellContents(adr('A1'), 5) - }, [ - {address: 'AX50', value: 3.47832712968835e+63}, - ], { numberOfRuns: 100 }), - - () => benchmarkCruds('Column ranges - batch', columnRanges, (engine: HyperFormula) => { - engine.batch(() => { - engine.setCellContents(adr('A1'), 1) - engine.setCellContents(adr('A1'), 2) - engine.setCellContents(adr('A1'), 3) - engine.setCellContents(adr('A1'), 4) - engine.setCellContents(adr('A1'), 5) - }) - }, [ - {address: 'AX50', value: 3.47832712968835e+63}, - ], { numberOfRuns: 100 }) - ) - - return result -} diff --git a/test/performance/run-basic-benchmark.ts b/test/performance/run-basic-benchmark.ts deleted file mode 100644 index 7851f54730..0000000000 --- a/test/performance/run-basic-benchmark.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { runBasicBenchmark } from './basic-benchmark' - -(() => { - const result = runBasicBenchmark() - console.table(result.map(e => ({ - name: e.name, - totalTime: e.totalTime - }))) -})() diff --git a/test/performance/run-cruds-benchmark.ts b/test/performance/run-cruds-benchmark.ts deleted file mode 100644 index 563629fbf1..0000000000 --- a/test/performance/run-cruds-benchmark.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { runCrudsBenchmark } from './cruds-benchmark' - -(() => { - const result = runCrudsBenchmark() - console.table(result.map(e => ({ - name: e.name, - totalTime: e.totalTime - }))) -})() diff --git a/test/performance/sheets/05-sheet-t.ts b/test/performance/sheets/05-sheet-t.ts deleted file mode 100644 index 89513c719e..0000000000 --- a/test/performance/sheets/05-sheet-t.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {ExpectedValue} from '../benchmark' -import {RawCellContent, Sheet} from '../../../src' - -export function sheet(rows: number = 10000): Sheet { - const sheet: Sheet = [] - - let prev = 1 - - const prettyRandomString = (chars: number) => [...Array(chars)].map(() => (~~(Math.random() * 36)).toString(36)).join('') - - while (prev <= rows) { - const rowToPush: RawCellContent[] = [ - prettyRandomString(30), - prettyRandomString(30), - `=CONCATENATE(A${prev}, B${prev})`, - ] - - sheet.push(rowToPush) - ++prev - } - return sheet -} - -export function expectedValues(sheet: Sheet): ExpectedValue[] { - return [ - {address: 'C1', value: `${sheet[0][0]}${sheet[0][1]}`}, - {address: 'C1000', value: `${sheet[999][0]}${sheet[999][1]}`}, - {address: 'C10000', value: `${sheet[9999][0]}${sheet[9999][1]}`}, - ] -} diff --git a/test/performance/sheets/09-sheet-a.ts b/test/performance/sheets/09-sheet-a.ts deleted file mode 100644 index f88e1c1cb5..0000000000 --- a/test/performance/sheets/09-sheet-a.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {RawCellContent, Sheet} from '../../../src' -import {ExpectedValue} from '../benchmark' - -export function sheet(rows: number = 10000): Sheet { - const sheet: Sheet = [] - sheet.push(['1', '2', '3', '4', '5']) - - let prev = 1 - - while (prev < rows) { - const rowToPush: RawCellContent[] = [ - `${prev + 1}`, - '3', - `=A${prev}*A${prev}`, - `=C${prev + 1}*A${prev}+B${prev}`, - `=C${prev}-D${prev}*D${prev}+D${prev}*C${prev}/7+C${prev}*C${prev}*3+7*2`, - ] - - sheet.push(rowToPush) - ++prev - } - return sheet -} - -export function expectedValues(_sheet: Sheet): ExpectedValue[] { - return [ - {address: 'A10000', value: 10000}, - {address: 'B10000', value: 3}, - {address: 'C10000', value: 99980001}, - ] -} diff --git a/test/performance/sheets/10-sheet-b.ts b/test/performance/sheets/10-sheet-b.ts deleted file mode 100644 index 5a8bb0d9ff..0000000000 --- a/test/performance/sheets/10-sheet-b.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {ExpectedValue} from '../benchmark' -import {RawCellContent, Sheet} from '../../../src' - -export function sheet(rows: number = 5000): Sheet { - const sheet: Sheet = [] - sheet.push(['1', '2', '3', '0', '0']) - - let prev = 1 - - while (prev < rows) { - const rowToPush: RawCellContent[] = [ - `${prev + 1}`, - '2', - '=3*5', - `=A${prev}+D${prev}`, - `=SUM($A$1:A${prev})`, - ] - - sheet.push(rowToPush) - ++prev - } - return sheet -} - -export function expectedValues(_sheet: Sheet): ExpectedValue[] { - return [ - {address: 'A5000', value: 5000}, - {address: 'B5000', value: 2}, - {address: 'C5000', value: 15}, - {address: 'D5000', value: 12497500}, - {address: 'E5000', value: 12497500}, - ] -} diff --git a/test/performance/sheets/column-ranges.ts b/test/performance/sheets/column-ranges.ts deleted file mode 100644 index b630891faa..0000000000 --- a/test/performance/sheets/column-ranges.ts +++ /dev/null @@ -1,32 +0,0 @@ -import {RawCellContent, Sheet} from '../../../src' -import {columnIndexToLabel, simpleCellAddressToString} from '../../../src/parser/addressRepresentationConverters' - -export function sheet(cols: number = 50): Sheet { - const sheet: Sheet = [] - - const firstRow: RawCellContent[] = [1] - - for (let i = 1; i < cols; ++i) { - const adr = simpleCellAddressToString(() => '', {sheet: 0, row: 0, col: i - 1}, 0) - firstRow.push(`=${adr} + 1`) - } - - sheet.push(firstRow) - - for (let i = 1; i < cols; ++i) { - const rowToPush: RawCellContent[] = [] - - rowToPush.push(...Array(i).fill(null)) - - const startColumn = columnIndexToLabel(i - 1) - - for (let j = i - 1; j < cols - 1; ++j) { - const endColumn = columnIndexToLabel(j) - rowToPush.push(`=SUM(${startColumn}:${endColumn})`) - } - - sheet.push(rowToPush) - } - - return sheet -} diff --git a/test/performance/utils/stats.ts b/test/performance/utils/stats.ts deleted file mode 100644 index 61ed562e99..0000000000 --- a/test/performance/utils/stats.ts +++ /dev/null @@ -1,87 +0,0 @@ -import {HyperFormula} from '../../../src' -import {StatType} from '../../../src/statistics' - -export type Stats = Map - -export enum ExtStatType { - INIT_DATASTRUCTURES = 'INIT_DATASTRUCTURES', - PREPROCESSING = 'PREPROCESSING', - CRUDS_TOTAL = 'CRUDS_TOTAL', -} - -export const EnrichedStatType = {...StatType, ...ExtStatType} -export type EnrichedStatType = StatType | ExtStatType - -export function enrichStatistics(stats: Stats): Stats { - const initDatastructures = (stats.get(EnrichedStatType.GRAPH_BUILD) || 0) - (stats.get(EnrichedStatType.PARSER) || 0) - const preprocessing = (stats.get(EnrichedStatType.GRAPH_BUILD) || 0) + (stats.get(EnrichedStatType.TOP_SORT) || 0) - const enriched = new Map(stats) - enriched.set(EnrichedStatType.INIT_DATASTRUCTURES, initDatastructures) - enriched.set(EnrichedStatType.PREPROCESSING, preprocessing) - return enriched -} - -export function measureCruds(engine: HyperFormula, name: string, func: (engine: HyperFormula) => void): Stats { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - engine._stats.reset() - - const start = Date.now() - func(engine) - const end = Date.now() - - const time = end - start - const actualStats: Stats = engine.getStats() - actualStats.set(EnrichedStatType.CRUDS_TOTAL, time) - - return actualStats -} - -export function statsTreePrint(stats: Stats): void { - const str = - `________________________________________________ -|BUILD_ENGINE_TOTAL: ${stats.get(EnrichedStatType.BUILD_ENGINE_TOTAL) || 0} -| |PREPROCESSING: ${stats.get(EnrichedStatType.PREPROCESSING) || 0} -| | |GRAPH_BUILD: ${stats.get(EnrichedStatType.GRAPH_BUILD) || 0} -| | | |INIT_DATASTRUCTURES: ${stats.get(EnrichedStatType.INIT_DATASTRUCTURES) || 0} -| | | | |BUILD_COLUMN_INDEX: ${stats.get(EnrichedStatType.BUILD_COLUMN_INDEX) || 0} -| | | |PARSER: ${stats.get(EnrichedStatType.PARSER) || 0} -| | |TOP_SORT: ${stats.get(EnrichedStatType.TOP_SORT) || 0} -| |EVALUATION: ${stats.get(EnrichedStatType.EVALUATION) || 0} -| | |VLOOKUP: ${stats.get(EnrichedStatType.VLOOKUP) || 0} -________________________________________________\n` - console.log(str) -} - -export function statsTreePrintCruds(stats: Stats): void { - const str = - `|CRUDS: ${stats.get(EnrichedStatType.CRUDS_TOTAL) || 0} -| |TRANSFORM_ASTS: ${stats.get(EnrichedStatType.TRANSFORM_ASTS) || 0} -| |TRANSFORM_ASTS_POSTPONED: ${stats.get(EnrichedStatType.TRANSFORM_ASTS_POSTPONED) || 0} -| |ADJUSTING_ADDRESS_MAPPING: ${stats.get(EnrichedStatType.ADJUSTING_ADDRESS_MAPPING) || 0} -| |ADJUSTING_MATRIX_MAPPING: ${stats.get(EnrichedStatType.ADJUSTING_ARRAY_MAPPING) || 0} -| |ADJUSTING_RANGES: ${stats.get(EnrichedStatType.ADJUSTING_RANGES) || 0} -| |ADJUSTING_GRAPH: ${stats.get(EnrichedStatType.ADJUSTING_GRAPH) || 0} -| |EVALUATION: ${stats.get(EnrichedStatType.EVALUATION) || 0} -| | |VLOOKUP: ${stats.get(EnrichedStatType.VLOOKUP) || 0} -________________________________________________\n` - console.log(str) -} - -export function reduceStats(stats: Stats[], fn: (_: number[]) => number): Stats { - const averages = new Map() - - for (const key of stats[0].keys()) { - const avg = fn(stats.map((stats) => stats.get(key) || 0)) - averages.set(key, avg) - } - - return averages -} - -export function average(values: number[]): number { - const sum = values.reduce((sum, value) => { - return sum + value - }, 0) - return sum / values.length -} diff --git a/test/performance/utils/utils.ts b/test/performance/utils/utils.ts deleted file mode 100644 index 2bee180ff7..0000000000 --- a/test/performance/utils/utils.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {SimpleCellAddress} from '../../../src' - -export const adr = (stringAddress: string, sheet: number = 0): SimpleCellAddress => { - const result = /^(\$([A-Za-z0-9_]+)\.)?(\$?)([A-Za-z]+)(\$?)([0-9]+)$/.exec(stringAddress)! - const row = Number(result[6]) - 1 - return {sheet: sheet, col: colNumber(result[4]), row: row} -} - -const colNumber = (input: string): number => { - if (input.length === 1) { - return input.toUpperCase().charCodeAt(0) - 65 - } else { - return input.split('').reduce((currentColumn, nextLetter) => { - return currentColumn * 26 + (nextLetter.toUpperCase().charCodeAt(0) - 64) - }, 0) - 1 - } -} diff --git a/test/performance/write-to-file.ts b/test/performance/write-to-file.ts deleted file mode 100644 index 103f1d8e7f..0000000000 --- a/test/performance/write-to-file.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { runBasicBenchmark } from './basic-benchmark' -import { runCrudsBenchmark } from './cruds-benchmark' -import fs = require('fs') - -(() => { - const filename = process.argv[2] - - if (!filename) { - console.log('Usage:\n$ npm run benchmark:write-to-file path/to/file.json') - return - } - - const basicResult = runBasicBenchmark() - const crudsResult = runCrudsBenchmark() - const allBenchmarksResult = [ ...basicResult, ...crudsResult ].map(e => ({ - name: e.name, - totalTime: e.totalTime - })) - - try { - fs.writeFileSync(filename, JSON.stringify(allBenchmarksResult)) - } catch (err) { - console.error(err) - } -})() diff --git a/test/unit/AbsoluteCellRange.spec.ts b/test/unit/AbsoluteCellRange.spec.ts deleted file mode 100644 index e698b237af..0000000000 --- a/test/unit/AbsoluteCellRange.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ -import {AbsoluteCellRange, AbsoluteColumnRange, AbsoluteRowRange} from '../../src/AbsoluteCellRange' -import {adr} from './testUtils' -import {SheetsNotEqual} from '../../src/errors' - -describe('AbsoluteCellRange', () => { - describe('#addressInRange', () => { - it('true in simplest case', () => { - const range = AbsoluteCellRange.fromCoordinates(0, 0, 0, 0, 0) - - expect(range.addressInRange(adr('A1'))).toBe(true) - }) - - it('false if different sheets', () => { - const range = AbsoluteCellRange.fromCoordinates(1, 0, 0, 0, 0) - - expect(range.addressInRange(adr('A1'))).toBe(false) - }) - }) - - describe('construct', () => { - it('start should be copied when using static method', () => { - const start = adr('A1') - const range = AbsoluteCellRange.spanFrom(start, 1, 1) - - expect(start).not.toBe(range.start) - }) - - it('ends should be copied whe using constructor', () => { - const start = adr('A1') - const end = adr('B2') - - const range = new AbsoluteCellRange(start, end) - - expect(start).not.toBe(range.start) - expect(end).not.toBe(range.end) - }) - - describe('fromSimpleCellAddresses()', () => { - it('constructs an AbsoluteCellRange when all the coordinates are finite', () => { - const start = { sheet: 0, row: 42, col: 42 } - const end = { sheet: 0, row: 666, col: 666 } - const range = AbsoluteCellRange.fromSimpleCellAddresses(start, end) - - expect(range.start).toEqual(start) - expect(range.end).toEqual(end) - expect(range).toBeInstanceOf(AbsoluteCellRange) - expect(range).not.toBeInstanceOf(AbsoluteColumnRange) - expect(range).not.toBeInstanceOf(AbsoluteRowRange) - }) - - it('constructs an AbsoluteColumnRange when called with end.row = Infinity', () => { - const start = { sheet: 0, row: 0, col: 42 } - const end = { sheet: 0, row: Infinity, col: 43 } - const range = AbsoluteCellRange.fromSimpleCellAddresses(start, end) - - expect(range.start).toEqual(start) - expect(range.end).toEqual(end) - expect(range).toBeInstanceOf(AbsoluteColumnRange) - }) - - it('constructs an AbsoluteRowRange when called with end.col = Infinity', () => { - const start = { sheet: 0, row: 42, col: 0 } - const end = { sheet: 0, row: 43, col: Infinity } - const range = AbsoluteCellRange.fromSimpleCellAddresses(start, end) - - expect(range.start).toEqual(start) - expect(range.end).toEqual(end) - expect(range).toBeInstanceOf(AbsoluteRowRange) - }) - - it('constructs an AbsoluteColumnRange when called with end.row = Infinity, and end.col = Infinity', () => { - const start = { sheet: 0, row: 0, col: 0 } - const end = { sheet: 0, row: Infinity, col: Infinity } - const range = AbsoluteCellRange.fromSimpleCellAddresses(start, end) - - expect(range.start).toEqual(start) - expect(range.end).toEqual(end) - expect(range).toBeInstanceOf(AbsoluteColumnRange) - }) - - it('throws error when trying to construct a cell range with different sheets', () => { - const start = { sheet: 1, row: 42, col: 42 } - const end = { sheet: 2, row: 43, col: 43 } - - expect(() => AbsoluteCellRange.fromSimpleCellAddresses(start, end)).toThrow(new SheetsNotEqual(start.sheet, end.sheet)) - }) - - it('throws error when trying to construct a column range with different sheets', () => { - const start = { sheet: 1, row: 0, col: 42 } - const end = { sheet: 2, row: Infinity, col: 43 } - - expect(() => AbsoluteCellRange.fromSimpleCellAddresses(start, end)).toThrow(new SheetsNotEqual(start.sheet, end.sheet)) - }) - - it('throws error when trying to construct a row range with different sheets', () => { - const start = { sheet: 1, row: 42, col: 0 } - const end = { sheet: 2, row: 43, col: Infinity } - - expect(() => AbsoluteCellRange.fromSimpleCellAddresses(start, end)).toThrow(new SheetsNotEqual(start.sheet, end.sheet)) - }) - }) - }) -}) diff --git a/test/unit/CellContentParser.spec.ts b/test/unit/CellContentParser.spec.ts deleted file mode 100644 index 12c579e2f2..0000000000 --- a/test/unit/CellContentParser.spec.ts +++ /dev/null @@ -1,161 +0,0 @@ -import {ErrorType} from '../../src/Cell' -import {CellContent, CellContentParser} from '../../src/CellContentParser' -import {Config} from '../../src/Config' -import {DateTimeHelper} from '../../src/DateTimeHelper' -import {ErrorMessage} from '../../src/error-message' -import { - CurrencyNumber, - DateNumber, - DateTimeNumber, - PercentNumber, - TimeNumber -} from '../../src/interpreter/InterpreterValue' -import {NumberLiteralHelper} from '../../src/NumberLiteralHelper' - -describe('CellContentParser', () => { - const config = new Config() - const cellContentParser = new CellContentParser(config, new DateTimeHelper(config), new NumberLiteralHelper(config)) - - it('a formula', () => { - expect(cellContentParser.parse('=FOO()')).toEqual(new CellContent.Formula('=FOO()')) - }) - - it('null is empty value', () => { - expect(cellContentParser.parse(null)).toEqual(new CellContent.Empty()) - }) - - it('undefined is empty value', () => { - expect(cellContentParser.parse(undefined)).toEqual(new CellContent.Empty()) - }) - - it('numbers', () => { - expect(cellContentParser.parse('42')).toEqual(new CellContent.Number(42)) - expect(cellContentParser.parse('+42')).toEqual(new CellContent.Number(42)) - expect(cellContentParser.parse(' 42')).toEqual(new CellContent.Number(42)) - expect(cellContentParser.parse('42 ')).toEqual(new CellContent.Number(42)) - expect(cellContentParser.parse('42.13')).toEqual(new CellContent.Number(42.13)) - expect(cellContentParser.parse('-42.13')).toEqual(new CellContent.Number(-42.13)) - expect(cellContentParser.parse('+42.13')).toEqual(new CellContent.Number(42.13)) - expect(cellContentParser.parse('.13')).toEqual(new CellContent.Number(0.13)) - }) - - it('boolean', () => { - expect(cellContentParser.parse('true')).toEqual(new CellContent.Boolean(true)) - expect(cellContentParser.parse('false')).toEqual(new CellContent.Boolean(false)) - expect(cellContentParser.parse('TRUE')).toEqual(new CellContent.Boolean(true)) - expect(cellContentParser.parse('FALSE')).toEqual(new CellContent.Boolean(false)) - expect(cellContentParser.parse('TrUe')).toEqual(new CellContent.Boolean(true)) - expect(cellContentParser.parse('faLSE')).toEqual(new CellContent.Boolean(false)) - }) - - it('numbers with different decimal separators', () => { - const config = new Config({decimalSeparator: ',', functionArgSeparator: ';'}) - const cellContentParser = new CellContentParser(config, new DateTimeHelper(config), new NumberLiteralHelper(config)) - - expect(cellContentParser.parse('42')).toEqual(new CellContent.Number(42)) - expect(cellContentParser.parse('42,13')).toEqual(new CellContent.Number(42.13)) - expect(cellContentParser.parse('-42,13')).toEqual(new CellContent.Number(-42.13)) - expect(cellContentParser.parse('+42,13')).toEqual(new CellContent.Number(42.13)) - expect(cellContentParser.parse(',13')).toEqual(new CellContent.Number(0.13)) - expect(cellContentParser.parse('42.13')).toEqual(new CellContent.String('42.13')) - expect(cellContentParser.parse('12,34,56')).toEqual(new CellContent.String('12,34,56')) - }) - - it('numbers with thousand separators', () => { - const config = new Config({thousandSeparator: ' ', functionArgSeparator: ';'}) - const cellContentParser = new CellContentParser(config, new DateTimeHelper(config), new NumberLiteralHelper(config)) - - expect(cellContentParser.parse('42')).toEqual(new CellContent.Number(42)) - expect(cellContentParser.parse('1234567')).toEqual(new CellContent.Number(1234567)) - expect(cellContentParser.parse('1 234 567')).toEqual(new CellContent.Number(1234567)) - expect(cellContentParser.parse('-1 234 567')).toEqual(new CellContent.Number(-1234567)) - expect(cellContentParser.parse('1234 567')).toEqual(new CellContent.Number(1234567)) - expect(cellContentParser.parse('12 3456 789')).toEqual(new CellContent.Number(123456789)) - expect(cellContentParser.parse('1 234 567.12')).toEqual(new CellContent.Number(1234567.12)) - expect(cellContentParser.parse('12 34 56 7')).toEqual(new CellContent.String('12 34 56 7')) - expect(cellContentParser.parse('1 234.12.12')).toEqual(new CellContent.String('1 234.12.12')) - }) - - it('non-string', () => { - expect(cellContentParser.parse(42)).toEqual(new CellContent.Number(42)) - expect(cellContentParser.parse(true)).toEqual(new CellContent.Boolean(true)) - expect(cellContentParser.parse(null)).toEqual(new CellContent.Empty()) - expect(cellContentParser.parse(undefined)).toEqual(new CellContent.Empty()) - expect(cellContentParser.parse(-0)).toEqual(new CellContent.Number(0)) - expect(cellContentParser.parse(Infinity)).toEqual(new CellContent.Error(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(cellContentParser.parse(-Infinity)).toEqual(new CellContent.Error(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(cellContentParser.parse(NaN)).toEqual(new CellContent.Error(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('string', () => { - expect(cellContentParser.parse('f42')).toEqual(new CellContent.String('f42')) - expect(cellContentParser.parse('42f')).toEqual(new CellContent.String('42f')) - expect(cellContentParser.parse(' =FOO()')).toEqual(new CellContent.String(' =FOO()')) - expect(cellContentParser.parse(' ')).toEqual(new CellContent.String(' ')) - expect(cellContentParser.parse('')).toEqual(new CellContent.String('')) - }) - - it('errors', () => { - expect(cellContentParser.parse('#DIV/0!')).toEqual(new CellContent.Error(ErrorType.DIV_BY_ZERO)) - expect(cellContentParser.parse('#NUM!')).toEqual(new CellContent.Error(ErrorType.NUM)) - expect(cellContentParser.parse('#N/A')).toEqual(new CellContent.Error(ErrorType.NA)) - expect(cellContentParser.parse('#VALUE!')).toEqual(new CellContent.Error(ErrorType.VALUE)) - expect(cellContentParser.parse('#CYCLE!')).toEqual(new CellContent.Error(ErrorType.CYCLE)) - expect(cellContentParser.parse('#NAME?')).toEqual(new CellContent.Error(ErrorType.NAME)) - expect(cellContentParser.parse('#REF!')).toEqual(new CellContent.Error(ErrorType.REF)) - }) - - it('errors are case insensitive', () => { - expect(cellContentParser.parse('#dIv/0!')).toEqual(new CellContent.Error(ErrorType.DIV_BY_ZERO)) - }) - - it('error-like literal is string', () => { - expect(cellContentParser.parse('#FOO!')).toEqual(new CellContent.String('#FOO!')) - }) - - it('date parsing', () => { - expect(cellContentParser.parse('02-02-2020')).toEqual(new CellContent.Number(new DateNumber(43863, 'DD/MM/YYYY'))) - expect(cellContentParser.parse(' 02-02-2020')).toEqual(new CellContent.Number(new DateNumber(43863, 'DD/MM/YYYY'))) - }) - - it('JS Date parsing', () => { - expect(cellContentParser.parse(new Date(1995, 11, 17))).toEqual(new CellContent.Number(new DateNumber(35050, 'Date()'))) - expect(cellContentParser.parse(new Date(1995, 11, 17, 12, 0, 0))).toEqual(new CellContent.Number(new DateTimeNumber(35050.5, 'Date()'))) - expect(cellContentParser.parse(new Date(1899, 11, 30, 6, 0, 0))).toEqual(new CellContent.Number(new TimeNumber(0.25, 'Date()'))) - expect(cellContentParser.parse(new Date(1899, 11, 29))).toEqual(new CellContent.Error(ErrorType.NUM, ErrorMessage.DateBounds)) - }) - - it('starts with \'', () => { - expect(cellContentParser.parse('\'123')).toEqual(new CellContent.String('123')) - expect(cellContentParser.parse('\'=1+1')).toEqual(new CellContent.String('=1+1')) - expect(cellContentParser.parse('\'\'1')).toEqual(new CellContent.String('\'1')) - expect(cellContentParser.parse('\' 1')).toEqual(new CellContent.String(' 1')) - expect(cellContentParser.parse(' \'1')).toEqual(new CellContent.String(' \'1')) - expect(cellContentParser.parse('\'02-02-2020')).toEqual(new CellContent.String('02-02-2020')) - }) - - it('currency parsing', () => { - expect(cellContentParser.parse('1$')).toEqual(new CellContent.Number(new CurrencyNumber(1, '$'))) - expect(cellContentParser.parse('$1')).toEqual(new CellContent.Number(new CurrencyNumber(1, '$'))) - expect(cellContentParser.parse('-1.1e1$')).toEqual(new CellContent.Number(new CurrencyNumber(-11, '$'))) - expect(cellContentParser.parse('$-1.1e1')).toEqual(new CellContent.Number(new CurrencyNumber(-11, '$'))) - expect(cellContentParser.parse(' 1$ ')).toEqual(new CellContent.Number(new CurrencyNumber(1, '$'))) - expect(cellContentParser.parse(' $1 ')).toEqual(new CellContent.Number(new CurrencyNumber(1, '$'))) - expect(cellContentParser.parse('1 $')).toEqual(new CellContent.String('1 $')) - expect(cellContentParser.parse('$ 1')).toEqual(new CellContent.String('$ 1')) - }) - - it('other currency parsing', () => { - expect(cellContentParser.parse('1PLN')).toEqual(new CellContent.String('1PLN')) - const configPLN = new Config({currencySymbol: ['PLN']}) - const cellContentParserPLN = new CellContentParser(configPLN, new DateTimeHelper(configPLN), new NumberLiteralHelper(configPLN)) - expect(cellContentParserPLN.parse('1PLN')).toEqual(new CellContent.Number(new CurrencyNumber(1, 'PLN'))) - }) - - it('percentage parsing', () => { - expect(cellContentParser.parse('100%')).toEqual(new CellContent.Number(new PercentNumber(1))) - expect(cellContentParser.parse('-1.1e3%')).toEqual(new CellContent.Number(new PercentNumber(-11))) - expect(cellContentParser.parse(' 100% ')).toEqual(new CellContent.Number(new PercentNumber(1))) - expect(cellContentParser.parse('100 %')).toEqual(new CellContent.String('100 %')) - }) -}) diff --git a/test/unit/CellValueExporter.spec.ts b/test/unit/CellValueExporter.spec.ts deleted file mode 100644 index ca1daf8cdb..0000000000 --- a/test/unit/CellValueExporter.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -import {DetailedCellError, ErrorType, HyperFormula} from '../../src' -import {CellError} from '../../src/Cell' -import {Config} from '../../src/Config' -import { SheetMapping } from '../../src/DependencyGraph' -import {ErrorMessage} from '../../src/error-message' -import {Exporter} from '../../src/Exporter' -import {plPL} from '../../src/i18n/languages' -import {EmptyValue} from '../../src/interpreter/InterpreterValue' -import {LazilyTransformingAstService} from '../../src/LazilyTransformingAstService' -import {NamedExpressions} from '../../src/NamedExpressions' -import {EmptyStatistics} from '../../src/statistics' -import {detailedError} from './testUtils' - -const namedExpressionsMock = {} as NamedExpressions -const sheetMappingMock = {} as SheetMapping -const lazilyTransforminService = new LazilyTransformingAstService(new EmptyStatistics()) - -describe('rounding', () => { - it('no rounding', () => { - const config = new Config({smartRounding: false}) - const cellValueExporter = new Exporter(config, namedExpressionsMock, sheetMappingMock, lazilyTransforminService) - expect(cellValueExporter.exportValue(1.000000000000001)).toBe(1.000000000000001) - expect(cellValueExporter.exportValue(-1.000000000000001)).toBe(-1.000000000000001) - expect(cellValueExporter.exportValue(0.000000000000001)).toBe(0.000000000000001) - expect(cellValueExporter.exportValue(-0.000000000000001)).toBe(-0.000000000000001) - expect(cellValueExporter.exportValue(true)).toBe(true) - expect(cellValueExporter.exportValue(false)).toBe(false) - expect(cellValueExporter.exportValue(1)).toBe(1) - expect(cellValueExporter.exportValue(EmptyValue)).toBe(null) - expect(cellValueExporter.exportValue('abcd')).toBe('abcd') - }) - - it('with rounding', () => { - const config = new Config() - const cellValueExporter = new Exporter(config, namedExpressionsMock, sheetMappingMock, lazilyTransforminService) - expect(cellValueExporter.exportValue(1.000000001)).toBe(1.000000001) - expect(cellValueExporter.exportValue(-1.000000001)).toBe(-1.000000001) - expect(cellValueExporter.exportValue(1.00000000001)).toBe(1) - expect(cellValueExporter.exportValue(-1.00000000001)).toBe(-1) - expect(cellValueExporter.exportValue(0.0000000000001)).toBeCloseTo(0.0000000000001, 13) - expect(cellValueExporter.exportValue(-0.0000000000001)).toBeCloseTo(-0.0000000000001, 13) - expect(cellValueExporter.exportValue(true)).toBe(true) - expect(cellValueExporter.exportValue(false)).toBe(false) - expect(cellValueExporter.exportValue(1)).toBe(1) - expect(cellValueExporter.exportValue(EmptyValue)).toBe(null) - expect(cellValueExporter.exportValue('abcd')).toBe('abcd') - }) -}) - -describe('detailed error', () => { - it('should return detailed errors', () => { - const config = new Config({language: 'enGB'}) - const cellValueExporter = new Exporter(config, namedExpressionsMock, sheetMappingMock, lazilyTransforminService) - - const error = cellValueExporter.exportValue(new CellError(ErrorType.VALUE)) as DetailedCellError - expect(error).toEqualError(detailedError(ErrorType.VALUE)) - expect(error.value).toEqual('#VALUE!') - }) - - it('should return detailed errors with translation', () => { - HyperFormula.registerLanguage('plPL', plPL) - const config = new Config({language: 'plPL'}) - const cellValueExporter = new Exporter(config, namedExpressionsMock, sheetMappingMock, lazilyTransforminService) - - const error = cellValueExporter.exportValue(new CellError(ErrorType.VALUE)) as DetailedCellError - expect(error).toEqualError(detailedError(ErrorType.VALUE, undefined, config)) - expect(error.value).toEqual('#ARG!') - }) - - it('pretty print', () => { - expect(`${detailedError(ErrorType.REF, ErrorMessage.DateBounds)}`).toEqual('#REF!') - }) -}) diff --git a/test/unit/ColumnsSpan.spec.ts b/test/unit/ColumnsSpan.spec.ts deleted file mode 100644 index f6db13a947..0000000000 --- a/test/unit/ColumnsSpan.spec.ts +++ /dev/null @@ -1,102 +0,0 @@ -import {ColumnsSpan} from '../../src/Span' - -describe('ColumnsSpan', () => { - it('raise error when starting column is less than 0', () => { - expect(() => { - new ColumnsSpan(0, -1, 0) - }).toThrowError('Starting column cant be less than 0') - }) - - it('raise error when column end before column start', () => { - expect(() => { - new ColumnsSpan(0, 1, 0) - }).toThrowError('Column span cant end before start') - }) - - it('#fromNumberOfColumns', () => { - const span = ColumnsSpan.fromNumberOfColumns(0, 42, 2) - - expect(span).toEqual(new ColumnsSpan(0, 42, 43)) - }) - - it('#numberOfColumns for one column', () => { - const span = new ColumnsSpan(0, 42, 42) - - expect(span.numberOfColumns).toBe(1) - }) - - it('#numberOfColumns for more than one column', () => { - const span = new ColumnsSpan(0, 42, 45) - - expect(span.numberOfColumns).toBe(4) - }) - - it('#columns iterates over column numbers', () => { - const span = new ColumnsSpan(0, 42, 45) - - expect(Array.from(span.columns())).toEqual([42, 43, 44, 45]) - }) - - it('#intersect with span from other sheet', () => { - const span1 = new ColumnsSpan(0, 42, 45) - const span2 = new ColumnsSpan(1, 43, 45) - - expect(() => { - span1.intersect(span2) - }).toThrowError("Can't intersect spans from different sheets") - }) - - it('#intersect with span overlapping at right', () => { - const span1 = new ColumnsSpan(0, 42, 45) - const span2 = new ColumnsSpan(0, 43, 46) - - expect(span1.intersect(span2)).toEqual(new ColumnsSpan(0, 43, 45)) - }) - - it('#intersect with span whole after', () => { - const span1 = new ColumnsSpan(0, 42, 45) - const span2 = new ColumnsSpan(0, 46, 49) - - expect(span1.intersect(span2)).toBe(null) - }) - - it('#intersect with span overlapping at left', () => { - const span1 = new ColumnsSpan(0, 42, 45) - const span2 = new ColumnsSpan(0, 40, 44) - - expect(span1.intersect(span2)).toEqual(new ColumnsSpan(0, 42, 44)) - }) - - it('#intersect with span whole before', () => { - const span1 = new ColumnsSpan(0, 42, 45) - const span2 = new ColumnsSpan(0, 39, 41) - - expect(span1.intersect(span2)).toBe(null) - }) - - it('#intersect with span included', () => { - const span1 = new ColumnsSpan(0, 42, 45) - const span2 = new ColumnsSpan(0, 43, 44) - - expect(span1.intersect(span2)).toEqual(span2) - }) - - it('#intersect with span outside', () => { - const span1 = new ColumnsSpan(0, 42, 45) - const span2 = new ColumnsSpan(0, 40, 47) - - expect(span1.intersect(span2)).toEqual(span1) - }) - - it('#firstColumn when one column', () => { - const span1 = new ColumnsSpan(0, 42, 42) - - expect(span1.firstColumn()).toEqual(span1) - }) - - it('#firstColumn when more columns', () => { - const span1 = new ColumnsSpan(0, 42, 44) - - expect(span1.firstColumn()).toEqual(new ColumnsSpan(0, 42, 42)) - }) -}) diff --git a/test/unit/RowsSpan.spec.ts b/test/unit/RowsSpan.spec.ts deleted file mode 100644 index 881fdd06b6..0000000000 --- a/test/unit/RowsSpan.spec.ts +++ /dev/null @@ -1,102 +0,0 @@ -import {RowsSpan} from '../../src/Span' - -describe('RowsSpan', () => { - it('raise error when starting row is less than 0', () => { - expect(() => { - new RowsSpan(0, -1, 0) - }).toThrowError('Starting row cant be less than 0') - }) - - it('raise error when row end before row start', () => { - expect(() => { - new RowsSpan(0, 1, 0) - }).toThrowError('Row span cant end before start') - }) - - it('#fromNumberOfRows', () => { - const span = RowsSpan.fromNumberOfRows(0, 42, 2) - - expect(span).toEqual(new RowsSpan(0, 42, 43)) - }) - - it('#numberOfRows for one row', () => { - const span = new RowsSpan(0, 42, 42) - - expect(span.numberOfRows).toBe(1) - }) - - it('#numberOfRows for more than one row', () => { - const span = new RowsSpan(0, 42, 45) - - expect(span.numberOfRows).toBe(4) - }) - - it('#rows iterates over row numbers', () => { - const span = new RowsSpan(0, 42, 45) - - expect(Array.from(span.rows())).toEqual([42, 43, 44, 45]) - }) - - it('#intersect with span from other sheet', () => { - const span1 = new RowsSpan(0, 42, 45) - const span2 = new RowsSpan(1, 43, 45) - - expect(() => { - span1.intersect(span2) - }).toThrowError("Can't intersect spans from different sheets") - }) - - it('#intersect with span overlapping at right', () => { - const span1 = new RowsSpan(0, 42, 45) - const span2 = new RowsSpan(0, 43, 46) - - expect(span1.intersect(span2)).toEqual(new RowsSpan(0, 43, 45)) - }) - - it('#intersect with span whole after', () => { - const span1 = new RowsSpan(0, 42, 45) - const span2 = new RowsSpan(0, 46, 49) - - expect(span1.intersect(span2)).toBe(null) - }) - - it('#intersect with span overlapping at left', () => { - const span1 = new RowsSpan(0, 42, 45) - const span2 = new RowsSpan(0, 40, 44) - - expect(span1.intersect(span2)).toEqual(new RowsSpan(0, 42, 44)) - }) - - it('#intersect with span whole before', () => { - const span1 = new RowsSpan(0, 42, 45) - const span2 = new RowsSpan(0, 39, 41) - - expect(span1.intersect(span2)).toBe(null) - }) - - it('#intersect with span included', () => { - const span1 = new RowsSpan(0, 42, 45) - const span2 = new RowsSpan(0, 43, 44) - - expect(span1.intersect(span2)).toEqual(span2) - }) - - it('#intersect with span outside', () => { - const span1 = new RowsSpan(0, 42, 45) - const span2 = new RowsSpan(0, 40, 47) - - expect(span1.intersect(span2)).toEqual(span1) - }) - - it('#firstRow when one row', () => { - const span1 = new RowsSpan(0, 42, 42) - - expect(span1.firstRow()).toEqual(span1) - }) - - it('#firstRow when more rows', () => { - const span1 = new RowsSpan(0, 42, 44) - - expect(span1.firstRow()).toEqual(new RowsSpan(0, 42, 42)) - }) -}) diff --git a/test/unit/_setupFiles/babel.js b/test/unit/_setupFiles/babel.js deleted file mode 100644 index 8e8cbfbbde..0000000000 --- a/test/unit/_setupFiles/babel.js +++ /dev/null @@ -1,3 +0,0 @@ -require('@babel/register')({ - 'extensions': ['.js', '.ts'] -}); diff --git a/test/unit/_setupFiles/bootstrap.ts b/test/unit/_setupFiles/bootstrap.ts deleted file mode 100644 index 572cd7a5be..0000000000 --- a/test/unit/_setupFiles/bootstrap.ts +++ /dev/null @@ -1,59 +0,0 @@ -/** - * This script file presents you the opportunity of running some code immediately - * after the test framework has been installed in the environment. - */ -import {HyperFormula} from '../../../src' -import {Config} from '../../../src/Config' -import {enGB} from '../../../src/i18n/languages' -import * as plugins from '../../../src/interpreter/plugin' -import {unregisterAllLanguages} from '../testUtils' -import {toContainEqualMatcher, toEqualErrorMatcher, toMatchObjectMatcher} from './matchers' - -Config.defaultConfig = Object.assign({}, Config.defaultConfig, { - functionPlugins: [], - useStats: true, - licenseKey: 'gpl-v3', -}) - -const jestPresent = (() => { - try { - expect([{a: 0}]).toContainEqual({a: 0}) - return true - } catch (e) { - return false - } -})() - -beforeEach(() => { - if (!jestPresent) { - (jasmine as any).setDefaultSpyStrategy((and: any) => and.callThrough()) - } - - unregisterAllLanguages() - - const defaultLanguage = Config.defaultConfig.language - - HyperFormula.registerLanguage(defaultLanguage, enGB) - - HyperFormula.unregisterAllFunctions() - - for (const pluginName of Object.getOwnPropertyNames(plugins)) { - if (!pluginName.startsWith('_')) { - HyperFormula.registerFunctionPlugin(plugins[pluginName as keyof typeof plugins]) - } - } -}) - -beforeAll(() => { - if (!jestPresent) { - (jasmine as any).addMatchers({ - ...toContainEqualMatcher, - ...toMatchObjectMatcher, - ...toEqualErrorMatcher, - }) - } else { - if (global.gc) { - global.gc() - } - } -}) diff --git a/test/unit/_setupFiles/globalSetup.ts b/test/unit/_setupFiles/globalSetup.ts deleted file mode 100644 index 792f64c289..0000000000 --- a/test/unit/_setupFiles/globalSetup.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * This script file presents you the opportunity of running some code immediately - * before the test framework has been installed in the environment. - */ -// eslint-disable-next-line @typescript-eslint/no-var-requires -const htConfig = require('./../../../ht.config') - -export default function() { - // Extract all HF constants to the process environment namespance. - // So the HT_RELEASE_DATE consts and other will be accessible while - // testing the lib. - Object.keys(htConfig).forEach((key) => { - process.env[key] = htConfig[key] - }) -} diff --git a/test/unit/_setupFiles/jest/bootstrap.ts b/test/unit/_setupFiles/jest/bootstrap.ts deleted file mode 100644 index f8f8f9d29a..0000000000 --- a/test/unit/_setupFiles/jest/bootstrap.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {toEqualError} from './toEqualError' - -beforeAll(() => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - // eslint-disable-next-line no-global-assign - spyOn = jest.spyOn - expect.extend(toEqualError) -}) diff --git a/test/unit/_setupFiles/jest/toEqualError.ts b/test/unit/_setupFiles/jest/toEqualError.ts deleted file mode 100644 index 80f274cfc2..0000000000 --- a/test/unit/_setupFiles/jest/toEqualError.ts +++ /dev/null @@ -1,29 +0,0 @@ -type CustomMatcherResult = jest.CustomMatcherResult -type ExpectExtendMap = jest.ExpectExtendMap - -declare global { - namespace jest { - interface Matchers { - toEqualError(expected: any): CustomMatcherResult, - } - } -} - -export const toEqualError: ExpectExtendMap = { - toEqualError(received: any, expected: any): CustomMatcherResult { - let result = false - - if (typeof received === 'object' && typeof expected === 'object' && received.message != null && expected.message != null && received.message.includes(expected.message)) { - result = this.equals( - {...received, message: undefined, root: undefined, address: undefined}, - {...expected, message: undefined, root: undefined, address: undefined} - ) - } else { - result = this.equals(received, expected) - } - return { - pass: result, - message: () => (result ? '' : `Expected ${JSON.stringify(received, null, 2)} to match ${JSON.stringify(expected, null, 2)}.`) - } - } -} diff --git a/test/unit/_setupFiles/jsdom.js b/test/unit/_setupFiles/jsdom.js deleted file mode 100644 index 5ab4d33496..0000000000 --- a/test/unit/_setupFiles/jsdom.js +++ /dev/null @@ -1,7 +0,0 @@ -import {JSDOM} from 'jsdom' - -const dom = new JSDOM(''); - -global.document = dom.window.document; -global.window = dom.window; -global.navigator = dom.window.navigator; diff --git a/test/unit/_setupFiles/matchers/index.ts b/test/unit/_setupFiles/matchers/index.ts deleted file mode 100644 index 4435383da1..0000000000 --- a/test/unit/_setupFiles/matchers/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export {toContainEqualMatcher} from './toContainEqual' -export {toMatchObjectMatcher} from './toMatchObject' -export {toEqualErrorMatcher} from './toEqualError' diff --git a/test/unit/_setupFiles/matchers/toContainEqual.ts b/test/unit/_setupFiles/matchers/toContainEqual.ts deleted file mode 100644 index 7277bb9226..0000000000 --- a/test/unit/_setupFiles/matchers/toContainEqual.ts +++ /dev/null @@ -1,25 +0,0 @@ -type CustomMatcher = jasmine.CustomMatcher -type CustomMatcherFactories = jasmine.CustomMatcherFactories -type CustomMatcherResult = jasmine.CustomMatcherResult -type MatchersUtil = jasmine.MatchersUtil - -declare global { - namespace jasmine { - interface Matchers { - toContainEqual(expected: object, expectationFailOutput?: string): boolean, - } - } -} - -export const toContainEqualMatcher: CustomMatcherFactories = { - toContainEqual: function(util: MatchersUtil): CustomMatcher { - return { - compare: function(actual: any[], expected: any): CustomMatcherResult { - return { - pass: util.equals(actual, jasmine.arrayContaining([expected])), - message: '' - } - }, - } - } -} diff --git a/test/unit/_setupFiles/matchers/toEqualError.ts b/test/unit/_setupFiles/matchers/toEqualError.ts deleted file mode 100644 index 6c0b87bb56..0000000000 --- a/test/unit/_setupFiles/matchers/toEqualError.ts +++ /dev/null @@ -1,34 +0,0 @@ -type CustomMatcher = jasmine.CustomMatcher -type CustomMatcherFactories = jasmine.CustomMatcherFactories -type CustomMatcherResult = jasmine.CustomMatcherResult -type MatchersUtil = jasmine.MatchersUtil - -declare global { - namespace jasmine { - interface Matchers { - toEqualError(expected: any, expectationFailOutput?: string): boolean, - } - } -} - -export const toEqualErrorMatcher: CustomMatcherFactories = { - toEqualError: function(util: MatchersUtil): CustomMatcher { - return { - compare: function(received: any, expected: any): CustomMatcherResult { - let result - if (typeof received === 'object' && typeof expected === 'object' && received.message != null && expected.message != null && received.message.includes(expected.message)) { - result = util.equals( - {...received, message: undefined, root: undefined, address: undefined}, - {...expected, message: undefined, root: undefined, address: undefined} - ) - } else { - result = util.equals(received, expected) - } - return { - pass: result, - message: result ? '' : `Expected ${JSON.stringify(received, null, 2)} to match ${JSON.stringify(expected, null, 2)}.` - } - }, - } - } -} diff --git a/test/unit/_setupFiles/matchers/toMatchObject.ts b/test/unit/_setupFiles/matchers/toMatchObject.ts deleted file mode 100644 index c8353988ca..0000000000 --- a/test/unit/_setupFiles/matchers/toMatchObject.ts +++ /dev/null @@ -1,25 +0,0 @@ -type CustomMatcher = jasmine.CustomMatcher -type CustomMatcherFactories = jasmine.CustomMatcherFactories -type CustomMatcherResult = jasmine.CustomMatcherResult -type MatchersUtil = jasmine.MatchersUtil - -declare global { - namespace jasmine { - interface Matchers { - toMatchObject(expected: object, expectationFailOutput?: string): boolean, - } - } -} - -export const toMatchObjectMatcher: CustomMatcherFactories = { - toMatchObject: function(util: MatchersUtil): CustomMatcher { - return { - compare: function(actual: never, expected: never): CustomMatcherResult { - return { - pass: util.equals(actual, jasmine.objectContaining(expected)), - message: '' - } - }, - } - } -} diff --git a/test/unit/address-mapping.spec.ts b/test/unit/address-mapping.spec.ts deleted file mode 100644 index 496c714d5d..0000000000 --- a/test/unit/address-mapping.spec.ts +++ /dev/null @@ -1,714 +0,0 @@ -import {AddressMapping, DenseStrategy, EmptyCellVertex, SparseStrategy, ValueCellVertex} from '../../src/DependencyGraph' -import { - AlwaysDense, - AlwaysSparse, - DenseSparseChooseBasedOnThreshold -} from '../../src/DependencyGraph/AddressMapping/ChooseAddressMappingPolicy' -import {findBoundaries} from '../../src/Sheet' -import {ColumnsSpan, RowsSpan} from '../../src/Span' -import {adr} from './testUtils' - -const sharedExamples = (builder: (width: number, height: number) => AddressMapping) => { - it('simple set', () => { - const mapping = builder(1, 1) - const vertex = new ValueCellVertex(42, 42) - const address = adr('A1') - - mapping.setCell(address, vertex) - - expect(mapping.getCell(address)).toBe(vertex) - }) - - it('set and using different reference when get', () => { - const mapping = builder(1, 1) - const vertex = new ValueCellVertex(42, 42) - - mapping.setCell(adr('A1'), vertex) - - expect(mapping.getCell(adr('A1'))).toBe(vertex) - }) - - it("get when there's even no column", () => { - const mapping = builder(1, 1) - - expect(mapping.getCell(adr('A1'))).toBe(undefined) - }) - - it('get when there was already something in that column', () => { - const mapping = builder(1, 2) - - mapping.setCell(adr('A2'), new ValueCellVertex(42, 42)) - - expect(mapping.getCell(adr('A1'))).toBe(undefined) - }) - - it('get when asking for out of the row bound cell', () => { - const mapping = builder(1, 1) - - expect(mapping.getCell(adr('A2'))).toBe(undefined) - }) - - it('get all entries', () => { - const mapping = builder(1, 2) - mapping.setCell(adr('A1', 0), new ValueCellVertex(42, 42)) - mapping.setCell(adr('A2', 0), new ValueCellVertex(43, 43)) - - const results = [] - for (const [simpleCellAddress, cellVertex] of mapping.sheetEntries(0)) { - results.push([ - simpleCellAddress.sheet, - simpleCellAddress.row, - simpleCellAddress.col, - String(cellVertex.getCellValue()), - ]) - } - - expect(results).toEqual([ - [0, 0, 0, String(42)], - [0, 1, 0, String(43)], - ]) - }) - - it('get all entries - from rows span', () => { - const mapping = builder(3, 3) - mapping.setCell(adr('A1', 0), new ValueCellVertex(42, 42)) - mapping.setCell(adr('A2', 0), new ValueCellVertex(43, 43)) - mapping.setCell(adr('A3', 0), new ValueCellVertex(44, 44)) - mapping.setCell(adr('B1', 0), new ValueCellVertex(45, 45)) - mapping.setCell(adr('B2', 0), new ValueCellVertex(46, 46)) - mapping.setCell(adr('B3', 0), new ValueCellVertex(47, 47)) - mapping.setCell(adr('C1', 0), new ValueCellVertex(48, 48)) - mapping.setCell(adr('C2', 0), new ValueCellVertex(49, 49)) - mapping.setCell(adr('C3', 0), new ValueCellVertex(50, 50)) - - const results = [] - for (const [simpleCellAddress, cellVertex] of mapping.entriesFromRowsSpan(new RowsSpan(0, 1, 2))) { - results.push([ - simpleCellAddress.sheet, - simpleCellAddress.row, - simpleCellAddress.col, - String(cellVertex.getCellValue()), - ]) - } - - expect(results).toEqual([ - [0, 1, 0, String(43)], - [0, 2, 0, String(44)], - [0, 1, 1, String(46)], - [0, 2, 1, String(47)], - [0, 1, 2, String(49)], - [0, 2, 2, String(50)], - ]) - }) - - it('get all entries - from columns span', () => { - const mapping = builder(3, 3) - mapping.setCell(adr('A1', 0), new ValueCellVertex(42, 42)) - mapping.setCell(adr('A2', 0), new ValueCellVertex(43, 43)) - mapping.setCell(adr('A3', 0), new ValueCellVertex(44, 44)) - mapping.setCell(adr('B1', 0), new ValueCellVertex(45, 45)) - mapping.setCell(adr('B2', 0), new ValueCellVertex(46, 46)) - mapping.setCell(adr('B3', 0), new ValueCellVertex(47, 47)) - mapping.setCell(adr('C1', 0), new ValueCellVertex(48, 48)) - mapping.setCell(adr('C2', 0), new ValueCellVertex(49, 49)) - mapping.setCell(adr('C3', 0), new ValueCellVertex(50, 50)) - - const results = [] - for (const [simpleCellAddress, cellVertex] of mapping.entriesFromColumnsSpan(new ColumnsSpan(0, 1, 2))) { - results.push([ - simpleCellAddress.sheet, - simpleCellAddress.row, - simpleCellAddress.col, - String(cellVertex.getCellValue()), - ]) - } - - expect(results).toEqual([ - [0, 0, 1, String(45)], - [0, 1, 1, String(46)], - [0, 2, 1, String(47)], - [0, 0, 2, String(48)], - [0, 1, 2, String(49)], - [0, 2, 2, String(50)], - ]) - }) - - it("set when there's already something in that column", () => { - const mapping = builder(1, 2) - const vertex0 = new ValueCellVertex(42, 42) - const vertex1 = new ValueCellVertex(42, 42) - mapping.setCell(adr('A1'), vertex0) - - mapping.setCell(adr('A2'), vertex1) - - expect(mapping.getCell(adr('A1'))).toBe(vertex0) - expect(mapping.getCell(adr('A2'))).toBe(vertex1) - }) - - it('set overrides old value', () => { - const mapping = builder(1, 1) - const vertex0 = new ValueCellVertex(42, 42) - const vertex1 = new ValueCellVertex(42, 42) - mapping.setCell(adr('A1'), vertex0) - - mapping.setCell(adr('A1'), vertex1) - - expect(mapping.getCell(adr('A1'))).toBe(vertex1) - }) - - it("has when there's even no column", () => { - const mapping = builder(1, 1) - - expect(mapping.has(adr('A1'))).toBe(false) - }) - - it("has when there's even no row", () => { - const mapping = builder(1, 1) - - expect(mapping.has(adr('A3'))).toBe(false) - }) - - it('has when there was already something in that column', () => { - const mapping = builder(1, 2) - - mapping.setCell(adr('A2'), new ValueCellVertex(42, 42)) - - expect(mapping.has(adr('A1'))).toBe(false) - }) - - it('has when there is a value', () => { - const mapping = builder(1, 1) - - mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) - - expect(mapping.has(adr('A1'))).toBe(true) - }) - - it('addRows in the beginning of a mapping', () => { - const mapping = builder(1, 1) - - mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) - - mapping.addRows(0, 0, 1) - - expect(mapping.getCell(adr('A1'))).toBe(undefined) - expect(mapping.getCell(adr('A2'))).toEqual(new ValueCellVertex(42, 42)) - expect(mapping.getSheetHeight(0)).toEqual(2) - }) - - it('addRows in the middle of a mapping', () => { - const mapping = builder(1, 2) - - mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) - mapping.setCell(adr('A2'), new ValueCellVertex(43, 43)) - - mapping.addRows(0, 1, 1) - - expect(mapping.getCell(adr('A1'))).toEqual(new ValueCellVertex(42, 42)) - expect(mapping.getCell(adr('A2'))).toBe(undefined) - expect(mapping.getCell(adr('A3'))).toEqual(new ValueCellVertex(43, 43)) - expect(mapping.getSheetHeight(0)).toEqual(3) - }) - - it('addRows in the end of a mapping', () => { - const mapping = builder(1, 1) - - mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) - - mapping.addRows(0, 1, 1) - - expect(mapping.getCell(adr('A1'))).toEqual(new ValueCellVertex(42, 42)) - expect(mapping.getCell(adr('A2'))).toBe(undefined) - expect(mapping.getSheetHeight(0)).toEqual(2) - }) - - it('addRows more than one row', () => { - const mapping = builder(1, 2) - - mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) - mapping.setCell(adr('A2'), new ValueCellVertex(43, 43)) - - mapping.addRows(0, 1, 3) - - expect(mapping.getCell(adr('A1'))).toEqual(new ValueCellVertex(42, 42)) - expect(mapping.getCell(adr('A2'))).toBe(undefined) - expect(mapping.getCell(adr('A3'))).toBe(undefined) - expect(mapping.getCell(adr('A4'))).toBe(undefined) - expect(mapping.getCell(adr('A5'))).toEqual(new ValueCellVertex(43, 43)) - expect(mapping.getSheetHeight(0)).toEqual(5) - }) - - it('addRows when more than one column present', () => { - const mapping = builder(2, 2) - - mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) - - mapping.addRows(0, 1, 1) - - expect(mapping.getCell(adr('A1'))).toEqual(new ValueCellVertex(11, 11)) - expect(mapping.getCell(adr('B1'))).toEqual(new ValueCellVertex(12, 12)) - expect(mapping.getCell(adr('A2'))).toBe(undefined) - expect(mapping.getCell(adr('B2'))).toBe(undefined) - expect(mapping.getCell(adr('A3'))).toEqual(new ValueCellVertex(21, 21)) - expect(mapping.getCell(adr('B3'))).toEqual(new ValueCellVertex(22, 22)) - expect(mapping.getSheetHeight(0)).toEqual(3) - }) - - it('removeRows - one row', () => { - const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) // to remove - mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) // to remove - mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) - - expect(mapping.getSheetHeight(0)).toBe(2) - mapping.removeRows(new RowsSpan(0, 0, 0)) - expect(mapping.getSheetHeight(0)).toBe(1) - expect(mapping.getCellValue(adr('A1'))).toBe(21) - expect(mapping.getCellValue(adr('B1'))).toBe(22) - }) - - it('removeRows - more than one row', () => { - const mapping = builder(2, 4) - mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) // to - mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) // re - mapping.setCell(adr('A3'), new ValueCellVertex(31, 31)) // mo - mapping.setCell(adr('B3'), new ValueCellVertex(32, 32)) // ve - mapping.setCell(adr('A4'), new ValueCellVertex(41, 41)) - mapping.setCell(adr('B4'), new ValueCellVertex(42, 42)) - - expect(mapping.getSheetHeight(0)).toBe(4) - mapping.removeRows(new RowsSpan(0, 1, 2)) - expect(mapping.getSheetHeight(0)).toBe(2) - expect(mapping.getCellValue(adr('A1'))).toBe(11) - expect(mapping.getCellValue(adr('A2'))).toBe(41) - }) - - it('removeRows - remove more rows thant mapping size', () => { - const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) - - expect(mapping.getSheetHeight(0)).toBe(2) - mapping.removeRows(new RowsSpan(0, 0, 5)) - expect(mapping.getSheetHeight(0)).toBe(0) - expect(mapping.has(adr('A1'))).toBe(false) - }) - - it('removeRows - remove more cols than size, but still something left', () => { - const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) - - mapping.removeRows(new RowsSpan(0, 1, 5)) - - expect(mapping.getSheetHeight(0)).toBe(1) - expect(mapping.has(adr('A1'))).toBe(true) - expect(mapping.has(adr('A2'))).toBe(false) - }) - - it('removeRows - sometimes nothing is removed', () => { - const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) - - mapping.removeRows(new RowsSpan(0, 2, 3)) - - expect(mapping.getSheetHeight(0)).toBe(2) - expect(mapping.has(adr('A1'))).toBe(true) - expect(mapping.has(adr('A2'))).toBe(true) - }) - - it('removeColumns - more than one col', () => { - const mapping = builder(4, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) - mapping.setCell(adr('A2'), new ValueCellVertex(12, 12)) - mapping.setCell(adr('B1'), new ValueCellVertex(21, 21)) // to - mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) // re - mapping.setCell(adr('C1'), new ValueCellVertex(31, 31)) // mo - mapping.setCell(adr('C2'), new ValueCellVertex(32, 32)) // ve - mapping.setCell(adr('D1'), new ValueCellVertex(41, 41)) - mapping.setCell(adr('D2'), new ValueCellVertex(42, 42)) - - expect(mapping.getSheetWidth(0)).toBe(4) - mapping.removeColumns(new ColumnsSpan(0, 1, 2)) - expect(mapping.getSheetWidth(0)).toBe(2) - expect(mapping.getCellValue(adr('A1'))).toBe(11) - expect(mapping.getCellValue(adr('B1'))).toBe(41) - }) - - it('removeColumns - remove more cols thant mapping size', () => { - const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) - - expect(mapping.getSheetHeight(0)).toBe(2) - mapping.removeColumns(new ColumnsSpan(0, 0, 5)) - expect(mapping.getSheetWidth(0)).toBe(0) - expect(mapping.has(adr('A1'))).toBe(false) - }) - - it('removeColumns - remove more cols than size, but still something left', () => { - const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) - - mapping.removeColumns(new ColumnsSpan(0, 1, 5)) - - expect(mapping.getSheetWidth(0)).toBe(1) - expect(mapping.has(adr('A1'))).toBe(true) - expect(mapping.has(adr('B1'))).toBe(false) - }) - - it('removeColumns - sometimes nothing is removed', () => { - const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) - - mapping.removeColumns(new ColumnsSpan(0, 2, 3)) - - expect(mapping.getSheetWidth(0)).toBe(2) - expect(mapping.has(adr('A1'))).toBe(true) - expect(mapping.has(adr('B1'))).toBe(true) - }) - - it('should expand columns when adding cell', () => { - const mapping = builder(2, 2) - mapping.setCell(adr('C1'), new EmptyCellVertex()) - expect(mapping.getSheetWidth(0)).toBe(3) - }) - - it('should expand rows when adding cell', () => { - const mapping = builder(2, 2) - mapping.setCell(adr('A3'), new EmptyCellVertex()) - expect(mapping.getSheetHeight(0)).toBe(3) - }) - - it('should move cell from source to destination', () => { - const mapping = builder(1, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) - - mapping.moveCell(adr('A1'), adr('A2')) - - expect(mapping.has(adr('A1'))).toEqual(false) - expect(mapping.has(adr('A2'))).toEqual(true) - }) - - it('should throw error when trying to move not existing vertex', () => { - const mapping = builder(1, 2) - - expect(() => mapping.moveCell(adr('A1'), adr('A2'))).toThrowError('Cannot move cell. No cell with such address.') - }) - - it('should throw error when trying to move vertex onto occupied place', () => { - const mapping = builder(1, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) - mapping.setCell(adr('A2'), new ValueCellVertex(42, 42)) - - expect(() => mapping.moveCell(adr('A1'), adr('A2'))).toThrowError('Cannot move cell. Destination already occupied.') - }) - - it('should move vertices between sheets', () => { - const mapping = builder(1, 2) - mapping.setCell(adr('A1', 0), new ValueCellVertex(42, 42)) - - mapping.moveCell(adr('A1', 0), adr('A2', 1)) - - expect(mapping.has(adr('A1', 0))).toEqual(false) - expect(mapping.has(adr('A2', 1))).toEqual(true) - }) - - it('should throw error when trying to move vertices in non-existing sheet', () => { - const mapping = builder(1, 2) - mapping.setCell(adr('A1', 0), new ValueCellVertex(42, 42)) - - expect(() => mapping.moveCell(adr('A1', 3), adr('A2', 3))).toThrowError('Sheet not initialized.') - }) - - it('entriesFromColumnsSpan returns the same result regardless of the strategy', () => { - const denseMapping = new AddressMapping(new AlwaysDense()) - denseMapping.addSheetWithStrategy(0, new DenseStrategy(5, 5)) - - const sparseMapping = new AddressMapping(new AlwaysSparse()) - sparseMapping.addSheetWithStrategy(0, new SparseStrategy(5, 5)) - - const mappingsAndResults: { mapping: AddressMapping, results: String[][] }[] = [ - { - mapping: denseMapping, - results: [], - }, - { - mapping: sparseMapping, - results: [], - } - ] - - mappingsAndResults.forEach((item) => { - item.mapping.setCell(adr('A1', 0), new ValueCellVertex(42, 42)) - item.mapping.setCell(adr('A2', 0), new ValueCellVertex(43, 43)) - item.mapping.setCell(adr('A3', 0), new ValueCellVertex(44, 44)) - item.mapping.setCell(adr('B1', 0), new ValueCellVertex(45, 45)) - item.mapping.setCell(adr('B2', 0), new ValueCellVertex(46, 46)) - item.mapping.setCell(adr('B3', 0), new ValueCellVertex(47, 47)) - item.mapping.setCell(adr('C1', 0), new ValueCellVertex(48, 48)) - item.mapping.setCell(adr('C2', 0), new ValueCellVertex(49, 49)) - item.mapping.setCell(adr('C3', 0), new ValueCellVertex(50, 50)) - }) - - mappingsAndResults.forEach((item) => { - for (const [simpleCellAddress, cellVertex] of item.mapping.entriesFromColumnsSpan(new ColumnsSpan(0, 1, 2))) { - item.results.push([ - String(simpleCellAddress.sheet), - String(simpleCellAddress.row), - String(simpleCellAddress.col), - String(cellVertex.getCellValue()), - ]) - } - }) - - expect(mappingsAndResults[0].results).toEqual(mappingsAndResults[1].results) - }) -} - -describe('SparseStrategy', () => { - sharedExamples((maxCol: number, maxRow: number) => { - const mapping = new AddressMapping(new AlwaysSparse()) - mapping.addSheetWithStrategy(0, new SparseStrategy(maxCol, maxRow)) - mapping.addSheetWithStrategy(1, new SparseStrategy(maxCol, maxRow)) - return mapping - }) - - it('returns maximum row/col for simplest case', () => { - const mapping = new AddressMapping(new AlwaysSparse()) - mapping.addSheetWithStrategy(0, new SparseStrategy(4, 16)) - - mapping.setCell(adr('D16'), new ValueCellVertex(42, 42)) - - expect(mapping.getSheetHeight(0)).toEqual(16) - expect(mapping.getSheetWidth(0)).toEqual(4) - }) - - it('get all vertices', () => { - const mapping = new AddressMapping(new AlwaysSparse()) - const sparseStrategy = new SparseStrategy(3, 3) - mapping.addSheetWithStrategy(0, sparseStrategy) - - mapping.setCell(adr('A1', 0), new ValueCellVertex(42, 42)) - mapping.setCell(adr('A2', 0), new ValueCellVertex(43, 43)) - mapping.setCell(adr('A3', 0), new ValueCellVertex(44, 44)) - mapping.setCell(adr('B1', 0), new ValueCellVertex(45, 45)) - mapping.setCell(adr('B2', 0), new ValueCellVertex(46, 46)) - mapping.setCell(adr('B3', 0), new ValueCellVertex(47, 47)) - mapping.setCell(adr('C1', 0), new ValueCellVertex(48, 48)) - mapping.setCell(adr('C2', 0), new ValueCellVertex(49, 49)) - mapping.setCell(adr('C3', 0), new ValueCellVertex(50, 50)) - - const results = [] - for (const cellVertex of sparseStrategy.vertices()) { - results.push(String(cellVertex.getCellValue())) - } - - expect(results).toEqual([ - '42', '43', '44', '45', '46', '47', '48', '49', '50' - ]) - }) - - it('get all vertices - from column', () => { - const mapping = new AddressMapping(new AlwaysSparse()) - const sparseStrategy = new SparseStrategy(3, 3) - mapping.addSheetWithStrategy(0, sparseStrategy) - - mapping.setCell(adr('A1', 0), new ValueCellVertex(42, 42)) - mapping.setCell(adr('A2', 0), new ValueCellVertex(43, 43)) - mapping.setCell(adr('A3', 0), new ValueCellVertex(44, 44)) - mapping.setCell(adr('B1', 0), new ValueCellVertex(45, 45)) - mapping.setCell(adr('B2', 0), new ValueCellVertex(46, 46)) - mapping.setCell(adr('B3', 0), new ValueCellVertex(47, 47)) - mapping.setCell(adr('C1', 0), new ValueCellVertex(48, 48)) - mapping.setCell(adr('C2', 0), new ValueCellVertex(49, 49)) - mapping.setCell(adr('C3', 0), new ValueCellVertex(50, 50)) - - const results = [] - for (const cellVertex of sparseStrategy.verticesFromColumn(2)) { - results.push(String(cellVertex.getCellValue())) - } - - const outOfRangeResults = [] - for (const cellVertex of sparseStrategy.verticesFromColumn(5)) { - outOfRangeResults.push(String(cellVertex.getCellValue())) - } - - expect(results).toEqual(['48', '49', '50']) - expect(outOfRangeResults).toEqual([]) - }) - - it('get all vertices - from row', () => { - const mapping = new AddressMapping(new AlwaysSparse()) - const sparseStrategy = new SparseStrategy(3, 3) - mapping.addSheetWithStrategy(0, sparseStrategy) - - mapping.setCell(adr('A1', 0), new ValueCellVertex(42, 42)) - mapping.setCell(adr('A2', 0), new ValueCellVertex(43, 43)) - mapping.setCell(adr('A3', 0), new ValueCellVertex(44, 44)) - mapping.setCell(adr('B1', 0), new ValueCellVertex(45, 45)) - mapping.setCell(adr('B2', 0), new ValueCellVertex(46, 46)) - mapping.setCell(adr('B3', 0), new ValueCellVertex(47, 47)) - mapping.setCell(adr('C1', 0), new ValueCellVertex(48, 48)) - mapping.setCell(adr('C2', 0), new ValueCellVertex(49, 49)) - mapping.setCell(adr('C3', 0), new ValueCellVertex(50, 50)) - - const results = [] - for (const cellVertex of sparseStrategy.verticesFromRow(1)) { - results.push(String(cellVertex.getCellValue())) - } - - const outOfRangeResults = [] - for (const cellVertex of sparseStrategy.verticesFromRow(5)) { - outOfRangeResults.push(String(cellVertex.getCellValue())) - } - - expect(results).toEqual(['43', '46', '49']) - expect(outOfRangeResults).toEqual([]) - }) -}) - -describe('DenseStrategy', () => { - sharedExamples((maxCol, maxRow) => { - const mapping = new AddressMapping(new AlwaysDense()) - mapping.addSheetWithStrategy(0, new DenseStrategy(maxCol, maxRow)) - mapping.addSheetWithStrategy(1, new DenseStrategy(maxCol, maxRow)) - return mapping - }) - - it('returns maximum row/col for simplest case', () => { - const mapping = new AddressMapping(new AlwaysDense()) - mapping.addSheetWithStrategy(0, new DenseStrategy(1, 2)) - - expect(mapping.getSheetHeight(0)).toEqual(2) - expect(mapping.getSheetWidth(0)).toEqual(1) - }) - - it('get all vertices', () => { - const mapping = new AddressMapping(new AlwaysDense()) - const denseStratgey = new DenseStrategy(3, 3) - mapping.addSheetWithStrategy(0, denseStratgey) - - mapping.setCell(adr('A1', 0), new ValueCellVertex(42, 42)) - mapping.setCell(adr('A2', 0), new ValueCellVertex(43, 43)) - mapping.setCell(adr('A3', 0), new ValueCellVertex(44, 44)) - mapping.setCell(adr('B1', 0), new ValueCellVertex(45, 45)) - mapping.setCell(adr('B2', 0), new ValueCellVertex(46, 46)) - mapping.setCell(adr('B3', 0), new ValueCellVertex(47, 47)) - mapping.setCell(adr('C1', 0), new ValueCellVertex(48, 48)) - mapping.setCell(adr('C2', 0), new ValueCellVertex(49, 49)) - mapping.setCell(adr('C3', 0), new ValueCellVertex(50, 50)) - - const results = [] - for (const cellVertex of denseStratgey.vertices()) { - results.push(String(cellVertex.getCellValue())) - } - - expect(results).toEqual([ - '42', '45', '48', '43', '46', '49', '44', '47', '50' - ]) - }) - - it('get all vertices - from column', () => { - const mapping = new AddressMapping(new AlwaysDense()) - const denseStratgey = new DenseStrategy(3, 3) - mapping.addSheetWithStrategy(0, denseStratgey) - - mapping.setCell(adr('A1', 0), new ValueCellVertex(42, 42)) - mapping.setCell(adr('A2', 0), new ValueCellVertex(43, 43)) - mapping.setCell(adr('A3', 0), new ValueCellVertex(44, 44)) - mapping.setCell(adr('B1', 0), new ValueCellVertex(45, 45)) - mapping.setCell(adr('B2', 0), new ValueCellVertex(46, 46)) - mapping.setCell(adr('B3', 0), new ValueCellVertex(47, 47)) - mapping.setCell(adr('C1', 0), new ValueCellVertex(48, 48)) - mapping.setCell(adr('C2', 0), new ValueCellVertex(49, 49)) - mapping.setCell(adr('C3', 0), new ValueCellVertex(50, 50)) - - const results = [] - for (const cellVertex of denseStratgey.verticesFromColumn(2)) { - results.push(String(cellVertex.getCellValue())) - } - - const outOfRangeResults = [] - for (const cellVertex of denseStratgey.verticesFromColumn(5)) { - outOfRangeResults.push(String(cellVertex.getCellValue())) - } - - expect(results).toEqual(['48', '49', '50']) - expect(outOfRangeResults).toEqual([]) - }) - - it('get all vertices - from row', () => { - const mapping = new AddressMapping(new AlwaysDense()) - const denseStratgey = new DenseStrategy(3, 3) - mapping.addSheetWithStrategy(0, denseStratgey) - - mapping.setCell(adr('A1', 0), new ValueCellVertex(42, 42)) - mapping.setCell(adr('A2', 0), new ValueCellVertex(43, 43)) - mapping.setCell(adr('A3', 0), new ValueCellVertex(44, 44)) - mapping.setCell(adr('B1', 0), new ValueCellVertex(45, 45)) - mapping.setCell(adr('B2', 0), new ValueCellVertex(46, 46)) - mapping.setCell(adr('B3', 0), new ValueCellVertex(47, 47)) - mapping.setCell(adr('C1', 0), new ValueCellVertex(48, 48)) - mapping.setCell(adr('C2', 0), new ValueCellVertex(49, 49)) - mapping.setCell(adr('C3', 0), new ValueCellVertex(50, 50)) - - const results = [] - for (const cellVertex of denseStratgey.verticesFromRow(1)) { - results.push(String(cellVertex.getCellValue())) - } - - const outOfRangeResults = [] - for (const cellVertex of denseStratgey.verticesFromRow(5)) { - outOfRangeResults.push(String(cellVertex.getCellValue())) - } - - expect(results).toEqual(['43', '46', '49']) - expect(outOfRangeResults).toEqual([]) - }) -}) - -describe('AddressMapping', () => { - it('#buildAddresMapping - when sparse matrix', () => { - const addressMapping = new AddressMapping(new DenseSparseChooseBasedOnThreshold(0.8)) - const sheet = [ - [null, null, null], - [null, null, '1'], - ] - addressMapping.addSheetAndSetStrategyBasedOnBoundaries(0, findBoundaries(sheet)) - - expect(addressMapping.getStrategyForSheetOrThrow(0)).toBeInstanceOf(SparseStrategy) - }) - - it('#buildAddresMapping - when dense matrix', () => { - const addressMapping = new AddressMapping(new DenseSparseChooseBasedOnThreshold(0.8)) - const sheet = [ - ['1', '1'], - ['1', '1'], - ] - addressMapping.addSheetAndSetStrategyBasedOnBoundaries(0, findBoundaries(sheet)) - - expect(addressMapping.getStrategyForSheetOrThrow(0)).toBeInstanceOf(DenseStrategy) - }) -}) diff --git a/test/unit/address-representation-converters.spec.ts b/test/unit/address-representation-converters.spec.ts deleted file mode 100644 index a0731eddb8..0000000000 --- a/test/unit/address-representation-converters.spec.ts +++ /dev/null @@ -1,122 +0,0 @@ -import {simpleCellRange} from '../../src/AbsoluteCellRange' -import {simpleCellAddress} from '../../src/Cell' -import {Maybe} from '../../src/Maybe' -import { - simpleCellAddressFromString, - simpleCellAddressToString, - simpleCellRangeFromString, - simpleCellRangeToString -} from '../../src/parser' -import {adr} from './testUtils' - -describe('simpleCellAddressFromString', () => { - const sheetMappingFunction = (name: string): Maybe => { - const index = ['Sheet1', 'Sheet2', 'Sheet3'].indexOf(name) - return index > 0 ? index : undefined - } - - it('should return a simple cell address when called with a valid string address', () => { - expect(simpleCellAddressFromString(sheetMappingFunction, 'A1', 0)).toEqual(adr('A1')) - expect(simpleCellAddressFromString(sheetMappingFunction, 'AY7', 0)).toEqual(simpleCellAddress(0, 50, 6)) - }) - - it('should return undefined when sheet does not exist', () => { - expect(simpleCellAddressFromString(sheetMappingFunction, 'Sheet4!A1', 0)).toBeUndefined() - }) - - it('should return address with context sheet when string address does not contain a sheet name', () => { - expect(simpleCellAddressFromString(sheetMappingFunction, 'A1', 1)).toEqual(adr('A1', 1)) - }) - - it('should return a valid address when called with a valid sheet name', () => { - expect(simpleCellAddressFromString(sheetMappingFunction, 'Sheet2!A1', 1)).toEqual(adr('A1', 1)) - }) - - it('should return address with sheet number from sheet mapping regardless of context sheet parameter', () => { - expect(simpleCellAddressFromString(sheetMappingFunction, 'Sheet3!A1', 1)).toEqual(adr('A1', 2)) - }) -}) - -describe('simpleCellRangeFromString', () => { - const sheetMappingFunction = (name: string): Maybe => { - const index = ['Sheet1', 'Sheet2', 'Sheet3'].indexOf(name) - return index > 0 ? index : undefined - } - - it('should not accept invalid ranges', () => { - expect(simpleCellRangeFromString(sheetMappingFunction, 'A1', 0)).toEqual(undefined) - expect(simpleCellRangeFromString(sheetMappingFunction, 'A1:B1:C1', 0)).toEqual(undefined) - }) - - it('should return undefined when the Sheet4 does not exist', () => { - expect(simpleCellRangeFromString(sheetMappingFunction, 'Sheet4!A1:A2', 0)).toEqual(undefined) - }) - - it('should return address with overridden sheet', () => { - expect(simpleCellRangeFromString(sheetMappingFunction, 'A1:A2', 1)).toEqual(simpleCellRange(adr('A1', 1), adr('A2', 1))) - }) - - it('should return address with sheet number from sheet mapping', () => { - expect(simpleCellRangeFromString(sheetMappingFunction, 'Sheet2!A1:A2', 1)).toEqual(simpleCellRange(adr('A1', 1), adr('A2', 1))) - }) - - it('should return address with sheet number from sheet mapping for both start and end', () => { - expect(simpleCellRangeFromString(sheetMappingFunction, 'Sheet2!A1:Sheet2!A2', 1)).toEqual(simpleCellRange(adr('A1', 1), adr('A2', 1))) - }) - - it('should return address with sheet number from sheet mapping regardless of override parameter', () => { - expect(simpleCellRangeFromString(sheetMappingFunction, 'Sheet3!A1:Sheet3!A2', 1)).toEqual(simpleCellRange(adr('A1', 2), adr('A2', 2))) - }) - - it('should not accept distinct sheets', () => { - expect(simpleCellRangeFromString(sheetMappingFunction, 'A1:Sheet3!A2', 1)).toEqual(undefined) - expect(simpleCellRangeFromString(sheetMappingFunction, 'Sheet2!A1:Sheet3!A2', 1)).toEqual(undefined) - }) -}) - -describe('simpleCellAddressToString', () => { - const sheetIndexMappingFunction = (index: number): Maybe => { - return ['Sheet1', 'Sheet2', 'Sheet3', '~`!@#$%^&*()_-+_=/|?{}[]"', "Sheet'With'Quotes"][index] - } - - it('should return string representation', () => { - expect(simpleCellAddressToString(sheetIndexMappingFunction, adr('A1'), 0)).toEqual('A1') - expect(simpleCellAddressToString(sheetIndexMappingFunction, simpleCellAddress(0, 50, 6), 0)).toEqual('AY7') - }) - - it('should return string representation with sheet name', () => { - expect(simpleCellAddressToString(sheetIndexMappingFunction, adr('A1'), 1)).toEqual('Sheet1!A1') - }) - - it('should quote sheet names with special characters', () => { - expect(simpleCellAddressToString(sheetIndexMappingFunction, adr('A1', 3), 1)).toEqual("'~`!@#$%^&*()_-+_=/|?{}[]\"'!A1") - }) - - it('should escape quote in quotes', () => { - expect(simpleCellAddressToString(sheetIndexMappingFunction, adr('A1', 4), 1)).toEqual("'Sheet''With''Quotes'!A1") - }) - - it('should return undefined', () => { - expect(simpleCellAddressToString(sheetIndexMappingFunction, adr('A1', 42), 42)).toBeUndefined() - expect(simpleCellAddressToString(sheetIndexMappingFunction, adr('A1', 42), 1)).toBeUndefined() - }) -}) - -describe('simpleCellRangeToString', () => { - const sheetIndexMappingFunction = (index: number): Maybe => { - return ['Sheet1', 'Sheet2', 'Sheet3'][index] - } - - it('should return string representation', () => { - expect(simpleCellRangeToString(sheetIndexMappingFunction, simpleCellRange(adr('A1'), adr('A2')), 0)).toEqual('A1:A2') - }) - - it('should return string representation with sheet name', () => { - expect(simpleCellRangeToString(sheetIndexMappingFunction, simpleCellRange(adr('A1'), adr('A2')), 1)).toEqual('Sheet1!A1:A2') - }) - - it('should return undefined when refering nonexistent sheet', () => { - expect(simpleCellRangeToString(sheetIndexMappingFunction, simpleCellRange(adr('A1', 42), adr('A2', 42)), 42)).toBeUndefined() - expect(simpleCellRangeToString(sheetIndexMappingFunction, simpleCellRange(adr('A1', 42), adr('A2', 42)), 1)).toBeUndefined() - }) -}) diff --git a/test/unit/arrays-integration.spec.ts b/test/unit/arrays-integration.spec.ts deleted file mode 100644 index 09aad73441..0000000000 --- a/test/unit/arrays-integration.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {HyperFormula} from '../../src' -import {adr} from './testUtils' - -describe('integration test', () => { - it('should work', () => { - const engine = HyperFormula.buildFromSheets({ - 'Output': [['=INDEX(LookupRange,MATCH(1,(Lookup!A1:A8<=Inputs!A1)*(Lookup!B1:B8>=Inputs!A1)*(Lookup!C1:C8=Inputs!B1), 0), 4)']], - 'Inputs': [[23, 'B']], - 'Lookup': [ - [11, 15, 'A', 66], - [11, 15, 'B', 77], - [16, 20, 'A', 88], - [16, 20, 'B', 99], - [21, 25, 'A', 110], - [21, 25, 'B', 121], - [26, 30, 'A', 132], - [26, 30, 'B', 143], - ] - }, {useArrayArithmetic: true}) //flag that enables ArrayFormula() everywhere - - engine.addNamedExpression('LookupRange', '=Lookup!$A$1:Lookup!$D$8') - - expect(engine.getCellValue(adr('A1'))).toEqual(121) - - engine.setCellContents(adr('B1', engine.getSheetId('Inputs')), 'A') - - expect(engine.getCellValue(adr('A1'))).toEqual(110) - }) -}) diff --git a/test/unit/arrays.spec.ts b/test/unit/arrays.spec.ts deleted file mode 100644 index 71d29cc94f..0000000000 --- a/test/unit/arrays.spec.ts +++ /dev/null @@ -1,537 +0,0 @@ -import {ErrorType, HyperFormula} from '../../src' -import {ArraySize} from '../../src/ArraySize' -import {ArrayFormulaVertex, ValueCellVertex} from '../../src/DependencyGraph' -import {ErrorMessage} from '../../src/error-message' -import {adr, detailedError, detailedErrorWithOrigin, expectVerticesOfTypes, noSpace} from './testUtils' - -describe('without arrayformula, with useArrayArithmetic flag', () => { - it('unary op, scalar ret', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], ['=SUM(-A1:C1)']], {useArrayArithmetic: true}) - expect(engine.getCellValue(adr('A2'))).toEqual(-6) - }) - - it('unary op, array ret', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], ['=-A1:C1']], {useArrayArithmetic: true}) - expect(engine.getSheetValues(0)).toEqual([[1, 2, 3], [-1, -2, -3]]) - }) - - it('binary op, scalar ret', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], ['=SUM(2*A1:C1+A2:C2)']], {useArrayArithmetic: true}) - expect(engine.getCellValue(adr('A3'))).toEqual(27) - }) - - it('binary op, array ret', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], ['=2*A1:C1+A2:C2']], {useArrayArithmetic: true}) - expect(engine.getSheetValues(0)).toEqual([[1, 2, 3], [4, 5, 6], [6, 9, 12]]) - }) - - it('binary op, array ret, concat', () => { - const engine = HyperFormula.buildFromArray([['a', 'b', 'c'], ['d', 'e', 'f'], ['=A1:C1&A2:C2']], {useArrayArithmetic: true}) - expect(engine.getSheetValues(0)).toEqual([['a', 'b', 'c'], ['d', 'e', 'f'], ['ad', 'be', 'cf']]) - }) - - it('index', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], ['=INDEX(2*A1:C1+3,1,1)']], {useArrayArithmetic: true}) - expect(engine.getCellValue(adr('A2'))).toEqual(5) - }) - - it('binary op + index', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ['=INDEX(A1:C2+A1:B3,1,1)', '=INDEX(A1:C2+A1:B3,1,2)', '=INDEX(A1:C2+A1:B3,1,3)'], - ['=INDEX(A1:C2+A1:B3,2,1)', '=INDEX(A1:C2+A1:B3,2,2)', '=INDEX(A1:C2+A1:B3,2,3)'], - ['=INDEX(A1:C2+A1:B3,3,1)', '=INDEX(A1:C2+A1:B3,3,2)', '=INDEX(A1:C2+A1:B3,3,3)'], - ], {useArrayArithmetic: true}) - expect(engine.getSheetValues(0)).toEqual( - [ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - [2, 4, detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!C4')], - [8, 10, detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!C5')], - [detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!A6'), detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!B6'), detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!C6')]]) - }) - - it('match', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(10,2*A2:E2)'], - [1, 2, 3, 4, 5], - ], {useArrayArithmetic: true}) - expect(engine.getCellValue(adr('A1'))).toEqual(5) - }) -}) - -describe('without arrayformula, without useArrayArithmetic flag', () => { - it('unary op', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [undefined, undefined, undefined, '=SUM(-A1:C1)']], {useArrayArithmetic: false}) - expect(engine.getCellValue(adr('D2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) - - it('binary op', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [undefined, undefined, undefined, '=SUM(2*A1:C1+A2:C2)']], {useArrayArithmetic: false}) - expect(engine.getCellValue(adr('D3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) - - it('index', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [undefined, undefined, undefined, '=INDEX(2*A1:C1+3,1,1)']], {useArrayArithmetic: false}) - expect(engine.getCellValue(adr('D2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) - - it('binary op + index', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ['=INDEX(A1:C2+A1:B3,1,1)', '=INDEX(A1:C2+A1:B3,1,2)', '=INDEX(A1:C2+A1:B3,1,3)'], - ['=INDEX(A1:C2+A1:B3,2,1)', '=INDEX(A1:C2+A1:B3,2,2)', '=INDEX(A1:C2+A1:B3,2,3)'], - ['=INDEX(A1:C2+A1:B3,3,1)', '=INDEX(A1:C2+A1:B3,3,2)', '=INDEX(A1:C2+A1:B3,3,3)'], - ], {useArrayArithmetic: false}) - expect(engine.getSheetValues(0)).toEqual( - [ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - [detailedErrorWithOrigin(ErrorType.VALUE, 'Sheet1!A4', ErrorMessage.ScalarExpected), detailedErrorWithOrigin(ErrorType.VALUE, 'Sheet1!B4', ErrorMessage.ScalarExpected), detailedErrorWithOrigin(ErrorType.VALUE, 'Sheet1!C4', ErrorMessage.ScalarExpected)], - [detailedErrorWithOrigin(ErrorType.VALUE, 'Sheet1!A5', ErrorMessage.ScalarExpected), detailedErrorWithOrigin(ErrorType.VALUE, 'Sheet1!B5', ErrorMessage.ScalarExpected), detailedErrorWithOrigin(ErrorType.VALUE, 'Sheet1!C5', ErrorMessage.ScalarExpected)], - [detailedErrorWithOrigin(ErrorType.VALUE, 'Sheet1!A6', ErrorMessage.ScalarExpected), detailedErrorWithOrigin(ErrorType.VALUE, 'Sheet1!B6', ErrorMessage.ScalarExpected), detailedErrorWithOrigin(ErrorType.VALUE, 'Sheet1!C6', ErrorMessage.ScalarExpected)] - ]) - }) - - it('match', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(10,2*B1:F1)', 1, 2, 3, 4, 5], - ], {useArrayArithmetic: false}) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) -}) - -describe('with arrayformula, without useArrayArithmetic flag', () => { - it('unary op', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], ['=ARRAYFORMULA(SUM(-A1:C1))']], {useArrayArithmetic: false}) - expect(engine.getCellValue(adr('A2'))).toEqual(-6) - }) - - it('unary op #2', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], ['=SUM(ARRAYFORMULA(-A1:C1))']], {useArrayArithmetic: false}) - expect(engine.getCellValue(adr('A2'))).toEqual(-6) - }) - - it('binary op', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], ['=ARRAYFORMULA(SUM(2*A1:C1+A2:C2))']], {useArrayArithmetic: false}) - expect(engine.getCellValue(adr('A3'))).toEqual(27) - }) - - it('binary op #2', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], ['=SUM(ARRAYFORMULA(2*A1:C1+A2:C2))']], {useArrayArithmetic: false}) - expect(engine.getCellValue(adr('A3'))).toEqual(27) - }) - - it('index', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], ['=ARRAYFORMULA(INDEX(2*A1:C1+3,1,1))']], {useArrayArithmetic: false}) - expect(engine.getCellValue(adr('A2'))).toEqual(5) - }) - - it('binary op + index', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ['=ARRAYFORMULA(INDEX(A1:C2+A1:B3,1,1))', '=ARRAYFORMULA(INDEX(A1:C2+A1:B3,1,2))', '=ARRAYFORMULA(INDEX(A1:C2+A1:B3,1,3))'], - ['=ARRAYFORMULA(INDEX(A1:C2+A1:B3,2,1))', '=ARRAYFORMULA(INDEX(A1:C2+A1:B3,2,2))', '=ARRAYFORMULA(INDEX(A1:C2+A1:B3,2,3))'], - ['=ARRAYFORMULA(INDEX(A1:C2+A1:B3,3,1))', '=ARRAYFORMULA(INDEX(A1:C2+A1:B3,3,2))', '=ARRAYFORMULA(INDEX(A1:C2+A1:B3,3,3))'], - ], {useArrayArithmetic: false}) - expect(engine.getSheetValues(0)).toEqual( - [ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - [2, 4, detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!C4')], - [8, 10, detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!C5')], - [detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!A6'), detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!B6'), detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!C6')]]) - }) - - it('match', () => { - const engine = HyperFormula.buildFromArray([ - ['=ARRAYFORMULA(MATCH(10,2*A2:E2))'], - [1, 2, 3, 4, 5], - ], {useArrayArithmetic: false}) - expect(engine.getCellValue(adr('A1'))).toEqual(5) - }) -}) - -describe('coercion of array to scalar', () => { - it('actual range', () => { - const engine = HyperFormula.buildFromArray([[0, 2, 3, '=SIN(A1:C1)']]) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('array + function #1', () => { - const engine = HyperFormula.buildFromArray([[0, 2, 3], ['=SIN(ARRAYFORMULA(2*A1:C1))']]) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) - - it('inline array + function #2', () => { - const engine = HyperFormula.buildFromArray([['=SIN({0,2,3})']]) - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) - - it('array + binary op #1', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], ['=ARRAYFORMULA(2*A1:C1)+ARRAYFORMULA(2*A1:C1)']]) - expect(engine.getSheetValues(0)).toEqual([[1, 2, 3], [4]]) - }) - - it('inline array + binary op #2', () => { - const engine = HyperFormula.buildFromArray([['={1,2,3}+{1,2,3}']]) - expect(engine.getSheetValues(0)).toEqual([[2]]) - }) - - it('array + unary op #1', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], ['=-ARRAYFORMULA(2*A1:C1)']]) - expect(engine.getSheetValues(0)).toEqual([[1, 2, 3], [-2]]) - }) - - it('inline array + unary op #2', () => { - const engine = HyperFormula.buildFromArray([['=-{1,2,3}']]) - expect(engine.getSheetValues(0)).toEqual([[-1]]) - }) -}) - -describe('range interpolation', () => { - it('with function', () => { - const engine = HyperFormula.buildFromArray([[0, 1, 2], ['=EXP(A1:C1)']]) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - }) - - it('with binary op', () => { - const engine = HyperFormula.buildFromArray([[0, 1, 2], [undefined, '=A1:C1+A1:C1']]) - expect(engine.getCellValue(adr('B2'))).toEqual(2) - }) - - it('with unary op', () => { - const engine = HyperFormula.buildFromArray([[0, 1, 2], [undefined, '=-A1:C1']]) - expect(engine.getCellValue(adr('B2'))).toEqual(-1) - }) - - it('columns', () => { - const engine = HyperFormula.buildFromArray([[0], [1, '=-A1:A3'], [2]]) - expect(engine.getCellValue(adr('B2'))).toEqual(-1) - }) - - it('too many rows', () => { - const engine = HyperFormula.buildFromArray([[0, 1, 2], [4, 5, 6], [undefined, '=-A1:C2']]) - expect(engine.getCellValue(adr('B3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) - - it('different sheets', () => { - const engine = HyperFormula.buildFromSheets({Sheet1: [[0, 1, 2]], Sheet2: [['=-Sheet1!A1:C1']]}) - expect(engine.getCellValue(adr('A1', 1))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) -}) - -describe('inline arrays', () => { - describe('should be expanded into multiple cells', () => { - it('simple 2D array', () => { - const engine = HyperFormula.buildFromArray([['={1,2;3,4}']]) - expect(engine.getSheetValues(0)).toEqual([[1, 2], [3, 4]]) - }) - - it('nested arrays', () => { - const engine = HyperFormula.buildFromArray([['={1,{2,3},4;{5;6},{7,8;9,10},{11;12};13,{14,15},16}']]) - expect(engine.getSheetValues(0)).toEqual([[1, 2, 3, 4], [5, 7, 8, 11], [6, 9, 10, 12], [13, 14, 15, 16]]) - }) - - it('nested arrays with operators', () => { - const engine = HyperFormula.buildFromArray([['=ARRAYFORMULA({1,{2,3}+{0,0},4;{5;6},2*{7,8;9,10},-{11;12};13,{14,15},16})']]) - expect(engine.getSheetValues(0)).toEqual([[1, 2, 3, 4], [5, 14, 16, -11], [6, 18, 20, -12], [13, 14, 15, 16]]) - }) - }) - - it('size mismatch should result in an error', () => { - const engine = HyperFormula.buildFromArray([['={1,2;3}']]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SizeMismatch)) - }) - - it('should work inside a function', () => { - const hyperFormula: HyperFormula = HyperFormula.buildFromArray( - [['=SUM({1, 2})']], - {licenseKey: 'gpl-v3'}, - ) - expect(hyperFormula.getCellValue({row: 0, col: 0, sheet: 0})).toBe(3) - }) - - it('should not update the value when referenced cells change', () => { - const hyperFormula: HyperFormula = HyperFormula.buildFromArray( - [[1, '={A1}']], - {licenseKey: 'gpl-v3'}, - ) - expect(hyperFormula.getCellValue({row: 0, col: 1, sheet: 0})).toBe(1) - hyperFormula.setCellContents({row: 0, col: 0, sheet: 0}, 42) - expect(hyperFormula.getCellValue({row: 0, col: 1, sheet: 0})).toBe(1) - }) -}) - -describe('vectorization', () => { - it('1 arg function row', () => { - const engine = HyperFormula.buildFromArray([['=ABS({-2,-1,1,2})']], {useArrayArithmetic: true}) - expect(engine.getSheetValues(0)).toEqual([[2, 1, 1, 2]]) - }) - - it('1 arg function column', () => { - const engine = HyperFormula.buildFromArray([['=ABS({2;-2})']], {useArrayArithmetic: true}) - expect(engine.getSheetValues(0)).toEqual([[2], [2]]) - }) - - it('1 arg function square', () => { - const engine = HyperFormula.buildFromArray([['=ABS({1,2;-1,-2})']], {useArrayArithmetic: true}) - expect(engine.getSheetValues(0)).toEqual([[1, 2], [1, 2]]) - }) - - it('1 arg function no flag - should cast to scalar', () => { - const engine = HyperFormula.buildFromArray([['=ABS({-2,-1,1,2})']], {useArrayArithmetic: false}) - expect(engine.getSheetValues(0)).toEqual([[2]]) - }) - - it('multi arg function', () => { - const engine = HyperFormula.buildFromArray([['=DATE({1,2},1,1)']], {useArrayArithmetic: true}) - expect(engine.getSheetValues(0)).toEqual([[367, 732]]) - }) - - it('multi arg function #2', () => { - const engine = HyperFormula.buildFromArray([['=DATE({1,2},{1,2},{1,2})']], {useArrayArithmetic: true}) - expect(engine.getSheetValues(0)).toEqual([[367, 764]]) - }) - - it('multi arg function #3', () => { - const engine = HyperFormula.buildFromArray([['=DATE({1,2},{1;2},{1})']], {useArrayArithmetic: true}) - expect(engine.getSheetValues(0)).toEqual([[367, 732], [398, 763]]) - }) - - it('multi arg function #4', () => { - const engine = HyperFormula.buildFromArray([['=DATE({1,2},{1,2,3},{1})']], {useArrayArithmetic: true}) - expect(engine.getSheetValues(0)).toEqual([[367, 763, detailedError(ErrorType.VALUE, ErrorMessage.InvalidDate)]]) - }) - - it('mixed types', () => { - const engine = HyperFormula.buildFromArray([['=ZTEST({1,2,1},{2;3})']], {useArrayArithmetic: true}) - const val = engine.getSheetValues(0) - expect(val.length).toEqual(2) - expect(val[0].length).toEqual(1) - expect(val[1].length).toEqual(1) - }) - - it('no vectorization here #1', () => { - const engine = HyperFormula.buildFromArray([['=SUM({1,2,1},{2;3})']], {useArrayArithmetic: true}) - expect(engine.getSheetValues(0)).toEqual([[9]]) - }) - - it('no vectorization here #2', () => { - const engine = HyperFormula.buildFromArray([['=AND({TRUE(),FALSE()},{TRUE();FALSE()})']], {useArrayArithmetic: true}) - expect(engine.getSheetValues(0)).toEqual([[false]]) - }) - - it('vectorize with defaults', () => { - const engine = HyperFormula.buildFromArray([['=IF({TRUE(),FALSE()},{1;2;3}, {2;3})']], {useArrayArithmetic: true}) - expect(engine.getSheetValues(0)).toEqual([[1, 2], [2, 3], [3, false]]) - }) - - it('should work with switch', () => { - const engine = HyperFormula.buildFromArray([ - ['=SWITCH({1,2,3},1,2,3,4,5)'] - ], {useArrayArithmetic: true}) - expect(engine.getSheetValues(0)).toEqual([[2, 5, 4]]) - }) -}) - -describe('build from array', () => { - it('should create engine with array', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, '=-A1:B2'], - [3, 4], - ], {useArrayArithmetic: true}) - - expect(engine.getSheetValues(0)).toEqual([ - [1, 2, -1, -2], - [3, 4, -3, -4], - ]) - }) - - it('should be enough to specify only corner of an array', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRANSPOSE(D1:E2)'], - ], {useArrayArithmetic: true}) - - expectVerticesOfTypes(engine, [ - [ArrayFormulaVertex, ArrayFormulaVertex], - [ArrayFormulaVertex, ArrayFormulaVertex], - ]) - }) - - it('should be separate arrays', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRANSPOSE(D1:E2)', '=TRANSPOSE(D1:E2)'], - ['=TRANSPOSE(D1:E2)', '=TRANSPOSE(D1:E2)'], - ], {useArrayArithmetic: true}) - - expectVerticesOfTypes(engine, [ - [ArrayFormulaVertex, ArrayFormulaVertex, undefined], - [ArrayFormulaVertex, ArrayFormulaVertex, ArrayFormulaVertex], - [undefined, ArrayFormulaVertex, ArrayFormulaVertex], - ]) - expect(engine.arrayMapping.arrayMapping.size).toEqual(4) - }) - - it('should REF last array', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRANSPOSE(D1:E2)', '=TRANSPOSE(D1:E2)', null, 1, 2], - ['=TRANSPOSE(D1:E2)', null, null, 1, 2], - ], {useArrayArithmetic: true}) - - expectVerticesOfTypes(engine, [ - [ArrayFormulaVertex, ArrayFormulaVertex, ArrayFormulaVertex], - [ArrayFormulaVertex, ArrayFormulaVertex, ArrayFormulaVertex], - [undefined, undefined], - ]) - expect(engine.getSheetValues(0)).toEqual([ - [noSpace(), 1, 1, 1, 2], - [noSpace(), 2, 2, 1, 2], - ]) - expect(engine.arrayMapping.arrayMapping.size).toEqual(3) - }) - - it('array should work with different types of data', () => { - const engine = HyperFormula.buildFromArray([ - [1, 'foo', '=TRANSPOSE(A1:B2)'], - [true, '=SUM(A1)'], - ], {useArrayArithmetic: true}) - - expect(engine.getSheetValues(0)).toEqual([ - [1, 'foo', 1, true], - [true, 1, 'foo', 1], - ]) - }) - - it('should make REF array if no space', () => { - const engine = HyperFormula.buildFromArray([ - ['=-C1:D2', 2], - [3, 4], - ], {useArrayArithmetic: true}) - - expect(engine.getSheetValues(0)).toEqual([ - [noSpace(), 2], - [3, 4], - ]) - }) - - it('should not shrink array if empty vertex', () => { - const engine = HyperFormula.buildFromArray([ - ['=-C1:D2', null], - [null, null] - ], {useArrayArithmetic: true}) - - expectVerticesOfTypes(engine, [ - [ArrayFormulaVertex, ArrayFormulaVertex], - [ArrayFormulaVertex, ArrayFormulaVertex], - ]) - - }) - - it('should shrink to one vertex if there is more content colliding with array', () => { - const engine = HyperFormula.buildFromArray([ - ['=-C1:D2', null], - [1, null] - ], {useArrayArithmetic: true}) - - expect(engine.arrayMapping.getArrayByCorner(adr('A1'))?.array.size).toEqual(ArraySize.error()) - expectVerticesOfTypes(engine, [ - [ArrayFormulaVertex, undefined], - [ValueCellVertex, undefined], - ]) - }) - - it('DependencyGraph changes should be empty after building fresh engine', () => { - const engine = HyperFormula.buildFromArray([ - ['=-C1:D2', null], - [1, null] - ], {useArrayArithmetic: true}) - - expect(engine.dependencyGraph.getAndClearContentChanges().isEmpty()).toEqual(true) - }) -}) - -describe('column ranges', () => { - it('arithmetic should work for column range', () => { - const engine = HyperFormula.buildFromArray([ - ['=2*(B:B)', 1], - [null, 2], - ], {useArrayArithmetic: true}) - - expect(engine.getSheetValues(0)).toEqual([[2, 1], [4, 2]]) - }) - - it('arithmetic should work for row range', () => { - const engine = HyperFormula.buildFromArray([ - ['=2*(2:2)', null], - [1, 2], - ], {useArrayArithmetic: true}) - - expect(engine.getSheetValues(0)).toEqual([[2, 4], [1, 2]]) - }) - - it('arithmetic for shifted column range -- error', () => { - const engine = HyperFormula.buildFromArray([ - [null, 1], - ['=2*(B:B)', 2], - ], {useArrayArithmetic: true}) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.SPILL, ErrorMessage.NoSpaceForArrayResult)) - }) - - it('arithmetic for shifted row range -- error', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=2*(2:2)'], - [1, 2], - ], {useArrayArithmetic: true}) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.SPILL, ErrorMessage.NoSpaceForArrayResult)) - }) - - it('sumproduct test', () => { - const engine = HyperFormula.buildFromArray([ - [1, 1, 3, '=SUMPRODUCT((A:A=1)*(B:B=1), C:C)'], - [1, 2, 3], - [3, 1, 3], - ], {useArrayArithmetic: true} - ) - - expect(engine.getCellValue(adr('D1'))).toEqual(3) - }) - - it('should handle array shrinking when dependent is a value cell', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2], - [3, 4], - ['=TRANSPOSE(A1:B2)'], - ], {useArrayArithmetic: true}) - - expect(engine.getCellValue(adr('A3'))).toBe(1) - expect(engine.getCellValue(adr('A4'))).toBe(2) - expect(engine.getCellValue(adr('B3'))).toBe(3) - expect(engine.getCellValue(adr('B4'))).toBe(4) - - engine.setCellContents(adr('B3'), 'obstructing value') - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.SPILL, ErrorMessage.NoSpaceForArrayResult)) - }) - - it('should correctly set address mapping for scalar formula', () => { - const engine = HyperFormula.buildFromArray([ - ['=1+1'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(2) - expect(engine.addressMapping.getCell(adr('A1'))).toBeDefined() - expect(engine.addressMapping.getCell(adr('B1'))).toBeUndefined() - }) -}) diff --git a/test/unit/build-engine.spec.ts b/test/unit/build-engine.spec.ts deleted file mode 100644 index 0e16c94480..0000000000 --- a/test/unit/build-engine.spec.ts +++ /dev/null @@ -1,114 +0,0 @@ -import {HyperFormula} from '../../src' -import {plPL} from '../../src/i18n/languages' -import {adr} from './testUtils' - -describe('Building empty engine', () => { - it('works', () => { - const engine = HyperFormula.buildEmpty() - expect(engine).toBeInstanceOf(HyperFormula) - }) - - it('accepts config params', () => { - const config = {dateFormats: ['MM']} - const engine = HyperFormula.buildEmpty(config) - expect(engine.getConfig().dateFormats[0]).toBe('MM') - }) -}) - -describe('Building engine from arrays', () => { - it('works', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [], - Sheet2: [], - }) - - expect(engine).toBeInstanceOf(HyperFormula) - }) - - it('#buildFromSheet adds default sheet Sheet1', () => { - const engine = HyperFormula.buildFromArray([]) - - expect(engine.getAllSheetsDimensions()).toEqual({'Sheet1': {'height': 0, 'width': 0}}) - }) - - it('#buildFromSheet adds default sheet Sheet1, in different languages', () => { - HyperFormula.registerLanguage('plPL', plPL) - const engine = HyperFormula.buildFromArray([], {language: 'plPL'}) - - expect(engine.getAllSheetsDimensions()).toEqual({'Arkusz1': {'height': 0, 'width': 0}}) - }) - - it('#buildFromSheets accepts config', () => { - const config = {dateFormats: ['MM']} - const engine = HyperFormula.buildFromSheets({ - Sheet1: [], - Sheet2: [], - }, config) - - expect(engine.getConfig().dateFormats[0]).toBe('MM') - }) - - it('#buildFromSheet accepts config', () => { - const config = {dateFormats: ['MM']} - const engine = HyperFormula.buildFromArray([], config) - - expect(engine.getConfig().dateFormats[0]).toBe('MM') - }) - - it('should allow to create sheets with a delay', () => { - const sheetName = 'Sheet2' - const engine = HyperFormula.buildFromArray([[`=${sheetName}!A1`]]) - - engine.addSheet(sheetName) - const sheetId = engine.getSheetId(sheetName)! - engine.setSheetContent(sheetId, [['1']]) - engine.rebuildAndRecalculate() - - expect(engine.getCellValue(adr('A1', sheetId))).toBe(1) - expect(engine.getCellValue(adr('A1', 0))).toBe(1) - }) - - it('corrupted sheet definition', () => { - expect(() => { - HyperFormula.buildFromArray([ - [0, 1], - [2, 3], - null, // broken sheet - [6, 7] - ] as any) - }).toThrowError('Invalid arguments, expected an array of arrays.') - }) -}) - -describe('named expressions', () => { - it('buildEmpty', () => { - const engine = HyperFormula.buildEmpty({}, [{name: 'FALSE', expression: false}]) - engine.addSheet('sheet') - engine.setSheetContent(0, [['=FALSE']]) - expect(engine.getCellValue(adr('A1'))).toEqual(false) - }) - - it('buildFromArray', () => { - const engine = HyperFormula.buildFromArray([['=FALSE']], {}, [{name: 'FALSE', expression: false}]) - expect(engine.getCellValue(adr('A1'))).toEqual(false) - }) - - it('buildFromSheets', () => { - const engine = HyperFormula.buildFromSheets({sheet: [['=FALSE']]}, {}, [{name: 'FALSE', expression: false}]) - expect(engine.getCellValue(adr('A1'))).toEqual(false) - }) - - it('buildFromArray + scope', () => { - const engine = HyperFormula.buildFromArray([['=FALSE']], {}, [{name: 'FALSE', expression: false, scope: 0}]) - expect(engine.getCellValue(adr('A1'))).toEqual(false) - }) - - it('buildFromSheets + scope', () => { - const engine = HyperFormula.buildFromSheets({sheet: [['=FALSE']]}, {}, [{ - name: 'FALSE', - expression: false, - scope: 0 - }]) - expect(engine.getCellValue(adr('A1'))).toEqual(false) - }) -}) diff --git a/test/unit/cache-order.spec.ts b/test/unit/cache-order.spec.ts deleted file mode 100644 index 88b66b7f32..0000000000 --- a/test/unit/cache-order.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {HyperFormula} from '../../src' -import {adr} from './testUtils' - -describe('cache order invariance', () => { - it('should evaluate properly #1', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], ['=SUM(A1,B1:C1)', '=SUM(B1:C1)']]) - expect(engine.getCellValue(adr('A2'))).toEqual(6) - expect(engine.getCellValue(adr('B2'))).toEqual(5) - }) - - it('should evaluate properly #2', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], ['=SUM(B1:C1,A1)', '=SUM(B1:C1)']]) - expect(engine.getCellValue(adr('A2'))).toEqual(6) - expect(engine.getCellValue(adr('B2'))).toEqual(5) - }) - - it('should evaluate properly #3', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], ['=SUM(B1:C1)', '=SUM(A1,B1:C1)']]) - expect(engine.getCellValue(adr('A2'))).toEqual(5) - expect(engine.getCellValue(adr('B2'))).toEqual(6) - }) - - it('should evaluate properly #4', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], ['=SUM(B1:C1)', '=SUM(B1:C1,A1)']]) - expect(engine.getCellValue(adr('A2'))).toEqual(5) - expect(engine.getCellValue(adr('B2'))).toEqual(6) - }) -}) diff --git a/test/unit/column-index.spec.ts b/test/unit/column-index.spec.ts deleted file mode 100644 index b82f81fe5e..0000000000 --- a/test/unit/column-index.spec.ts +++ /dev/null @@ -1,844 +0,0 @@ -import {deepStrictEqual} from 'assert' -import {HyperFormula} from '../../src' -import {AbsoluteCellRange} from '../../src/AbsoluteCellRange' -import {CellError, ErrorType} from '../../src' -import {Config} from '../../src/Config' -import {DependencyGraph} from '../../src/DependencyGraph' -import {AddRowsTransformer} from '../../src/dependencyTransformers/AddRowsTransformer' -import {RemoveRowsTransformer} from '../../src/dependencyTransformers/RemoveRowsTransformer' -import {FunctionRegistry} from '../../src/interpreter/FunctionRegistry' -import {EmptyValue} from '../../src/interpreter/InterpreterValue' -import {SimpleRangeValue} from '../../src' -import {LazilyTransformingAstService} from '../../src/LazilyTransformingAstService' -import {ColumnIndex} from '../../src/Lookup/ColumnIndex' -import {NamedExpressions} from '../../src/NamedExpressions' -import {ColumnsSpan, RowsSpan} from '../../src/Span' -import {Statistics} from '../../src/statistics' -import {adr, detailedError, expectColumnIndexToMatchSheet} from './testUtils' -import { ErrorMessage } from '../../src/error-message' - -function buildEmptyIndex(transformingService: LazilyTransformingAstService, config: Config, statistics: Statistics): ColumnIndex { - const functionRegistry = new FunctionRegistry(config) - const namedExpression = new NamedExpressions() - const dependencyGraph = DependencyGraph.buildEmpty(transformingService, config, functionRegistry, namedExpression, statistics) - return new ColumnIndex(dependencyGraph, config, statistics) -} - -describe('ColumnIndex#add', () => { - const statistics = new Statistics() - const transformingService = new LazilyTransformingAstService(statistics) - - it('should add value to empty index', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('B5')) - - const columnMap = index.getColumnMap(0, 1) - - expect(columnMap.size).toBe(1) - - expect(columnMap.get(1)!.index[0]).toBe(4) - }) - - it('should keep values in sorted order', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A3')) - index.add(1, adr('A5')) - index.add(1, adr('A1')) - - const columnMap = index.getColumnMap(0, 0) - - expect(columnMap.size).toBe(1) - - expect(columnMap.get(1)!.index[0]).toBe(0) - }) - - it('should not store duplicates', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - index.add(1, adr('A5')) - index.add(1, adr('A5')) - index.add(1, adr('A1')) - index.add(1, adr('A1')) - - const columnMap = index.getColumnMap(0, 0) - - expect(columnMap.size).toBe(1) - - expect(columnMap.get(1)!.index.length).toBe(2) - }) - - it('should ignore CellErrors', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - const error = new CellError(ErrorType.DIV_BY_ZERO) - - index.add(error, adr('A1')) - - const columnMap = index.getColumnMap(0, 0) - expect(columnMap.size).toBe(0) - expect(columnMap.keys()).not.toContain(error) - }) - - it('should add values from SimpleRangeValue', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - const simpleRangeValue = SimpleRangeValue.onlyNumbers([[1, 2]]) - - index.add(simpleRangeValue, adr('A1')) - - const columnA = index.getColumnMap(0, 0) - const columnB = index.getColumnMap(0, 1) - - expect(columnA.size).toBe(1) - expect(columnB.size).toBe(1) - }) - - it('should handle strings correctly', () => { - const index = buildEmptyIndex(transformingService, new Config({ - caseSensitive: false, - accentSensitive: false - }), statistics) - index.add('a', adr('A1')) - index.add('A', adr('A2')) - index.add('ą', adr('A3')) - - // Some strings don't have a canonical form, so for them, the index is created as usual. - index.add('l', adr('A4')) - index.add('ł', adr('A5')) - index.add('t', adr('A6')) - index.add('ŧ', adr('A7')) - - const columnMap = index.getColumnMap(0, 0) - - expect(columnMap.get('a')!.index.length).toBe(3) - expect(columnMap.get('l')!.index.length).toBe(1) - expect(columnMap.get('ł')!.index.length).toBe(1) - expect(columnMap.get('t')!.index.length).toBe(1) - expect(columnMap.get('ŧ')!.index.length).toBe(1) - }) - - it('should ignore EmptyValue', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(EmptyValue, adr('A1')) - expect(index.getColumnMap(0, 0).size).toBe(0) - }) -}) - -describe('ColumnIndex change/remove', () => { - const statistics = new Statistics() - const transformingService = new LazilyTransformingAstService(statistics) - - it('should remove value from index', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - index.add(1, adr('A2')) - index.add(1, adr('A3')) - - index.remove(1, adr('A2')) - - - const valueIndex = index.getColumnMap(0, 0).get(1)! - expect(valueIndex.index.length).toBe(2) - expect(valueIndex.index).toContain(0) - expect(valueIndex.index).toContain(2) - }) - - it('should do nothing if passed value is undefined', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - index.add(1, adr('A2')) - index.add(1, adr('A3')) - - index.remove(undefined, adr('A2')) - - - const valueIndex = index.getColumnMap(0, 0).get(1)! - expect(valueIndex.index.length).toBe(3) - expect(valueIndex.index).toContain(0) - expect(valueIndex.index).toContain(1) - expect(valueIndex.index).toContain(2) - }) - - it('should change value in index', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - - index.change(1, 2, adr('A1')) - - expect(index.getColumnMap(0, 0).keys()).not.toContain(1) - - const valueIndex = index.getColumnMap(0, 0).get(2)! - expect(valueIndex.index).toContain(0) - }) - - it('should do nothing when changing to the same value', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - - const spyRemove = spyOn(index, 'remove') - const spyAdd = spyOn(index, 'add') - - index.change(1, 1, adr('A1')) - - expect(spyRemove).not.toHaveBeenCalled() - expect(spyAdd).not.toHaveBeenCalled() - }) - - it('should change range values', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - const range = SimpleRangeValue.onlyNumbers([ - [1, 2], - [3, 4], - ]) - index.add(range, adr('A1')) - deepStrictEqual(index.getColumnMap(0, 0), new Map([ - [1, {index: [0], version: 0}], - [3, {index: [1], version: 0}], - ])) - deepStrictEqual(index.getColumnMap(0, 1), new Map([ - [2, {index: [0], version: 0}], - [4, {index: [1], version: 0}], - ])) - - index.change(range, SimpleRangeValue.onlyNumbers([ - [5, 6], - [7, 8], - ]), adr('A1')) - - deepStrictEqual(index.getColumnMap(0, 0), new Map([ - [5, {index: [0], version: 0}], - [7, {index: [1], version: 0}], - ])) - deepStrictEqual(index.getColumnMap(0, 1), new Map([ - [6, {index: [0], version: 0}], - [8, {index: [1], version: 0}], - ])) - }) - - it('should ignore CellErrors', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - - const error = new CellError(ErrorType.DIV_BY_ZERO) - index.change(1, error, adr('A1')) - - expect(index.getColumnMap(0, 0).keys()).not.toContain(1) - expect(index.getColumnMap(0, 0).keys()).not.toContain(error) - }) - - it('should ignore EmptyValue', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - - index.change(1, EmptyValue, adr('A1')) - - expect(index.getColumnMap(0, 0).size).toBe(0) - }) -}) - -describe('ColumnIndex#addColumns', () => { - const statistics = new Statistics() - const transformingService = new LazilyTransformingAstService(statistics) - - it('should add column to index', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - - index.addColumns(ColumnsSpan.fromNumberOfColumns(0, 0, 1)) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([]) - expect(index.getValueIndex(0, 1, 1).index).toEqual([0]) - }) - - it('should add columns in the middle', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - index.add(1, adr('B1')) - index.add(1, adr('C1')) - - index.addColumns(ColumnsSpan.fromNumberOfColumns(0, 1, 2)) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([0]) - expect(index.getValueIndex(0, 3, 1).index).toEqual([0]) - expect(index.getValueIndex(0, 4, 1).index).toEqual([0]) - }) - - it('should add columns only in one sheet', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('B1')) - index.add(1, adr('B1', 1)) - - index.addColumns(ColumnsSpan.fromNumberOfColumns(0, 1, 2)) - - expect(index.getValueIndex(0, 1, 1).index).toEqual([]) - expect(index.getValueIndex(0, 3, 1).index).toEqual([0]) - expect(index.getValueIndex(1, 1, 1).index).toEqual([0]) - }) -}) - -describe('ColumnIndex#removeColumns', () => { - const statistics = new Statistics() - const transformingService = new LazilyTransformingAstService(statistics) - - it('should remove column', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - - index.removeColumns(ColumnsSpan.fromNumberOfColumns(0, 0, 1)) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([]) - }) - - it('should work when empty index', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - - index.removeColumns(ColumnsSpan.fromNumberOfColumns(0, 0, 1)) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([]) - }) - - it('should remove multiple columns in the middle', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - index.add(2, adr('B1')) - index.add(3, adr('C1')) - index.add(4, adr('D1')) - - index.removeColumns(ColumnsSpan.fromNumberOfColumns(0, 1, 2)) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([0]) - expect(index.getValueIndex(0, 1, 4).index).toEqual([0]) - expect(index.getValueIndex(0, 2, 3).index).toEqual([]) - expect(index.getValueIndex(0, 3, 4).index).toEqual([]) - }) - - it('should remove columns only in one sheet', () => { - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1', 0)) - index.add(1, adr('A1', 1)) - - index.removeColumns(ColumnsSpan.fromNumberOfColumns(0, 0, 1)) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([]) - expect(index.getValueIndex(1, 0, 1).index).toEqual([0]) - }) -}) - -describe('ColumnIndex#find', () => { - const stats = new Statistics() - const transformService = new LazilyTransformingAstService(stats) - - it('should find row number', () => { - const index = buildEmptyIndex(transformService, new Config(), stats) - - index.add(1, adr('A2')) - const row = index.find(1, SimpleRangeValue.onlyRange(new AbsoluteCellRange(adr('A1'), adr('A3')), undefined!), { ordering: 'asc', ifNoMatch: 'returnNotFound' }) - - expect(row).toBe(1) - }) - - it('should find the smallest row number for value if range not sorted', () => { - const index = buildEmptyIndex(transformService, new Config(), stats) - - index.add(1, adr('A4')) - index.add(1, adr('A10')) - const row = index.find(1, SimpleRangeValue.onlyRange(new AbsoluteCellRange(adr('A1'), adr('A20')), undefined!), { ordering: 'none', ifNoMatch: 'returnNotFound' }) - - expect(row).toBe(3) - }) - - it('should find the largest row number for value if range sorted ascending', () => { - const index = buildEmptyIndex(transformService, new Config(), stats) - - index.add(1, adr('A4')) - index.add(1, adr('A10')) - const row = index.find(1, SimpleRangeValue.onlyRange(new AbsoluteCellRange(adr('A1'), adr('A20')), undefined!), { ordering: 'asc', ifNoMatch: 'returnNotFound' }) - - expect(row).toBe(9) - }) -}) - -describe('ColumnIndex#addRows', () => { - it('should add row', () => { - const statistics = new Statistics() - const transformingService = new LazilyTransformingAstService(statistics) - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - index.add(2, adr('B3')) - - transformingService.addTransformation(new AddRowsTransformer(RowsSpan.fromNumberOfRows(0, 0, 1))) - transformingService.addTransformation(new AddRowsTransformer(RowsSpan.fromNumberOfRows(1, 0, 1))) - - index.ensureRecentData(0, 0, 1) - expect(index.getValueIndex(0, 0, 1).index).toEqual([1]) - index.ensureRecentData(0, 1, 2) - expect(index.getValueIndex(0, 1, 2).index).toEqual([3]) - }) - - it('should not shift row', () => { - const statistics = new Statistics() - const transformingService = new LazilyTransformingAstService(statistics) - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - - transformingService.addTransformation(new AddRowsTransformer(RowsSpan.fromNumberOfRows(0, 1, 1))) - index.ensureRecentData(0, 0, 1) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([0]) - }) - - it('should add rows in the middle', () => { - const statistics = new Statistics() - const transformingService = new LazilyTransformingAstService(statistics) - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - index.add(1, adr('A2')) - index.add(1, adr('A3')) - index.add(1, adr('A4')) - - transformingService.addTransformation(new AddRowsTransformer(RowsSpan.fromNumberOfRows(0, 1, 2))) - index.ensureRecentData(0, 0, 1) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([0, 3, 4, 5]) - }) - - it('should add rows for all columns', () => { - const statistics = new Statistics() - const transformingService = new LazilyTransformingAstService(statistics) - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A2')) - index.add(1, adr('B2')) - index.add(2, adr('C2')) - - transformingService.addTransformation(new AddRowsTransformer(RowsSpan.fromNumberOfRows(0, 1, 2))) - index.ensureRecentData(0, 0, 1) - index.ensureRecentData(0, 1, 1) - index.ensureRecentData(0, 2, 2) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([3]) - expect(index.getValueIndex(0, 1, 1).index).toEqual([3]) - expect(index.getValueIndex(0, 2, 2).index).toEqual([3]) - }) - - it('should add rows for different values', () => { - const statistics = new Statistics() - const transformingService = new LazilyTransformingAstService(statistics) - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - index.add(2, adr('A2')) - index.add(3, adr('A3')) - index.add(4, adr('B1')) - index.add(4, adr('B5')) - - transformingService.addTransformation(new AddRowsTransformer(RowsSpan.fromNumberOfRows(0, 1, 2))) - index.ensureRecentData(0, 0, 1) - index.ensureRecentData(0, 0, 2) - index.ensureRecentData(0, 0, 3) - index.ensureRecentData(0, 1, 4) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([0]) - expect(index.getValueIndex(0, 0, 2).index).toEqual([3]) - expect(index.getValueIndex(0, 0, 3).index).toEqual([4]) - expect(index.getValueIndex(0, 1, 4).index).toEqual([0, 6]) - }) -}) - -describe('ColumnIndex#removeRows', () => { - it('should remove rows', () => { - const statistics = new Statistics() - const transformingService = new LazilyTransformingAstService(statistics) - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - - transformingService.addTransformation(new RemoveRowsTransformer(RowsSpan.fromNumberOfRows(0, 0, 1))) - index.ensureRecentData(0, 0, 1) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([]) - }) - - it('should remove rows in the middle', () => { - const statistics = new Statistics() - const transformingService = new LazilyTransformingAstService(statistics) - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - index.add(1, adr('A2')) - index.add(1, adr('A3')) - index.add(1, adr('A4')) - - transformingService.addTransformation(new RemoveRowsTransformer(RowsSpan.fromNumberOfRows(0, 1, 2))) - index.ensureRecentData(0, 0, 1) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([0, 1]) - }) - - it('should remove rows in every column', () => { - const statistics = new Statistics() - const transformingService = new LazilyTransformingAstService(statistics) - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A2')) - index.add(1, adr('B2')) - index.add(1, adr('C2')) - - transformingService.addTransformation(new RemoveRowsTransformer(RowsSpan.fromNumberOfRows(0, 0, 1))) - index.ensureRecentData(0, 0, 1) - index.ensureRecentData(0, 1, 1) - index.ensureRecentData(0, 2, 1) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([0]) - expect(index.getValueIndex(0, 1, 1).index).toEqual([0]) - expect(index.getValueIndex(0, 2, 1).index).toEqual([0]) - }) - - it('should remove rows for different values', () => { - const statistics = new Statistics() - const transformingService = new LazilyTransformingAstService(statistics) - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A2')) - index.add(2, adr('A3')) - index.add(3, adr('A4')) - index.add(4, adr('B3')) - - transformingService.addTransformation(new RemoveRowsTransformer(RowsSpan.fromNumberOfRows(0, 0, 2))) - index.ensureRecentData(0, 0, 1) - index.ensureRecentData(0, 0, 2) - index.ensureRecentData(0, 0, 3) - index.ensureRecentData(0, 1, 4) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([]) - expect(index.getValueIndex(0, 0, 2).index).toEqual([0]) - expect(index.getValueIndex(0, 0, 3).index).toEqual([1]) - expect(index.getValueIndex(0, 1, 4).index).toEqual([0]) - }) - - it('should remove rows only in one sheet', () => { - const statistics = new Statistics() - const transformingService = new LazilyTransformingAstService(statistics) - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A2')) - index.add(1, adr('A2', 1)) - - transformingService.addTransformation(new RemoveRowsTransformer(RowsSpan.fromNumberOfRows(0, 0, 1))) - index.ensureRecentData(0, 0, 1) - index.ensureRecentData(1, 0, 1) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([0]) - expect(index.getValueIndex(1, 0, 1).index).toEqual([1]) - }) - - it('should remove proper rows', () => { - const statistics = new Statistics() - const transformingService = new LazilyTransformingAstService(statistics) - const index = buildEmptyIndex(transformingService, new Config(), statistics) - index.add(1, adr('A1')) - index.add(1, adr('A3')) - index.add(1, adr('A4')) - index.add(1, adr('A6')) - - transformingService.addTransformation(new RemoveRowsTransformer(RowsSpan.fromNumberOfRows(0, 1, 4))) - index.ensureRecentData(0, 0, 1) - - expect(index.getValueIndex(0, 0, 1).index).toEqual([0, 1]) - }) -}) - -describe('ColumnIndex - lazy crud operations', () => { - it('should add rows only in specific column after find', () => { - const stats = new Statistics() - const transformService = new LazilyTransformingAstService(stats) - const index = buildEmptyIndex(transformService, new Config(), stats) - index.add(1, adr('A1')) - index.add(1, adr('B1')) - - transformService.addTransformation(new AddRowsTransformer(RowsSpan.fromNumberOfRows(0, 0, 1))) - - const rowA = index.find(1, SimpleRangeValue.onlyRange(new AbsoluteCellRange(adr('A1'), adr('A2')), undefined!), { ordering: 'asc', ifNoMatch: 'returnNotFound' }) - expect(rowA).toEqual(1) - expect(index.getValueIndex(0, 0, 1).index).toEqual([1]) - expect(index.getValueIndex(0, 1, 1).index).toEqual([0]) - - const rowB = index.find(1, SimpleRangeValue.onlyRange(new AbsoluteCellRange(adr('B1'), adr('B2')), undefined!), { ordering: 'asc', ifNoMatch: 'returnNotFound' }) - expect(rowB).toEqual(1) - expect(index.getValueIndex(0, 0, 1).index).toEqual([1]) - expect(index.getValueIndex(0, 1, 1).index).toEqual([1]) - }) - - it('should add rows only for specific value after find', () => { - const stats = new Statistics() - const transformService = new LazilyTransformingAstService(stats) - const index = buildEmptyIndex(transformService, new Config(), stats) - index.add(1, adr('A1')) - index.add(2, adr('A2')) - - transformService.addTransformation(new AddRowsTransformer(RowsSpan.fromNumberOfRows(0, 0, 1))) - - const row1 = index.find(1, SimpleRangeValue.onlyRange(new AbsoluteCellRange(adr('A1'), adr('A3')), undefined!), { ordering: 'asc', ifNoMatch: 'returnNotFound' }) - expect(row1).toEqual(1) - expect(index.getValueIndex(0, 0, 1).index).toEqual([1]) - expect(index.getValueIndex(0, 0, 2).index).toEqual([1]) - - const row2 = index.find(2, SimpleRangeValue.onlyRange(new AbsoluteCellRange(adr('A1'), adr('A3')), undefined!), { ordering: 'asc', ifNoMatch: 'returnNotFound' }) - expect(row2).toEqual(2) - expect(index.getValueIndex(0, 0, 1).index).toEqual([1]) - expect(index.getValueIndex(0, 0, 2).index).toEqual([2]) - }) -}) - -describe('Arrays', () => { - it('should update column index with array values', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, '=-A1:B1'], - ], {useArrayArithmetic: true, useColumnIndex: true}) - - expectColumnIndexToMatchSheet([ - [1, 2, -1, -2] - ], engine) - }) - - it('should remove values from index when REF', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, '=-A1:B1'], - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.setCellContents(adr('D1'), [['foo']]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.SPILL, ErrorMessage.NoSpaceForArrayResult)) - - expectColumnIndexToMatchSheet([ - [1, 2, null, 'foo'] - ], engine) - }) - - it('should remove values from index when replacing array with scalar', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, '=-A1:B1'], - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.setCellContents(adr('C1'), [['foo']]) - - expectColumnIndexToMatchSheet([ - [1, 2, 'foo'] - ], engine) - }) - - it('should remove values when replacing array with smaller one', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, '=-A1:B1'], - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.setCellContents(adr('C1'), [['=2*A1:A1']]) - - expectColumnIndexToMatchSheet([ - [1, 2, 2] - ], engine) - }) - - it('should remove values when replacing array with parsing error', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, '=-A1:B1'], - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.setCellContents(adr('C1'), [['=SUM(']]) - - expectColumnIndexToMatchSheet([ - [1, 2] - ], engine) - }) - - it('should update index when replacing array with another one', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, '=-A1:B1'], - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.setCellContents(adr('C1'), [['=2*A1:B1']]) - - expectColumnIndexToMatchSheet([ - [1, 2, 2, 4] - ], engine) - }) - - it('should move array values when adding rows above array', () => { - const engine = HyperFormula.buildFromArray([ - ['=-B3:B4'], - [null, 'foo'], - [null, 1], - [null, 2], - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.addRows(0, [0, 1]) - - expectColumnIndexToMatchSheet([ - [null], - [-1], - [-2, 'foo'], - [null, 1], - [null, 2], - ], engine) - }) - - it('should not move array values when adding rows', () => { - const engine = HyperFormula.buildFromArray([ - ['=-B3:B4'], - [null, 'foo'], - [-2, 1], - [-1, 2], - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.addRows(0, [1, 1]) - - expectColumnIndexToMatchSheet([ - [-1], - [-2], - [null, 'foo'], - [-2, 1], - [-1, 2], - ], engine) - }) - - it('should move array values when removing rows above array', () => { - const engine = HyperFormula.buildFromArray([ - [], - ['=-B3:B4'], - [null, 1], - [null, 2], - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.removeRows(0, [0, 1]) - - expectColumnIndexToMatchSheet([ - [-1], - [-2, 1], - [null, 2] - ], engine) - }) - - it('should not move array values when removing rows', () => { - const engine = HyperFormula.buildFromArray([ - ['=-B4:B5'], - [null], - [null, 'foo'], - [-2, 1], - [-1, 2], - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.removeRows(0, [1, 1]) - - expectColumnIndexToMatchSheet([ - [-1], - [-2, 'foo'], - [-2, 1], - [-1, 2], - ], engine) - }) - - it('should remove array values from index when removing row with left corner', () => { - const engine = HyperFormula.buildFromArray([ - ['=-B2:B3'], - [null, 1], - [null, 2], - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.removeRows(0, [0, 1]) - - expectColumnIndexToMatchSheet([ - [null, 1], - [null, 2] - ], engine) - }) - - it('should remove array values from index when REF after removing rows', () => { - const engine = HyperFormula.buildFromArray([ - ['=-B2:B3'], - [null, 1], - [3, 2], - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.removeRows(0, [1, 1]) - - expectColumnIndexToMatchSheet([ - [], - [3, 2] - ], engine) - }) - - it('should move array values when adding columns before array', () => { - const engine = HyperFormula.buildFromArray([ - ['=-C2:D2'], - [null, 'foo', 1, 2] - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.addColumns(0, [0, 1]) - - expectColumnIndexToMatchSheet([ - [null, -1, -2], - [null, null, 'foo', 1, 2] - ], engine) - }) - - it('should not move array values when adding columns', () => { - const engine = HyperFormula.buildFromArray([ - ['=-C2:E2', null, null, -3, -2, -1], - [null, 'foo', 1, 2, 3] - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.addColumns(0, [1, 1]) - - expectColumnIndexToMatchSheet([ - [-1, -2, -3, null, -3, -2, -1], - [null, null, 'foo', 1, 2, 3] - ], engine) - }) - - it('should move array values when removing columns before array', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=-C2:D2'], - [null, null, 1, 2] - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.removeColumns(0, [0, 1]) - - expectColumnIndexToMatchSheet([ - [-1, -2], - [null, 1, 2] - ], engine) - }) - - it('should not move array values when removing columns', () => { - const engine = HyperFormula.buildFromArray([ - ['=-D2:E2', null, null, -2, -1], - [-2, 'foo', null, 1, 2] - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.removeColumns(0, [1, 1]) - - expectColumnIndexToMatchSheet([ - [-1, -2, -2, -1], - [-2, null, 1, 2] - ], engine) - }) - - it('should remove array values from index when removing column with left corner', () => { - const engine = HyperFormula.buildFromArray([ - ['=-B2:C2'], - [null, 1, 2], - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.removeColumns(0, [0, 1]) - - expectColumnIndexToMatchSheet([ - [], - [1, 2], - ], engine) - }) - - it('should remove array values from index when REF after removing columns', () => { - const engine = HyperFormula.buildFromArray([ - ['=-B2:C2', null, 1], - [2, null, 3] - ], {useArrayArithmetic: true, useColumnIndex: true}) - - engine.removeColumns(0, [1, 1]) - - expectColumnIndexToMatchSheet([ - [null, 1], - [2, 3] - ], engine) - }) -}) diff --git a/test/unit/column-range.spec.ts b/test/unit/column-range.spec.ts deleted file mode 100644 index a538063c56..0000000000 --- a/test/unit/column-range.spec.ts +++ /dev/null @@ -1,97 +0,0 @@ -import {HyperFormula} from '../../src' -import {AbsoluteCellRange} from '../../src/AbsoluteCellRange' -import {adr, colEnd, colStart, extractColumnRange} from './testUtils' -import {RangeVertex} from '../../src/DependencyGraph' - -describe('Column ranges', () => { - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=SUM(A:B)'] - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(3) - }) - - it('should create correct edges for infinite range when building graph', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(C:D)', '=SUM(C5:D6)'], - ]) - - const cd = engine.rangeMapping.getRangeVertex(colStart('C'), colEnd('D')) as RangeVertex - - const c5 = engine.dependencyGraph.fetchCell(adr('C5')) - const c6 = engine.dependencyGraph.fetchCell(adr('C6')) - const d5 = engine.dependencyGraph.fetchCell(adr('D5')) - const d6 = engine.dependencyGraph.fetchCell(adr('D6')) - - expect(engine.graph.existsEdge(c5, cd)).toBe(true) - expect(engine.graph.existsEdge(c6, cd)).toBe(true) - expect(engine.graph.existsEdge(d5, cd)).toBe(true) - expect(engine.graph.existsEdge(d6, cd)).toBe(true) - }) - - it('should create correct edges for infinite range', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(C:E)'], - ['=SUM(D:G)'], - ]) - - engine.setCellContents(adr('B1'), '=SUM(D42:H42)') - - const ce = engine.rangeMapping.getRangeVertex(colStart('C'), colEnd('E')) as RangeVertex - const dg = engine.rangeMapping.getRangeVertex(colStart('D'), colEnd('G')) as RangeVertex - - const d42 = engine.dependencyGraph.fetchCell(adr('D42')) - const e42 = engine.dependencyGraph.fetchCell(adr('E42')) - const f42 = engine.dependencyGraph.fetchCell(adr('F42')) - const g42 = engine.dependencyGraph.fetchCell(adr('G42')) - const h42 = engine.dependencyGraph.fetchCell(adr('H42')) - - expect(engine.graph.existsEdge(d42, ce)).toBe(true) - expect(engine.graph.existsEdge(e42, ce)).toBe(true) - expect(engine.graph.existsEdge(f42, ce)).toBe(false) - - expect(engine.graph.existsEdge(d42, dg)).toBe(true) - expect(engine.graph.existsEdge(e42, dg)).toBe(true) - expect(engine.graph.existsEdge(f42, dg)).toBe(true) - expect(engine.graph.existsEdge(g42, dg)).toBe(true) - expect(engine.graph.existsEdge(h42, dg)).toBe(false) - }) - - it('should clear column range set in graph when removing column', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(B:B)'] - ]) - - engine.removeColumns(0, [1, 1]) - - expect(engine.graph.getInfiniteRanges().length).toBe(0) - }) - - it('should not move infinite range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '', '', '=SUM(A:B)'] - ]) - expect(engine.getCellValue(adr('E1'))).toEqual(3) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 2, 1), adr('C1')) - - expect(engine.getCellValue(adr('E1'))).toEqual(0) - const range = extractColumnRange(engine, adr('E1')) - expect(range.start).toEqual(colStart('A')) - expect(range.end).toEqual(colEnd('B')) - }) - - it('should correctly handle infinite column ranges when setting cell values (line 890)', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(C:D)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - - engine.setCellContents(adr('C5'), 10) - engine.setCellContents(adr('D5'), 20) - - expect(engine.getCellValue(adr('A1'))).toBe(30) - }) -}) diff --git a/test/unit/computation-suspension.spec.ts b/test/unit/computation-suspension.spec.ts deleted file mode 100644 index 836e791370..0000000000 --- a/test/unit/computation-suspension.spec.ts +++ /dev/null @@ -1,227 +0,0 @@ -import {EvaluationSuspendedError, ExportedCellChange, HyperFormula} from '../../src' -import {AbsoluteCellRange} from '../../src/AbsoluteCellRange' -import {CellType} from '../../src/Cell' -import {adr} from './testUtils' - -describe('Evaluation suspension', () => { - it('by default, evaluation is automatic', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=A1'], - ]) - - engine.setCellContents(adr('C1'), [['=B1']]) - - expect(engine.getCellValue(adr('C1'))).toBe(2) - }) - - it('when evaluation is stopped, getting cell values is forbidden', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '42'], - ]) - - engine.suspendEvaluation() - - expect(() => { - engine.getCellValue(adr('C1')) - }).toThrow(new EvaluationSuspendedError()) - expect(() => { - engine.getSheetValues(0) - }).toThrow(new EvaluationSuspendedError()) - expect(() => { - engine.getAllSheetsValues() - }).toThrow(new EvaluationSuspendedError()) - expect(() => { - engine.getRangeValues(AbsoluteCellRange.spanFrom(adr('A1'), 1, 2)) - }).toThrow(new EvaluationSuspendedError()) - expect(() => { - engine.getNamedExpressionValue('FOO') - }).toThrow(new EvaluationSuspendedError()) - }) - - it('when evaluation is stopped, getting serialized cell values is forbidden', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '42'], - ]) - - engine.suspendEvaluation() - - expect(() => { - engine.getCellSerialized(adr('C1')) - }).toThrow(new EvaluationSuspendedError()) - expect(() => { - engine.getSheetSerialized(0) - }).toThrow(new EvaluationSuspendedError()) - expect(() => { - engine.getAllSheetsSerialized() - }).toThrow(new EvaluationSuspendedError()) - expect(() => { - engine.getRangeSerialized(AbsoluteCellRange.spanFrom(adr('A1'), 1, 2)) - }).toThrow(new EvaluationSuspendedError()) - }) - - it('when evaluation is stopped, getting cell value types is forbidden', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '42'], - ]) - - engine.suspendEvaluation() - - expect(() => { - engine.getCellValueType(adr('C1')) - }).toThrow(new EvaluationSuspendedError()) - }) - - it('when evaluation is stopped, getting cell types is possible', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '42'], - ]) - - engine.suspendEvaluation() - engine.setCellContents(adr('C1'), [['=A1+78']]) - - expect(engine.getCellType(adr('C1'))).toEqual(CellType.FORMULA) - expect(engine.doesCellHaveSimpleValue(adr('C1'))).toBe(false) - expect(engine.doesCellHaveFormula(adr('C1'))).toBe(true) - expect(engine.isCellEmpty(adr('C1'))).toBe(false) - expect(engine.isCellPartOfArray(adr('C1'))).toBe(false) - }) - - it('when evaluation is stopped, getting cell formulas is possible', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=A1+42'], - ]) - engine.suspendEvaluation() - engine.setCellContents(adr('C1'), [['=A1+78']]) - - expect(engine.getCellFormula(adr('C1'))).toEqual('=A1+78') - expect(engine.getSheetFormulas(0)).toEqual([[undefined, undefined, '=A1+78']]) - expect(engine.getAllSheetsFormulas()).toEqual({Sheet1: [[undefined, undefined, '=A1+78']]}) - expect(engine.getRangeFormulas(AbsoluteCellRange.spanFrom(adr('A1'), 3, 1))).toEqual([[undefined, undefined, '=A1+78']]) - }) - - it('formulas are rebuild even if evaluation is suspended', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=A2+42'], - ['42'] - ]) - engine.suspendEvaluation() - - engine.addRows(0, [1, 1]) - - expect(engine.getCellFormula(adr('C1'))).toEqual('=A3+42') - }) - - it('resuming evaluation', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=A1'], - ]) - engine.suspendEvaluation() - engine.setCellContents(adr('C1'), [['=B1']]) - - const changes = engine.resumeEvaluation() - - expect(engine.getCellValue(adr('C1'))).toBe(2) - expect(changes).toContainEqual(new ExportedCellChange(adr('C1'), 2)) - }) - - it('#isEvaluationSuspended when evaluation is suspended', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=A1'], - ]) - engine.suspendEvaluation() - - expect(engine.isEvaluationSuspended()).toBe(true) - }) - - it('#isEvaluationSuspended when evaluation is resumed', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=A1'], - ]) - - expect(engine.isEvaluationSuspended()).toBe(false) - }) - - describe('clipboard operations depend on values, so they are forbidden', () => { - it('copy', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=A1'], - ]) - engine.suspendEvaluation() - - expect(() => { - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - }).toThrow(new EvaluationSuspendedError()) - }) - - it('cut', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=A1'], - ]) - engine.suspendEvaluation() - - expect(() => { - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - }).toThrow(new EvaluationSuspendedError()) - }) - - it('paste', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=A1'], - ]) - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - engine.suspendEvaluation() - - expect(() => { - engine.paste(adr('A3')) - }).toThrow(new EvaluationSuspendedError()) - }) - }) - - it('undo-redo works when computation suspended', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=A2+42'], - ['42'] - ]) - engine.suspendEvaluation() - engine.addRows(0, [1, 1]) - - engine.undo() - - expect(engine.getCellFormula(adr('C1'))).toEqual('=A2+42') - }) - - it('#1291 - neighboring cell changes of an array formula should be emitted', () => { - const engine = HyperFormula.buildFromArray([[null, null, null]], { useArrayArithmetic: true }) - engine.suspendEvaluation() - - engine.setCellContents(adr('A1'), '={1;2;3}') - engine.setCellContents(adr('B1'), '4') - engine.setCellContents(adr('C1'), '5') - - const changes = engine.resumeEvaluation() - expect(changes.length).toEqual(5) - expect(changes).toContainEqual(new ExportedCellChange(adr('A1'), 1)) - expect(changes).toContainEqual(new ExportedCellChange(adr('A2'), 2)) - expect(changes).toContainEqual(new ExportedCellChange(adr('A3'), 3)) - expect(changes).toContainEqual(new ExportedCellChange(adr('B1'), 4)) - expect(changes).toContainEqual(new ExportedCellChange(adr('C1'), 5)) - }) - - it('allows to update cell content of a not computed formula cell (#1194)', () => { - const hf = HyperFormula.buildFromArray([], { - licenseKey: 'gpl-v3' - }) - - hf.suspendEvaluation() - hf.setCellContents(adr('A1'), '=42') - hf.setCellContents(adr('A1'), 42) - hf.setCellContents(adr('A1'), '=42') - hf.setCellContents(adr('A1'), null) - hf.setCellContents(adr('A1'), '=42') - hf.setCellContents(adr('A1'), '==') - hf.setCellContents(adr('A1'), '=42') - hf.setCellContents(adr('A1'), '=42') - hf.resumeEvaluation() - expect(hf.getCellValue(adr('A1'))).toBe(42) - }) -}) diff --git a/test/unit/config.spec.ts b/test/unit/config.spec.ts deleted file mode 100644 index 3c79abed00..0000000000 --- a/test/unit/config.spec.ts +++ /dev/null @@ -1,423 +0,0 @@ -import {ErrorType, HyperFormula, MissingTranslationError} from '../../src' -import {Config} from '../../src/Config' -import {enGB, plPL} from '../../src/i18n/languages' -import {EmptyValue, NumberType} from '../../src/interpreter/InterpreterValue' -import {adr, unregisterAllLanguages} from './testUtils' -import {CellValueNoNumber} from '../../src/Cell' -import {UIElement} from '../../src/i18n' - -describe('Config', () => { - beforeEach(() => { - unregisterAllLanguages() - HyperFormula.registerLanguage(plPL.langCode, plPL) - HyperFormula.registerLanguage(enGB.langCode, enGB) - }) - - it('works', () => { - const config = new Config({language: 'plPL'}) - - expect(config.language).toBe('plPL') - }) - - it('has some defaults', () => { - const config = new Config() - - expect(config.language).toBe(Config.defaultConfig.language) - }) - - it('can translate functions', () => { - const config = new Config({language: 'plPL'}) - - expect(config.translationPackage.getFunctionTranslation('SUM')).toEqual('SUMA') - }) - - it('validation: boolean params', () => { - // eslint-disable-next-line - // @ts-ignore - expect(() => new Config({ignorePunctuation: 1})).toThrowError('Expected value of type: boolean for config parameter: ignorePunctuation') - // eslint-disable-next-line - // @ts-ignore - expect(() => new Config({accentSensitive: 'abcd'})).toThrowError('Expected value of type: boolean for config parameter: accentSensitive') - // eslint-disable-next-line - // @ts-ignore - expect(() => new Config({caseSensitive: 'abcd'})).toThrowError('Expected value of type: boolean for config parameter: caseSensitive') - // eslint-disable-next-line - // @ts-ignore - expect(() => new Config({smartRounding: []})).toThrowError('Expected value of type: boolean for config parameter: smartRounding') - // eslint-disable-next-line - // @ts-ignore - expect(() => new Config({useColumnIndex: Symbol()})).toThrowError('Expected value of type: boolean for config parameter: useColumnIndex') - // eslint-disable-next-line - // @ts-ignore - expect(() => new Config({leapYear1900: () => 1})).toThrowError('Expected value of type: boolean for config parameter: leapYear1900') - }) - - it('validation: number params', () => { - // eslint-disable-next-line - // @ts-ignore - expect(() => new Config({nullYear: true})).toThrowError('Expected value of type: number for config parameter: nullYear') - // eslint-disable-next-line - // @ts-ignore - expect(() => new Config({precisionRounding: /abcd/})).toThrowError('Expected value of type: number for config parameter: precisionRounding') - // eslint-disable-next-line - // @ts-ignore - expect(() => new Config({precisionEpsilon: {}})).toThrowError('Expected value of type: number for config parameter: precisionEpsilon') - }) - - it('validation: string params', () => { - // eslint-disable-next-line - // @ts-ignore - expect(() => new Config({functionArgSeparator: 123})).toThrowError('Expected value of type: string for config parameter: functionArgSeparator') - // eslint-disable-next-line - // @ts-ignore - expect(() => new Config({localeLang: EmptyValue})).toThrowError('Expected value of type: string for config parameter: localeLang') - }) - - it('validation: function params', () => { - // eslint-disable-next-line - // @ts-ignore - expect(() => new Config({parseDateTime: true})).toThrowError('Expected value of type: function for config parameter: parseDateTime') - // eslint-disable-next-line - // @ts-ignore - expect(() => new Config({stringifyDateTime: 1})).toThrowError('Expected value of type: function for config parameter: stringifyDateTime') - }) - - it('validation: other params', () => { - expect(() => new Config({ - // eslint-disable-next-line - // @ts-ignore - nullDate: {year: 123, month: 123, day: true} - })).toThrowError('Expected value of type: IDate for config parameter: nullDate') - // eslint-disable-next-line - // @ts-ignore - expect(() => new Config({dateFormats: {}})).toThrowError('Expected value of type: array for config parameter: dateFormats') - // eslint-disable-next-line - // @ts-ignore - expect(() => new Config({caseFirst: 'abcd'})).toThrowError('Expected one of \'upper\' \'lower\' \'false\' for config parameter: caseFirst') - }) - - it('should return the specified context', () => { - const context ={a: 1, b: 2} - const config = new Config({context}) - expect(config.context).toBe(context) - }) - - it('should throw error when Function Translation cannot be found', () => { - const config = new Config() - const functionName = '0123456ABCDEFGH' - expect(() => { - config.translationPackage.getFunctionTranslation(functionName) - }).toThrow(new MissingTranslationError(`functions.${functionName}`)) - }) - - it('should throw error when Error Translation cannot be found', () => { - const config = new Config() - const errorType = '0123456ABCDEFGH' - expect(() => { - config.translationPackage.getErrorTranslation(errorType as ErrorType) - }).toThrow(new MissingTranslationError(`errors.${errorType}`)) - }) - - it('should throw error when UI Translation cannot be found', () => { - const config = new Config() - const uiElement = '0123456ABCDEFGH' - expect(() => { - config.translationPackage.getUITranslation(uiElement as UIElement) - }).toThrow(new MissingTranslationError(`ui.${uiElement}`)) - }) - - it('should throw error when there is a conflict between separators', () => { - expect(() => { - new Config({decimalSeparator: ',', functionArgSeparator: ',', thousandSeparator: ' '}) - }).toThrowError('Config initialization failed. Parameters in conflict: [decimalSeparator,functionArgSeparator]') - expect(() => { - new Config({decimalSeparator: ',', functionArgSeparator: ';', thousandSeparator: ','}) - }).toThrowError('Config initialization failed. Parameters in conflict: [decimalSeparator,thousandSeparator]') - expect(() => { - new Config({decimalSeparator: '.', functionArgSeparator: ',', thousandSeparator: ','}) - }).toThrowError('Config initialization failed. Parameters in conflict: [functionArgSeparator,thousandSeparator]') - expect(() => { - new Config({decimalSeparator: ',', functionArgSeparator: ',', thousandSeparator: ','}) - }).toThrowError('Config initialization failed. Parameters in conflict: [decimalSeparator,functionArgSeparator,thousandSeparator]') - expect(() => { - new Config({arrayColumnSeparator: ';', arrayRowSeparator: ';'}) - }).toThrowError('Config initialization failed. Parameters in conflict: [arrayColumnSeparator,arrayRowSeparator]') - }) - - it('should throw error when currency symbol is empty', () => { - expect(() => { - new Config({currencySymbol: ['']}) - }).toThrowError('Config parameter currencySymbol cannot be empty.') - }) - - it('should throw error when currency symbol is not a string', () => { - expect(() => { - new Config({currencySymbol: [ 42 as unknown as string ]}) - }).toThrowError('Expected value of type: string[] for config parameter: currencySymbol') - }) - - it('should throw error when currency symbol is not an array', () => { - expect(() => { - // eslint-disable-next-line - // @ts-ignore - new Config({currencySymbol: '$'}) - }).toThrowError('Expected value of type: array for config parameter: currencySymbol') - }) - - it('should throw error when decimal separator is not correct', () => { - expect(() => { - // eslint-disable-next-line - // @ts-ignore - new Config({decimalSeparator: ';'}) - }).toThrowError('Expected one of \'.\' \',\' for config parameter: decimalSeparator') - }) - - it('should throw error when thousand separator is not correct', () => { - expect(() => { - // eslint-disable-next-line - // @ts-ignore - new Config({thousandSeparator: ';'}) - }).toThrowError('Expected one of \'\' \',\' \' \' \'.\' for config parameter: thousandSeparator') - }) - - it('should throw error when matrix row separator is not correct', () => { - expect(() => { - // eslint-disable-next-line - // @ts-ignore - new Config({arrayRowSeparator: ','}) - }).toThrowError('Expected one of \';\' \'|\' for config parameter: arrayRowSeparator') - }) - - it('should throw error when matrix columns separator is not correct', () => { - expect(() => { - // eslint-disable-next-line - // @ts-ignore - new Config({arrayColumnSeparator: '|'}) - }).toThrowError('Expected one of \',\' \';\' for config parameter: arrayColumnSeparator') - }) - - it('#undoLimit validation', () => { - expect(() => new Config({undoLimit: 0})).not.toThrowError() - expect(() => new Config({undoLimit: 42})).not.toThrowError() - expect(() => new Config({undoLimit: Infinity})).not.toThrowError() - expect(() => new Config({undoLimit: -1})).toThrowError('Config parameter undoLimit should be at least 0') - }) - - it('#precisionEpsilon', () => { - expect(() => new Config({precisionEpsilon: 0})).not.toThrowError() - expect(() => new Config({precisionEpsilon: 42})).not.toThrowError() - expect(() => new Config({precisionEpsilon: Infinity})).not.toThrowError() - expect(() => new Config({precisionEpsilon: -1})).toThrowError('Config parameter precisionEpsilon should be at least 0') - }) - - it('#precisionRounding', () => { - expect(() => new Config({precisionRounding: 0})).not.toThrowError() - expect(() => new Config({precisionRounding: 42})).not.toThrowError() - expect(() => new Config({precisionRounding: Infinity})).not.toThrowError() - expect(() => new Config({precisionRounding: -1})).toThrowError('Config parameter precisionRounding should be at least 0') - }) - - it('#maxRows', () => { - expect(() => new Config({maxRows: 1})).not.toThrowError() - expect(() => new Config({maxRows: 42})).not.toThrowError() - expect(() => new Config({maxRows: Infinity})).not.toThrowError() - expect(() => new Config({maxRows: 0})).toThrowError('Config parameter maxRows should be at least 1') - }) - - it('#maxColumns', () => { - expect(() => new Config({maxColumns: 1})).not.toThrowError() - expect(() => new Config({maxColumns: 42})).not.toThrowError() - expect(() => new Config({maxColumns: Infinity})).not.toThrowError() - expect(() => new Config({maxColumns: 0})).toThrowError('Config parameter maxColumns should be at least 1') - }) - - it('#nullYear', () => { - expect(() => new Config({nullYear: -1})).toThrowError('Config parameter nullYear should be at least 0') - expect(() => new Config({nullYear: 0})).not.toThrowError() - expect(() => new Config({nullYear: 42})).not.toThrowError() - expect(() => new Config({nullYear: 100})).not.toThrowError() - expect(() => new Config({nullYear: 101})).toThrowError('Config parameter nullYear should be at most 100') - }) - - describe('#dateFormats', () => { - it('should use the data formats provided in config param', () => { - const dateFormats = ['DD/MM/YYYY'] - const engine = HyperFormula.buildFromArray([ - ['1'], - ['01/03/2022'], - ['2022/01/01'], - ], { dateFormats }) - - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(NumberType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('A2'))).toEqual(NumberType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('A3'))).toEqual(CellValueNoNumber.STRING) - }) - - it('should parse the dates with different separators', () => { - const dateFormats = ['DD/MM/YYYY'] - const engine = HyperFormula.buildFromArray([[ - '01/03/2022', - '01-03-2022', - '01 03 2022', - '01.03.2022', - '01/03-2022', - '01 03.2022', - '01 03/2022', - '01.03-2022', - ]], { dateFormats }) - - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(NumberType.NUMBER_DATE) - expect(engine.getCellValueFormat(adr('A1'))).toEqual('DD/MM/YYYY') - expect(engine.getCellValueDetailedType(adr('B1'))).toEqual(NumberType.NUMBER_DATE) - expect(engine.getCellValueFormat(adr('B1'))).toEqual('DD/MM/YYYY') - expect(engine.getCellValueDetailedType(adr('C1'))).toEqual(NumberType.NUMBER_DATE) - expect(engine.getCellValueFormat(adr('C1'))).toEqual('DD/MM/YYYY') - expect(engine.getCellValueDetailedType(adr('D1'))).toEqual(NumberType.NUMBER_DATE) - expect(engine.getCellValueFormat(adr('D1'))).toEqual('DD/MM/YYYY') - expect(engine.getCellValueDetailedType(adr('E1'))).toEqual(NumberType.NUMBER_DATE) - expect(engine.getCellValueFormat(adr('E1'))).toEqual('DD/MM/YYYY') - expect(engine.getCellValueDetailedType(adr('F1'))).toEqual(NumberType.NUMBER_DATE) - expect(engine.getCellValueFormat(adr('F1'))).toEqual('DD/MM/YYYY') - expect(engine.getCellValueDetailedType(adr('G1'))).toEqual(NumberType.NUMBER_DATE) - expect(engine.getCellValueFormat(adr('G1'))).toEqual('DD/MM/YYYY') - expect(engine.getCellValueDetailedType(adr('H1'))).toEqual(NumberType.NUMBER_DATE) - expect(engine.getCellValueFormat(adr('H1'))).toEqual('DD/MM/YYYY') - }) - }) - - describe('#timeFormats', () => { - it('should work with the "hh:mm" format', () => { - const timeFormats = ['hh:mm'] - const engine = HyperFormula.buildFromArray([ - ['13.33'], - ['13:33'], - ['01:33'], - ['1:33'], - ['13:33:33'], - ], { timeFormats }) - - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(NumberType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('A2'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A3'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A4'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A5'))).toEqual(CellValueNoNumber.STRING) - }) - - it('should accept "AM/PM" designator', () => { - const timeFormats = ['hh:mm'] - const engine = HyperFormula.buildFromArray([ - ['1:33 AM'], - ['1:33 PM'], - ['01:33 am'], - ['1:33 pm'], - ['1:33AM'], - ['1:33PM'], - ['1:33 AM'], - ['1:33 PM'], - ['13:33 AM'], - ], { timeFormats }) - - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A2'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A3'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A4'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A5'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A6'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A7'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A8'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A9'))).toEqual(CellValueNoNumber.STRING) - }) - - it('should work with the "hh:mm:ss" format', () => { - const timeFormats = ['hh:mm:ss'] - const engine = HyperFormula.buildFromArray([ - ['13:33'], - ['1:33:33'], - ['1:33:33 AM'], - ['1:33:33 PM'], - ['1:33:33 am'], - ['1:33:33 pm'], - ['1:33:33 a'], - ['1:33:33 p'], - ['1:33:33 A'], - ['1:33:33 P'], - ['13:33:33'], - ['01:33:33'], - ['1:33:33.3'], - ['1:33:33.33'], - ['1:33:33.333'], - ['1:33:33.3333'], - ['1:33:33.333333333333333333333333333333333333333333333333333333'], - ], { timeFormats }) - - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(CellValueNoNumber.STRING) - expect(engine.getCellValueDetailedType(adr('A2'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A3'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A4'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A5'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A6'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A7'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A8'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A9'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A10'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A11'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A12'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A13'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A14'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A15'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A16'))).toEqual(NumberType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('A17'))).toEqual(NumberType.NUMBER_TIME) - }) - - it('the parsing result should be the same regardless of decimal places specified in format string', () => { - const dateAsString = '13:33:33.33333' - const dateAsNumber = 0.564969131944444 - - let engine = HyperFormula.buildFromArray([[dateAsString]], { timeFormats: ['hh:mm:ss'] }) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(dateAsNumber, 11) - - engine = HyperFormula.buildFromArray([[dateAsString]], { timeFormats: ['hh:mm:ss.s'] }) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(dateAsNumber, 11) - - engine = HyperFormula.buildFromArray([[dateAsString]], { timeFormats: ['hh:mm:ss.ss'] }) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(dateAsNumber, 11) - - engine = HyperFormula.buildFromArray([[dateAsString]], { timeFormats: ['hh:mm:ss.sss'] }) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(dateAsNumber, 11) - - engine = HyperFormula.buildFromArray([[dateAsString]], { timeFormats: ['hh:mm:ss.ssss'] }) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(dateAsNumber, 11) - - engine = HyperFormula.buildFromArray([[dateAsString]], { timeFormats: ['hh:mm:ss.sssss'] }) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(dateAsNumber, 11) - }) - }) -}) - -describe('getConfig', () => { - it('should not be an instance of Config', () => { - const engine = HyperFormula.buildEmpty() - expect(engine.getConfig()).not.toBeInstanceOf(Config) - }) - - it('should copy returned values', () => { - const arr = ['mm'] - const engine = HyperFormula.buildEmpty({dateFormats: arr}) - const arr2 = engine.getConfig().dateFormats - expect(arr).toEqual(arr2) - expect(arr).not.toBe(arr2) - }) -}) - -describe('getDefaultConfig', () => { - it('should not be an instance of Config', () => { - expect(HyperFormula.defaultConfig).not.toBeInstanceOf(Config) - }) - - it('should copy returned values', () => { - const defaultConfig = HyperFormula.defaultConfig - defaultConfig.dateFormats.push('mm') - const defaultDateFormats = HyperFormula.defaultConfig.dateFormats - expect(defaultDateFormats).toEqual(['DD/MM/YYYY', 'DD/MM/YY']) - }) -}) diff --git a/test/unit/content-changes.spec.ts b/test/unit/content-changes.spec.ts deleted file mode 100644 index c6ca4e4774..0000000000 --- a/test/unit/content-changes.spec.ts +++ /dev/null @@ -1,113 +0,0 @@ -import {CellValueChange, ChangeExporter, ContentChanges} from '../../src/ContentChanges' -import {SimpleRangeValue} from '../../src/SimpleRangeValue' -import {adr} from './testUtils' - -class SimpleChangeExporter implements ChangeExporter { - exportChange(change: CellValueChange): CellValueChange { - return {address: change.address, value: change.value} - } -} - -class SpreadRangeExporter implements ChangeExporter { - exportChange(change: CellValueChange): CellValueChange | CellValueChange[] { - const value = change.value - const address = change.address - if (value instanceof SimpleRangeValue) { - return Array.from(value.entriesFromTopLeftCorner(address)).map(([v, a]) => { - return {address: a, value: v} - }) - } else { - return {address: address, value: value} - } - } -} - -describe('ContentChanges', () => { - const simpleChangeExporter = new SimpleChangeExporter() - const spreadRangeExporter = new SpreadRangeExporter() - - it('should be empty', () => { - const contentChanges = ContentChanges.empty() - - expect(contentChanges.isEmpty()).toEqual(true) - }) - - it('should replace simple value change', () => { - const contentChanges = ContentChanges.empty() - - contentChanges.addChange(1, adr('A1')) - contentChanges.addChange(2, adr('A1')) - - const exportedChanges = contentChanges.exportChanges(simpleChangeExporter) - expect(exportedChanges.length).toEqual(1) - expect(exportedChanges).toContainEqual({address: adr('A1'), value: 2}) - }) - - it('should export simple value change', () => { - const contentChanges = ContentChanges.empty() - - contentChanges.addChange(1, adr('A1')) - - const exportedChanges = contentChanges.exportChanges(simpleChangeExporter) - - expect(contentChanges.isEmpty()).toEqual(false) - expect(exportedChanges.length).toEqual(1) - expect(exportedChanges).toContainEqual({address: adr('A1'), value: 1}) - }) - - it('should export SimpleRangeValue change', () => { - const contentChanges = ContentChanges.empty() - - contentChanges.addChange(SimpleRangeValue.onlyValues([['foo', 'bar']]), adr('A1')) - - const exportedChanges = contentChanges.exportChanges(simpleChangeExporter) - - expect(exportedChanges.length).toEqual(1) - expect(exportedChanges).toContainEqual({address: adr('A1'), value: SimpleRangeValue.onlyValues([['foo', 'bar']])}) - }) - - it('should add all changes', () => { - const contentChanges = ContentChanges.empty() - contentChanges.addChange(1, adr('A1')) - - const otherChanges = ContentChanges.empty() - otherChanges.addChange(2, adr('A2')) - contentChanges.addAll(otherChanges) - - const exportedChanges = contentChanges.exportChanges(simpleChangeExporter) - expect(exportedChanges.length).toEqual(2) - expect(exportedChanges).toContainEqual({address: adr('A1'), value: 1}) - expect(exportedChanges).toContainEqual({address: adr('A2'), value: 2}) - }) - - it('should handle array change', () => { - const contentChanges = ContentChanges.empty() - contentChanges.addChange(SimpleRangeValue.onlyValues([[1, 2], ['foo', 'bar']]), adr('A1')) - - const exportedChanges = contentChanges.exportChanges(spreadRangeExporter) - - expect(exportedChanges.length).toEqual(4) - expect(exportedChanges).toContainEqual({address: adr('A1'), value: 1}) - expect(exportedChanges).toContainEqual({address: adr('B1'), value: 2}) - expect(exportedChanges).toContainEqual({address: adr('A2'), value: 'foo'}) - expect(exportedChanges).toContainEqual({address: adr('B2'), value: 'bar'}) - }) - - it('#1291 - array change should not delete neighboring non-overlapping changes', () => { - const contentChanges = ContentChanges.empty() - contentChanges.addChange(4, adr('B1')) - contentChanges.addChange(5, adr('C1')) - - const otherChanges = ContentChanges.empty() - otherChanges.addChange(SimpleRangeValue.onlyValues([[1], [2], [3]]), adr('A1')) - contentChanges.addAll(otherChanges) - - const exportedChanges = contentChanges.exportChanges(spreadRangeExporter) - expect(exportedChanges.length).toEqual(5) - expect(exportedChanges).toContainEqual({address: adr('A1'), value: 1}) - expect(exportedChanges).toContainEqual({address: adr('A2'), value: 2}) - expect(exportedChanges).toContainEqual({address: adr('A3'), value: 3}) - expect(exportedChanges).toContainEqual({address: adr('B1'), value: 4}) - expect(exportedChanges).toContainEqual({address: adr('C1'), value: 5}) - }) -}) diff --git a/test/unit/criterion.spec.ts b/test/unit/criterion.spec.ts deleted file mode 100644 index 9a3cf8617e..0000000000 --- a/test/unit/criterion.spec.ts +++ /dev/null @@ -1,82 +0,0 @@ -import {CellError, ErrorType} from '../../src/Cell' -import {Config} from '../../src/Config' -import {DateTimeHelper} from '../../src/DateTimeHelper' -import {ArithmeticHelper} from '../../src/interpreter/ArithmeticHelper' -import {buildCriterion, CriterionBuilder, CriterionType} from '../../src/interpreter/Criterion' -import {NumberLiteralHelper} from '../../src/NumberLiteralHelper' - -describe('Criterion', () => { - const config = new Config() - const dateTimeHelper = new DateTimeHelper(config) - const numberLiteralsHelper = new NumberLiteralHelper(config) - const arithmeticHelper = new ArithmeticHelper(config, dateTimeHelper, numberLiteralsHelper) - const criterionBuilder = new CriterionBuilder(config) - it('greater than', () => { - expect(criterionBuilder.parseCriterion('>0', arithmeticHelper)).toEqual(buildCriterion(CriterionType.GREATER_THAN, 0)) - }) - - it('greater or equal than', () => { - expect(criterionBuilder.parseCriterion('>=0', arithmeticHelper)).toEqual(buildCriterion(CriterionType.GREATER_THAN_OR_EQUAL, 0)) - }) - - it('less than', () => { - expect(criterionBuilder.parseCriterion('<0', arithmeticHelper)).toEqual(buildCriterion(CriterionType.LESS_THAN, 0)) - }) - - it('less or equal than', () => { - expect(criterionBuilder.parseCriterion('<=0', arithmeticHelper)).toEqual(buildCriterion(CriterionType.LESS_THAN_OR_EQUAL, 0)) - }) - - it('not equal', () => { - expect(criterionBuilder.parseCriterion('<>0', arithmeticHelper)).toEqual(buildCriterion(CriterionType.NOT_EQUAL, 0)) - }) - - it('equal', () => { - expect(criterionBuilder.parseCriterion('=0', arithmeticHelper)).toEqual(buildCriterion(CriterionType.EQUAL, 0)) - }) - - it('works with bigger number', () => { - expect(criterionBuilder.parseCriterion('>=123', arithmeticHelper)).toEqual(buildCriterion(CriterionType.GREATER_THAN_OR_EQUAL, 123)) - }) - - it('works with negative numbers', () => { - expect(criterionBuilder.parseCriterion('>=-123', arithmeticHelper)).toEqual(buildCriterion(CriterionType.GREATER_THAN_OR_EQUAL, -123)) - }) - - it('works with floats', () => { - expect(criterionBuilder.parseCriterion('>=100.5', arithmeticHelper)).toEqual(buildCriterion(CriterionType.GREATER_THAN_OR_EQUAL, 100.5)) - }) - - it('works with scientific notation', () => { - expect(criterionBuilder.parseCriterion('>=1e5', arithmeticHelper)).toEqual(buildCriterion(CriterionType.GREATER_THAN_OR_EQUAL, 100000)) - }) - - it('works with strings', () => { - expect(criterionBuilder.parseCriterion('=asdf', arithmeticHelper)).toEqual(buildCriterion(CriterionType.EQUAL, 'asdf')) - }) - - it('works with empty string', () => { - expect(criterionBuilder.parseCriterion('=', arithmeticHelper)).toEqual(buildCriterion(CriterionType.EQUAL, null)) - }) - - it('null when unknown operator', () => { - expect(criterionBuilder.parseCriterion('><0', arithmeticHelper)).toEqual(undefined) - }) - - it('defaults to equal when unparsable string', () => { - expect(criterionBuilder.parseCriterion('$fdsa', arithmeticHelper)).toEqual(buildCriterion(CriterionType.EQUAL, '$fdsa')) - }) - - xit('defaults to equal when string with weirdly used operators', () => { - // not sure what should happen here - expect(criterionBuilder.parseCriterion('> { - expect(criterionBuilder.parseCriterion(new CellError(ErrorType.VALUE), arithmeticHelper)).toEqual(undefined) - }) - - it('works with criterion being just value', () => { - expect(criterionBuilder.parseCriterion(100.5, arithmeticHelper)).toEqual(buildCriterion(CriterionType.EQUAL, 100.5)) - }) -}) diff --git a/test/unit/crud-random.spec.ts b/test/unit/crud-random.spec.ts deleted file mode 100644 index cdea9913cd..0000000000 --- a/test/unit/crud-random.spec.ts +++ /dev/null @@ -1,277 +0,0 @@ -import {HyperFormula} from '../../src' -import {AbsoluteCellRange} from '../../src/AbsoluteCellRange' -import {verifyValues} from './testUtils' - -/** - * random int from global variable 'state' - */ - -const getInt = (function() { - let state = 1 - return function() { - state ^= state << 13 - state ^= state >> 17 - state ^= state << 5 - state &= 0x7FFFFFFF - return state - } -})() - -/** - * random float from global variable 'state' - */ -function getFloat(): number { - return getInt() / 0x80000000 -} - -/** - * boolean flag for outputing crud operations - */ -const outputLog = false - -/** - * random int in range between min and max - * - * @param min - * @param max - */ -function randomInteger(min: number, max: number) { - return Math.floor(getFloat() * (max - min)) + min -} - -type Pts = { x: number, y: number } - -type Rectangle = { topleft: Pts, bottomright: Pts } - -/** - * picks a random range as a subset of rectangle - * and builds a formula =SUM(range) - * @param engine - * @param rect - */ -function randomRange(engine: HyperFormula, rect: Rectangle): string { - const x1 = randomInteger(rect.topleft.x, rect.bottomright.x) - const x2 = randomInteger(rect.topleft.x, rect.bottomright.x) - const y1 = randomInteger(rect.topleft.y, rect.bottomright.y) - const y2 = randomInteger(rect.topleft.y, rect.bottomright.y) - const startAddress = engine.simpleCellAddressToString({ - sheet: 0, - col: Math.min(x1, x2), - row: Math.min(y1, y2), - }, 0) as string - const endAddress = engine.simpleCellAddressToString({ - sheet: 0, - col: Math.max(x1, x2), - row: Math.max(y1, y2) - }, 0) as string - return '=SUM(' + startAddress + ':' + endAddress + ')' -} - -function undoRedo(engine: HyperFormula) { - if (outputLog) { - console.log('engine.undo()') - } - engine.undo() - if (outputLog) { - console.log('engine.redo()') - } - engine.redo() -} - -/** - * Fills a rectangle of cells with random formula sums with a random ranges from another rectangle. - * @param engine - * @param rectFormulas - * @param rectValues - */ -function randomSums(engine: HyperFormula, rectFormulas: Rectangle, rectValues: Rectangle) { - allPts(rectFormulas).forEach((pts) => { - const formula = randomRange(engine, rectValues) - if (outputLog) { - console.log(`engine.setCellContents({sheet: 0, col: ${pts.x}, row: ${pts.y}}, '${formula}')`) - } - engine.setCellContents({sheet: 0, col: pts.x, row: pts.y}, formula) - undoRedo(engine) - }) -} - -/** - * Fills a rectangle of cells with random values. - * @param engine - * @param rectValues - */ -function randomVals(engine: HyperFormula, rectValues: Rectangle) { - allPts(rectValues).forEach((pts) => { - const val = randomInteger(-10, 10) - if (outputLog) { - console.log(`engine.setCellContents({sheet: 0, col:${pts.x}, row:${pts.y}}, ${val})`) - } - engine.setCellContents({sheet: 0, col: pts.x, row: pts.y}, val) - undoRedo(engine) - }) -} - -/** - * builds a rectangle from corner + X side length + Y side length - * @param pts - * @param sideX - * @param sideY - */ -function rectangleFromCorner(pts: Pts, sideX: number, sideY: number): Rectangle { - return {topleft: pts, bottomright: {x: pts.x + sideX, y: pts.y + sideY}} -} - -/** - * all addresses from a rectangle - * - * @param rect - */ -function allPts(rect: Rectangle): Pts[] { - const ret: Pts[] = [] - for (let x = rect.topleft.x; x < rect.bottomright.x; x++) { - for (let y = rect.topleft.y; y < rect.bottomright.y; y++) { - ret.push({x, y}) - } - } - return ret -} - -/** - * random shuffle of an array - * - * @param array - */ -function shuffleArray(array: T[]): T[] { - for (let i = array.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [array[i], array[j]] = [array[j], array[i]] - } - return array -} - -/** - * swaps two rectangles in-place - * @param engine - * @param corner1 - * @param corner2 - * @param sideX - * @param sideY - */ -function swapTwoRectangles(engine: HyperFormula, pts1: Pts, pts2: Pts, sideX: number, sideY: number) { - if (outputLog) { - console.log(`engine.moveCells( AbsoluteCellRange.spanFrom({sheet: 0, col: ${pts1.x}, row: ${pts1.y}}, ${sideX}, ${sideY}), {sheet: 0, col: 1000, row: 1000})`) - } - engine.moveCells(AbsoluteCellRange.spanFrom({sheet: 0, col: pts1.x, row: pts1.y}, sideX, sideY), { - sheet: 0, - col: 1000, - row: 1000 - }) - undoRedo(engine) - if (outputLog) { - console.log(`engine.moveCells( AbsoluteCellRange.spanFrom({sheet: 0, col: ${pts2.x}, row: ${pts2.y}}, ${sideX}, ${sideY}), {sheet: 0, col: ${pts1.x}, row: ${pts1.y}})`) - } - engine.moveCells(AbsoluteCellRange.spanFrom({sheet: 0, col: pts2.x, row: pts2.y}, sideX, sideY), { - sheet: 0, - col: pts1.x, - row: pts1.y - }) - undoRedo(engine) - if (outputLog) { - console.log(`engine.moveCells( AbsoluteCellRange.spanFrom({sheet: 0, col: 1000, row: 1000}, ${sideX}, ${sideY}), {sheet: 0, col: ${pts2.x}, row: ${pts2.y}})`) - } - engine.moveCells(AbsoluteCellRange.spanFrom({sheet: 0, col: 1000, row: 1000}, sideX, sideY), { - sheet: 0, - col: pts2.x, - row: pts2.y - }) - undoRedo(engine) -} - -/** - * Empties the engine using .setCellContents() - * operates only on sheet: 0 - * - * @param engine - engine to be emptied - * @param rect - rectangle of adresses we expect nonempty cell to be in - * - * The outcome should be that there are no cells in the engine. - */ -function randomCleanup(engine: HyperFormula, rect: Rectangle) { - shuffleArray(allPts(rect)).forEach((pts) => { - if (outputLog) { - console.log(`engine.setCellContents({sheet: 0, col:${pts.x}, row:${pts.y}}, null)`) - } - engine.setCellContents({sheet: 0, col: pts.x, row: pts.y}, null) - undoRedo(engine) - } - ) -} - -describe('large psuedo-random test', () => { - it('growing rectangle + addRows + addColumns + removeRows + removeColumns should produce the same sheet as static sheet', () => { - const engine = HyperFormula.buildFromArray([]) - let sideX = 3 - const n = 4 - let sideY = 3 - for (let rep = 0; rep < 3; rep++) { - randomVals(engine, rectangleFromCorner({x: 0, y: 0}, sideX, sideY)) - verifyValues(engine) - for (let i = 0; i < n; i++) { - randomSums(engine, - rectangleFromCorner({x: sideX * (i + 1), y: 0}, sideX, sideY), - rectangleFromCorner({x: sideX * i, y: 0}, sideX, sideY) - ) - verifyValues(engine) - } - for (let i = 0; i < n; i++) { - randomSums(engine, - rectangleFromCorner({x: sideX * (i + 1), y: 0}, sideX, sideY), - rectangleFromCorner({x: 0, y: 0}, sideX * (i + 1), sideY) - ) - verifyValues(engine) - } - for (let i = 0; i < n; i++) { - const columnPositionToAdd = randomInteger(0, sideX * (n + 1) + 1) - if (outputLog) { - console.log(`engine.addColumns(0, [${columnPositionToAdd},2])`) - } - engine.addColumns(0, [columnPositionToAdd, 2]) - undoRedo(engine) - verifyValues(engine) - const columnPositionToRemove = randomInteger(0, sideX * (n + 1)) - if (outputLog) { - console.log(`engine.removeColumns(0, [${columnPositionToRemove},1])`) - } - engine.removeColumns(0, [columnPositionToRemove, 1]) - undoRedo(engine) - } - sideX += 1 - - const rowPositionToAdd = randomInteger(0, sideY + 1) - if (outputLog) { - console.log(`engine.addRows(0, [${rowPositionToAdd},2])`) - } - engine.addRows(0, [rowPositionToAdd, 2]) - undoRedo(engine) - sideY += 2 - verifyValues(engine) - const rowPositionToRemove = randomInteger(0, sideY) - if (outputLog) { - console.log(`engine.removeRows(0, [${rowPositionToRemove},1])`) - } - engine.removeRows(0, [rowPositionToRemove, 1]) - undoRedo(engine) - sideY -= 1 - verifyValues(engine) - const x1 = randomInteger(0, n * sideX) - const x2 = randomInteger(0, n * sideX) - const y1 = randomInteger(0, sideY) - const y2 = randomInteger(0, sideY) - swapTwoRectangles(engine, {x: x1, y: y1}, {x: x2, y: y2}, sideX, sideY) - verifyValues(engine) - } - randomCleanup(engine, rectangleFromCorner({x: 0, y: 0}, 2 * (n + 1) * sideX, 2 * sideY)) - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) -}) diff --git a/test/unit/cruds/adding-columns-dependencies.spec.ts b/test/unit/cruds/adding-columns-dependencies.spec.ts deleted file mode 100644 index 17a2434057..0000000000 --- a/test/unit/cruds/adding-columns-dependencies.spec.ts +++ /dev/null @@ -1,533 +0,0 @@ -import {HyperFormula} from '../../../src' -import {EmptyCellVertex} from '../../../src/DependencyGraph' -import {CellAddress} from '../../../src/parser' - -import { - adr, - colEnd, - colStart, - expectEngineToBeTheSameAs, - extractReference, - extractRowRange, - rowEnd, - rowStart -} from '../testUtils' - -describe('Adding column, fixing dependency', () => { - describe('all in same sheet (case 1)', () => { - it('same sheet, case Aa, absolute column', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* new col */ '=$A1'], - ]) - - engine.addColumns(0, [1, 1]) - - expect(extractReference(engine, adr('C1'))).toEqual(CellAddress.absoluteCol(0, 0)) - }) - - it('same sheet, case Aa, absolute row and col', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* new col */ '=$A$1'], - ]) - - engine.addColumns(0, [1, 1]) - - expect(extractReference(engine, adr('C1'))).toEqual(CellAddress.absolute(0, 0)) - }) - - it('same sheet, case Ab', () => { - const engine = HyperFormula.buildFromArray([ - ['=$B1' /* new col */, '42'], - ]) - - engine.addColumns(0, [1, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.absoluteCol(2, 0)) - }) - - it('same sheet, case Raa', () => { - const engine = HyperFormula.buildFromArray([ - ['=B1', '13', /* new col */ '42'], - ]) - - engine.addColumns(0, [2, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(1, 0)) - }) - - it('same sheet, case Rab', () => { - const engine = HyperFormula.buildFromArray([ - ['42', '13', /* new col */ '=B1'], - ]) - - engine.addColumns(0, [2, 1]) - - expect(extractReference(engine, adr('D1'))).toEqual(CellAddress.relative(-2, 0)) - }) - - it('same sheet, case Rba', () => { - const engine = HyperFormula.buildFromArray([ - ['=C1', '13', /* new col */ '42'], - ]) - - engine.addColumns(0, [2, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(3, 0)) - }) - - it('same sheet, case Rbb', () => { - const engine = HyperFormula.buildFromArray([ - ['42', /* new col */ '=C1', '13'], - ]) - - engine.addColumns(0, [1, 1]) - - expect(extractReference(engine, adr('C1'))).toEqual(CellAddress.relative(1, 0)) - }) - - it('same sheet, same column', () => { - const engine = HyperFormula.buildFromArray([ - ['42', '43'], - [null, '=B1'], - ]) - - engine.addColumns(0, [1, 1]) - - expect(extractReference(engine, adr('C2'))).toEqual(CellAddress.relative(0, -1)) - }) - }) - - describe('dependency address sheet different than formula address sheet and sheet in which we add columns (case 2)', () => { - it('absolute case', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - [ /* new col */ '=Sheet2!$A1'], - ], - Sheet2: [ - ['1'], - ], - }) - - engine.addColumns(0, [0, 1]) - - expect(extractReference(engine, adr('B1'))).toEqual(CellAddress.absoluteCol(0, 0, 1)) - }) - - it('R < r', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - [/* new col */ null, '=Sheet2!A1'], - ], - Sheet2: [ - ['1'], - ], - }) - - engine.addColumns(0, [0, 1]) - - expect(extractReference(engine, adr('C1'))).toEqual(CellAddress.relative(-2, 0, 1)) - }) - - it('r = R', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - [/* new col */ '=Sheet2!B1'], - ], - Sheet2: [ - [null, '1'], - ], - }) - - engine.addColumns(0, [0, 1]) - - expect(extractReference(engine, adr('B1'))).toEqual(CellAddress.relative(0, 0, 1)) - }) - - it('r < R', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=Sheet2!A1' /* new col */], - ], - Sheet2: [ - ['1'], - ], - }) - - engine.addColumns(0, [1, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 0, 1)) - }) - }) - - describe('formula address sheet different than dependency address sheet and sheet in which we add columns (case 3)', () => { - it('dependency address before added column', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - [/* new col */ '1', '2'], - ], - Sheet2: [ - ['=Sheet1!B1'], - ], - }) - - engine.addColumns(0, [0, 1]) - - expect(extractReference(engine, adr('A1', 1))).toEqual(CellAddress.relative(2, 0, 0)) - }) - - it('dependency address at added column', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - [/* new col */ '1'], - ], - Sheet2: [ - ['=Sheet1!A1'], - ], - }) - - engine.addColumns(0, [0, 1]) - - expect(extractReference(engine, adr('A1', 1))).toEqual(CellAddress.relative(1, 0, 0)) - }) - - it('dependency address after added column', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1' /* new col */], - ], - Sheet2: [ - ['=Sheet1!A1'], - ], - }) - - engine.addColumns(0, [1, 1]) - - expect(extractReference(engine, adr('A1', 1))).toEqual(CellAddress.relative(0, 0, 0)) - }) - }) - - describe('sheet where we add columns different than dependency address and formula address (case 4)', () => { - it('works', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=B1', '13'], - ], - Sheet2: [ - [null, /* new col */ '78'], - ], - }) - - engine.addColumns(1, [1, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(1, 0)) - }) - }) - - describe('each sheet different (case 5)', () => { - it('works', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=Sheet2!B1', '13'], - ], - Sheet2: [ - [null, '78'], - ], - Sheet3: [ - [null, /* new col */ null], - ], - }) - - engine.addColumns(2, [1, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(1, 0, 1)) - }) - }) -}) - -describe('Adding column, fixing ranges', () => { - it('insert column to empty range', () => { - const engine = HyperFormula.buildFromArray([ - [null, /* new col */ null, null], - ['=SUM(A1:C1)'], - ]) - - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('C1'))).not.toBe(undefined) - - engine.addColumns(0, [1, 1]) - - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('C1'))).toBe(undefined) - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('D1'))).not.toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null, null, null], - ['=SUM(A1:D1)'], - ])) - }) - - it('insert column in middle of range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* new col */ '2', '3'], - ['=SUM(A1:C1)'], - ]) - - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('C1'))).not.toBe(undefined) - - engine.addColumns(0, [1, 1]) - - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('C1'))).toBe(undefined) - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('D1'))).not.toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null, '2', '3'], - ['=SUM(A1:D1)'], - ])) - }) - - it('insert column before range', () => { - const engine = HyperFormula.buildFromArray([ - [/* new col */ '1', '2', '3'], - ['=SUM(A1:C1)'], - ]) - - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('C1'))).not.toBe(undefined) - engine.addColumns(0, [0, 1]) - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('C1'))).toBe(undefined) - expect(engine.rangeMapping.getRangeVertex(adr('B1'), adr('D1'))).not.toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '1', '2', '3'], - [null, '=SUM(B1:D1)'], - ])) - }) - - it('insert column after range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3' /* new col */], - ['=SUM(A1:C1)'], - ]) - - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('C1'))).not.toBe(undefined) - engine.addColumns(0, [3, 1]) - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('C1'))).not.toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '2', '3', null], - ['=SUM(A1:C1)'], - ])) - }) - - it('should insert new cell with edge to only one range at right', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', /* */ '3', '4'], - ['=SUM(A1:A1)', '=SUM(A1:B1)', /* */ '=SUM(A1:C1)', '=SUM(A1:D1)'], - ]) - - engine.addColumns(0, [2, 1]) - - const c1 = engine.addressMapping.getCell(adr('C1')) - const a1d1 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('D1')) - const a1e1 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('E1')) - - expect(engine.graph.existsEdge(c1!, a1d1)).toBe(true) - expect(engine.graph.existsEdge(c1!, a1e1)).toBe(true) - expect(engine.graph.adjacentNodesCount(c1!)).toBe(2) - }) - - it('range start in column', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* */ '2', '3', '4'], - [null, /* */ '=SUM(B1:D1)'], - ]) - - engine.addColumns(0, [1, 1]) - - const b1 = engine.addressMapping.getCell(adr('B1')) - expect(b1).toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null, '2', '3', '4'], - [null, null, '=SUM(C1:E1)'], - ])) - }) - - it('range start before added column', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* */ '2', '3', '4'], - [null, /* */ '=SUM(A1:D1)'], - ]) - - engine.addColumns(0, [1, 1]) - - const b1 = engine.addressMapping.getCell(adr('B1')) - const range = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('E1')) - expect(b1).toBeInstanceOf(EmptyCellVertex) - expect(engine.graph.existsEdge(b1!, range)).toBe(true) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null, '2', '3', '4'], - [null, null, '=SUM(A1:E1)'], - ])) - }) - - it('range start after added column', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* */ '2', '3', '4'], - [null, /* */ '=SUM(C1:D1)'], - ]) - - engine.addColumns(0, [1, 1]) - - const b1 = engine.addressMapping.getCell(adr('B1')) - expect(b1).toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null, '2', '3', '4'], - [null, null, '=SUM(D1:E1)'], - ])) - }) - - it('range end before added column', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* */ '2', '3', '4'], - [null, /* */ '=SUM(A1:A1)'], - ]) - - engine.addColumns(0, [1, 1]) - - const b1 = engine.addressMapping.getCell(adr('B1')) - expect(b1).toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null, '2', '3', '4'], - [null, null, '=SUM(A1:A1)'], - ])) - }) - - it('range end in a added column', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* */ '2', '3', '4'], - [null, /* */ '=SUM(A1:B1)'], - ]) - - engine.addColumns(0, [1, 1]) - - const b1 = engine.addressMapping.getCell(adr('B1')) - - const range = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('C1')) - expect(b1).toBeInstanceOf(EmptyCellVertex) - expect(engine.graph.existsEdge(b1!, range)).toBe(true) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null, '2', '3', '4'], - [null, null, '=SUM(A1:C1)'], - ])) - }) - - it('range end after added column', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* */ '2', '3', '4'], - [null, /* */ '=SUM(A1:C1)'], - ]) - - engine.addColumns(0, [1, 1]) - - const b1 = engine.addressMapping.getCell(adr('B1')) - - const range = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('D1')) - expect(b1).toBeInstanceOf(EmptyCellVertex) - expect(engine.graph.existsEdge(b1!, range)).toBe(true) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null, '2', '3', '4'], - [null, null, '=SUM(A1:D1)'], - ])) - }) - - it('range start and end in an added column', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* */ '2', '3', '4'], - [null, /* */ '=SUM(B1:B1)'], - ]) - - engine.addColumns(0, [1, 1]) - - const b1 = engine.addressMapping.getCell(adr('B1')) - expect(b1).toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null, '2', '3', '4'], - [null, null, '=SUM(C1:C1)'], - ])) - }) -}) - -describe('Adding column, fixing column ranges', () => { - it('insert column in middle of column range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* new col */ '2', '3', '=SUM(A:C)'], - ]) - - expect(engine.rangeMapping.getRangeVertex(colStart('A'), colEnd('C'))).not.toBe(undefined) - - engine.addColumns(0, [1, 1]) - - expect(engine.rangeMapping.getRangeVertex(colStart('A'), colEnd('C'))).toBe(undefined) - expect(engine.rangeMapping.getRangeVertex(colStart('A'), colEnd('D'))).not.toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null, '2', '3', '=SUM(A:D)'], - ])) - }) - - it('insert column before column range', () => { - const engine = HyperFormula.buildFromArray([ - [/* new col */ '1', '2', '3', '=SUM(A:C)'], - ]) - - expect(engine.rangeMapping.getRangeVertex(colStart('A'), colEnd('C'))).not.toBe(undefined) - engine.addColumns(0, [0, 1]) - expect(engine.rangeMapping.getRangeVertex(colStart('A'), colEnd('C'))).toBe(undefined) - expect(engine.rangeMapping.getRangeVertex(colStart('B'), colEnd('D'))).not.toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '1', '2', '3', '=SUM(B:D)'], - ])) - }) - - it('insert column after column range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3' /* new col */, '=SUM(A:C)'], - ]) - - expect(engine.rangeMapping.getRangeVertex(colStart('A'), colEnd('C'))).not.toBe(undefined) - engine.addColumns(0, [3, 1]) - expect(engine.rangeMapping.getRangeVertex(colStart('A'), colEnd('C'))).not.toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '2', '3', null, '=SUM(A:C)'], - ])) - }) -}) - -describe('Adding column, row range', () => { - it('row range should not be affected', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /*new column */ '2', '3'], - ['4', /*new column */ '5', '6'], - [null, /*new column */null, '=SUM(1:2)'], - ]) - - engine.addColumns(0, [1, 1]) - - expect(engine.rangeMapping.getRangeVertex(rowStart(1), rowEnd(2))).not.toBe(undefined) - const rowRange = extractRowRange(engine, adr('D3')) - expect(rowRange.start).toEqual(rowStart(1)) - expect(rowRange.end).toEqual(rowEnd(2)) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null, '2', '3'], - ['4', null, '5', '6'], - [null, null, null, '=SUM(1:2)'], - ])) - }) -}) diff --git a/test/unit/cruds/adding-columns.spec.ts b/test/unit/cruds/adding-columns.spec.ts deleted file mode 100644 index feded11825..0000000000 --- a/test/unit/cruds/adding-columns.spec.ts +++ /dev/null @@ -1,473 +0,0 @@ -import { - AlwaysDense, - ExportedCellChange, - HyperFormula, - SheetSizeLimitExceededError, - ErrorType, -} from '../../../src' -import {AbsoluteCellRange} from '../../../src/AbsoluteCellRange' -import {Config} from '../../../src/Config' -import {ArrayFormulaVertex, ScalarFormulaVertex} from '../../../src/DependencyGraph' -import {ColumnIndex} from '../../../src/Lookup/ColumnIndex' -import { - adr, - expectArrayWithSameContent, - expectEngineToBeTheSameAs, - extractMatrixRange, - extractRange -} from '../testUtils' -import { ErrorMessage } from '../../../src/error-message' -import { detailedError } from '../testUtils' - -describe('Adding column - checking if its possible', () => { - it('no if starting column is negative', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddColumns(0, [-1, 1])).toEqual(false) - }) - - it('no if starting column is not an integer', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddColumns(0, [1.5, 1])).toEqual(false) - }) - - it('no if starting column is NaN/Infinity', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddColumns(0, [NaN, 1])).toEqual(false) - expect(engine.isItPossibleToAddColumns(0, [Infinity, 1])).toEqual(false) - expect(engine.isItPossibleToAddColumns(0, [-Infinity, 1])).toEqual(false) - }) - - it('no if number of columns is not positive', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddColumns(0, [0, 0])).toEqual(false) - }) - - it('no if number of columns is not an integer', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddColumns(0, [0, 1.5])).toEqual(false) - }) - - it('no if number of columns is NaN/Infinity', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddColumns(0, [0, NaN])).toEqual(false) - expect(engine.isItPossibleToAddColumns(0, [0, Infinity])).toEqual(false) - expect(engine.isItPossibleToAddColumns(0, [0, -Infinity])).toEqual(false) - }) - - it('no if sheet does not exist', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddColumns(1, [0, 1])).toEqual(false) - expect(engine.isItPossibleToAddColumns(1.5, [0, 1])).toEqual(false) - expect(engine.isItPossibleToAddColumns(-1, [0, 1])).toEqual(false) - expect(engine.isItPossibleToAddColumns(NaN, [0, 1])).toEqual(false) - expect(engine.isItPossibleToAddColumns(Infinity, [0, 1])).toEqual(false) - expect(engine.isItPossibleToAddColumns(-Infinity, [0, 1])).toEqual(false) - }) - - it('no if adding column would exceed sheet size limit', () => { - const engine = HyperFormula.buildFromArray([ - Array(Config.defaultConfig.maxColumns - 1).fill('') - ]) - - expect(engine.isItPossibleToAddColumns(0, [0, 2])).toEqual(false) - expect(engine.isItPossibleToAddColumns(0, [0, 1], [5, 1])).toEqual(false) - }) - - it('yes otherwise', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddColumns(0, [0, 1])).toEqual(true) - }) -}) - -describe('Adding column - matrix check', () => { - it('should be possible to add a row crossing matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', 'foo', 'bar'], - ['3', '4', '=TRANSPOSE(A1:B3)'], - ['5', '6'], - ], {chooseAddressMappingPolicy: new AlwaysDense()}) - - engine.addColumns(0, [3, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '2', 'foo', null, 'bar'], - ['3', '4', '=TRANSPOSE(A1:B3)'], - ['5', '6'] - ])) - }) - - it('should adjust matrix address mapping when adding multiple columns', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', 'foo', 'bar'], - ['3', '4', '=TRANSPOSE(A1:B3)'], - ['5', '6'], - ], {chooseAddressMappingPolicy: new AlwaysDense()}) - - engine.addColumns(0, [3, 3]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '2', 'foo', null, null, null, 'bar'], - ['3', '4', '=TRANSPOSE(A1:B3)'], - ['5', '6'] - ])) - }) - - it('should result in cell errors when attempting to use nonscalars', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', 'foo', 'bar'], - ['3', '4', '=TRANSPOSE(A1:B3)'], - ['5', '=A2:A3'] - ], { chooseAddressMappingPolicy: new AlwaysDense() }) - - engine.addColumns(0, [3, 1]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('B1'))).toBe(2) - expect(engine.getCellValue(adr('C1'))).toBe('foo') - expect(engine.getCellValue(adr('D1'))).toBe(null) - expect(engine.getCellValue(adr('E1'))).toBe('bar') - - expect(engine.getCellValue(adr('A2'))).toBe(3) - expect(engine.getCellValue(adr('B2'))).toBe(4) - expect(engine.getCellValue(adr('C2'))).toBe(1) - expect(engine.getCellValue(adr('D2'))).toBe(3) - expect(engine.getCellValue(adr('E2'))).toBe(5) - - expect(engine.getCellValue(adr('A3'))).toBe(5) - expect(engine.getCellValue(adr('B3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('C3'))).toBe(2) - expect(engine.getCellValue(adr('D3'))).toBe(4) - expect(engine.getCellValue(adr('E3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) - - it('should be possible to add row right before matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRANSPOSE(C1:D2)', undefined, '1', '2'], - [undefined, undefined, '3', '4'], - ]) - - engine.addColumns(0, [0, 1]) - - expect(engine.getCellValue(adr('A1'))).toBe(null) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('C1'))).toEqual(3) - expect(engine.getCellValue(adr('D1'))).toEqual(1) - }) - - it('should be possible to add row right after matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRANSPOSE(C1:D2)', undefined, '1', '2'], - [undefined, undefined, '3', '4'], - ]) - - engine.addColumns(0, [2, 1]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('B1'))).toEqual(3) - expect(engine.getCellValue(adr('C1'))).toBe(null) - expect(engine.getCellValue(adr('D1'))).toEqual(1) - }) -}) - -describe('Adding column - reevaluation', () => { - it('reevaluates cells', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* new col */ '2', '=COUNTBLANK(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(0) - engine.addColumns(0, [1, 1]) - expect(engine.getCellValue(adr('D1'))).toEqual(1) - }) - - it('dont reevaluate everything', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* new col */ '2', '=COUNTBLANK(A1:B1)'], - ['=SUM(A1:A1)'], - ]) - const c1 = engine.addressMapping.getCell(adr('C1')) - const a2 = engine.addressMapping.getCell(adr('A2')) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const c1setCellValueSpy = spyOn(c1 as any, 'setCellValue') - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const a2setCellValueSpy = spyOn(a2 as any, 'setCellValue') - - engine.addColumns(0, [1, 1]) - - expect(a2setCellValueSpy).not.toHaveBeenCalled() - expect(c1setCellValueSpy).toHaveBeenCalled() - }) - - it('reevaluates cells which are dependent on structure changes', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* */ '2', '=COLUMNS(A1:B1)'], - ]) - const c1 = engine.addressMapping.getCell(adr('C1')) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const c1setCellValueSpy = spyOn(c1 as any, 'setCellValue') - - engine.addColumns(0, [1, 1]) - - expect(c1setCellValueSpy).toHaveBeenCalled() - expect(extractRange(engine, adr('D1'))).toEqual(new AbsoluteCellRange(adr('A1'), adr('C1'))) - }) - - it('returns changed values', () => { - const engine = HyperFormula.buildFromArray([ - /* */ - ['1', '2', '=COLUMNS(A1:B1)'], - ]) - - const changes = engine.addColumns(0, [1, 1]) - - expect(changes.length).toBe(1) - expect(changes).toContainEqual(new ExportedCellChange(adr('D1'), 3)) - }) -}) - -describe('Adding column - ScalarFormulaVertex#address update', () => { - it('updates addresses in formulas', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* new col */ '=A1'], - ]) - - engine.addColumns(0, [1, 1]) - - const c1 = engine.addressMapping.getCell(adr('C1')) as ScalarFormulaVertex - expect(c1).toBeInstanceOf(ScalarFormulaVertex) - expect(c1.getAddress(engine.lazilyTransformingAstService)).toEqual(adr('C1')) - }) -}) - -describe('Adding column - address mapping', () => { - it('verify sheet dimensions', () => { - const engine = HyperFormula.buildFromArray([ - ['1', /* new col */ '=A1'], - ]) - - engine.addColumns(0, [1, 1]) - - expect(engine.getSheetDimensions(0)).toEqual({ - width: 3, - height: 1, - }) - }) -}) - -describe('different sheet', () => { - it('adding row in different sheet but same row as formula should not update formula address', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1'], - ], - Sheet2: [ - ['=Sheet1!A1'], - ], - }) - - engine.addColumns(0, [0, 1]) - - const formulaVertex = engine.addressMapping.getCell(adr('A1', 1)) as ScalarFormulaVertex - - expect(formulaVertex.getAddress(engine.lazilyTransformingAstService)).toEqual(adr('A1', 1)) - formulaVertex.getFormula(engine.lazilyTransformingAstService) // force transformations to be applied - expect(formulaVertex.getAddress(engine.lazilyTransformingAstService)).toEqual(adr('A1', 1)) - }) -}) - -describe('Adding column - sheet dimensions', () => { - it('should do nothing when adding column outside effective sheet', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ]) - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const recalcSpy = spyOn(engine.evaluator as any, 'partialRun') - engine.addColumns(0, [1, 1]) - engine.addColumns(0, [10, 15]) - - expect(recalcSpy).not.toHaveBeenCalled() - expect(engine.getSheetDimensions(0)).toEqual({ - width: 1, - height: 1, - }) - }) - - it('should throw error when trying to expand sheet beyond limits', () => { - const engine = HyperFormula.buildFromArray([ - Array(Config.defaultConfig.maxColumns - 1).fill('') - ]) - - expect(() => { - engine.addColumns(0, [0, 2]) - }).toThrow(new SheetSizeLimitExceededError()) - - expect(() => { - engine.addColumns(0, [0, 1], [5, 1]) - }).toThrow(new SheetSizeLimitExceededError()) - }) -}) - -describe('Adding column - column index', () => { - it('should update column index when adding row', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=VLOOKUP(1, A1:A1, 1, TRUE())'], - ], {useColumnIndex: true}) - const index = (engine.columnSearch as ColumnIndex) - - expectArrayWithSameContent([0], index.getValueIndex(0, 0, 1).index) - - engine.addColumns(0, [0, 1]) - - expectArrayWithSameContent([], index.getValueIndex(0, 0, 1).index) - expectArrayWithSameContent([0], index.getValueIndex(0, 1, 1).index) - }) -}) - -describe('Adding column - arrays', () => { - it('should be possible to add column before array', () => { - const engine = HyperFormula.buildFromArray([ - ['=-A3:C4', null, null, 'foo'], - ], {useArrayArithmetic: true}) - - engine.addColumns(0, [0, 1]) - - const expected = HyperFormula.buildFromArray([ - [null, '=-B3:D4', null, null, 'foo'], - ], {useArrayArithmetic: true}) - - expectEngineToBeTheSameAs(engine, expected) - }) - - it('adding column across array should not change array', () => { - const engine = HyperFormula.buildFromArray([ - [null, null, null, '=-A1:C1', null, null, 'foo'] - ], {useArrayArithmetic: true}) - - engine.addColumns(0, [4, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null, null, '=-A1:C1', null, null, null, 'foo'] - ], {useArrayArithmetic: true})) - }) - - it('adding column should expand dependent array', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, '=TRANSPOSE(A1:B2)'], - [3, 4], - ], {useArrayArithmetic: true}) - - engine.addColumns(0, [1, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, null, 2, '=TRANSPOSE(A1:C2)'], - [3, null, 4], - ], {useArrayArithmetic: true})) - }) - - it('undo add column with dependent array', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, '=TRANSPOSE(A1:B2)'], - [3, 4], - ], {useArrayArithmetic: true}) - - engine.addColumns(0, [1, 1]) - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2, '=TRANSPOSE(A1:B2)'], - [3, 4], - ], {useArrayArithmetic: true})) - }) - - it('ArrayFormulaVertex#formula should be updated', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, '=TRANSPOSE(A1:B2)'], - [3, 4], - ]) - - engine.addColumns(0, [1, 1]) - - expect(extractMatrixRange(engine, adr('D1'))).toEqual(new AbsoluteCellRange(adr('A1'), adr('C2'))) - }) - - it('ArrayFormulaVertex#formula should be updated when different sheets', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '2'], - ['3', '4'], - ], - Sheet2: [ - ['=TRANSPOSE(Sheet1!A1:B2)'], - ], - }) - - engine.addColumns(0, [1, 1]) - - expect(extractMatrixRange(engine, adr('A1', 1))).toEqual(new AbsoluteCellRange(adr('A1'), adr('C2'))) - }) - - it('ArrayFormulaVertex#address should be updated', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, '=TRANSPOSE(A1:B2)'], - [3, 4], - ]) - - engine.addColumns(0, [1, 1]) - - const matrixVertex = engine.addressMapping.getCell(adr('D1')) as ArrayFormulaVertex - expect(matrixVertex.getAddress(engine.lazilyTransformingAstService)).toEqual(adr('D1')) - }) -}) - -describe('Adding column where there are empty rows in the address mapping (DenseStrategy) - issue #1406', () => { - it('should not throw errors (simple case)', () => { - const hf = HyperFormula.buildFromArray([], { chooseAddressMappingPolicy: new AlwaysDense() }) - - hf.setCellContents({ row: 1, col: 0, sheet: 0 }, 'test') - hf.addColumns(0, [0, 1]) //one column added index [row, amount] - added - - expect(hf.getSheetSerialized(0)).toEqual([ [], [null, 'test'] ]) - }) - - it('should not throw errors (stackblitz reproduction)', () => { - const hf = HyperFormula.buildEmpty({ - licenseKey: 'gpl-v3', - chooseAddressMappingPolicy: new AlwaysDense() - }) - - const sheetName = hf.addSheet('main') - const sheetId = hf.getSheetId(sheetName)! - - hf.setCellContents( - { row: 0, col: 0, sheet: sheetId }, - [ - ['test', null, undefined, 4], - [null, 3, '=ISBLANK(B1)'], - ] - ) - - hf.addRows(0, [2, 1]) - hf.addRows(0, [3, 1]) - hf.setCellContents( - { row: 3, col: 0, sheet: sheetId }, - 'will-it-break?' - ) - hf.addColumns(0, [0, 1]) - - expect(hf.getSheetSerialized(0)).toEqual([ - [null, 'test', null, null, 4], - [null, null, 3, '=ISBLANK(C1)'], - [], - [null, 'will-it-break?'], - ]) - }) -}) diff --git a/test/unit/cruds/adding-row-dependencies.spec.ts b/test/unit/cruds/adding-row-dependencies.spec.ts deleted file mode 100644 index 812c53dc75..0000000000 --- a/test/unit/cruds/adding-row-dependencies.spec.ts +++ /dev/null @@ -1,646 +0,0 @@ -import {HyperFormula} from '../../../src' -import {EmptyCellVertex} from '../../../src/DependencyGraph' -import {CellAddress} from '../../../src/parser' -import {adr, colEnd, colStart, expectEngineToBeTheSameAs, extractReference, rowEnd, rowStart} from '../testUtils' - -describe('Adding row - fixing dependencies', () => { - describe('all in same sheet (case 1)', () => { - it('same sheet, case Aa, absolute row', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - // new row - ['=A$1'], - ]) - - engine.addRows(0, [1, 1]) - - expect(extractReference(engine, adr('A3'))).toEqual(CellAddress.absoluteRow(0, 0)) - }) - - it('same sheet, case Aa, absolute row and col', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - // new row - ['=$A$1'], - ]) - - engine.addRows(0, [1, 1]) - - expect(extractReference(engine, adr('A3'))).toEqual(CellAddress.absolute(0, 0)) - }) - - it('same sheet, case Ab', () => { - const engine = HyperFormula.buildFromArray([ - ['=A$2'], - // new row - ['42'], - ]) - - engine.addRows(0, [1, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.absoluteRow(0, 2)) - }) - - it('same sheet, case Raa', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2'], - ['13'], - // new row - ['42'], - ]) - - engine.addRows(0, [2, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 1)) - }) - - it('same sheet, case Rab', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ['13'], - // new row - ['=A2'], - ]) - - engine.addRows(0, [2, 1]) - - expect(extractReference(engine, adr('A4'))).toEqual(CellAddress.relative(0, -2)) - }) - - it('same sheet, case Rba', () => { - const engine = HyperFormula.buildFromArray([ - ['=A3'], - ['13'], - // new row - ['42'], - ]) - - engine.addRows(0, [2, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 3)) - }) - - it('same sheet, case Rbb', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - // new row - ['=A3'], - ['13'], - ]) - - engine.addRows(0, [1, 1]) - - expect(extractReference(engine, adr('A3'))).toEqual(CellAddress.relative(0, 1)) - }) - - it('same sheet, same row', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ['43', '=A2'], - ]) - - engine.addRows(0, [1, 1]) - - expect(extractReference(engine, adr('B3'))).toEqual(CellAddress.relative(-1, 0)) - }) - }) - - describe('dependency address sheet different than formula address sheet and sheet in which we add rows (case 2)', () => { - it('absolute case', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - // new row - ['=Sheet2!A$1'], - ], - Sheet2: [ - ['1'], - ], - }) - - engine.addRows(0, [0, 1]) - - expect(extractReference(engine, adr('A2'))).toEqual(CellAddress.absoluteRow(0, 0, 1)) - }) - - it('R < r', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - // new row - [null], - ['=Sheet2!A1'], - ], - Sheet2: [ - ['1'], - ], - }) - - engine.addRows(0, [0, 1]) - - expect(extractReference(engine, adr('A3'))).toEqual(CellAddress.relative(0, -2, 1)) - }) - - it('r = R', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - // new row - ['=Sheet2!A2'], - ], - Sheet2: [ - [null], - ['1'], - ], - }) - - engine.addRows(0, [0, 1]) - - expect(extractReference(engine, adr('A2'))).toEqual(CellAddress.relative(0, 0, 1)) - }) - - it('r < R', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=Sheet2!A1'], - // new row - ], - Sheet2: [ - ['1'], - ], - }) - - engine.addRows(0, [1, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 0, 1)) - }) - }) - - describe('formula address sheet different than dependency address sheet and sheet in which we add rows (case 3)', () => { - it('dependency address before added row', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - // new row - ['1'], - ['2'], - ], - Sheet2: [ - ['=Sheet1!A2'], - ], - }) - - engine.addRows(0, [0, 1]) - - expect(extractReference(engine, adr('A1', 1))).toEqual(CellAddress.relative(0, 2, 0)) - }) - - it('dependency address at added row', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - // new row - ['1'], - ], - Sheet2: [ - ['=Sheet1!A1'], - ], - }) - - engine.addRows(0, [0, 1]) - - expect(extractReference(engine, adr('A1', 1))).toEqual(CellAddress.relative(0, 1, 0)) - }) - - it('dependency address after added row', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1'], - // new row - ], - Sheet2: [ - ['=Sheet1!A1'], - ], - }) - - engine.addRows(0, [1, 1]) - - expect(extractReference(engine, adr('A1', 1))).toEqual(CellAddress.relative(0, 0, 0)) - }) - }) - - describe('sheet where we add rows different than dependency address and formula address (case 4)', () => { - it('works', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=A2'], - ['13'], - ], - Sheet2: [ - [null], - // new row - ['78'], - ], - }) - - engine.addRows(1, [1, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 1)) - }) - }) - - describe('each sheet different (case 5)', () => { - it('works', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=Sheet2!A2'], - ['13'], - ], - Sheet2: [ - [null], - ['78'], - ], - Sheet3: [ - [null], - // new row - [null], - ], - }) - - engine.addRows(2, [1, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 1, 1)) - }) - }) -}) - -describe('Adding row, ranges', () => { - it('insert row in middle of range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=SUM(A1:A3)'], - // new row - ['2', null], - ['3', null], - ]) - - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('A3'))).not.toBe(undefined) - engine.addRows(0, [1, 1]) - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('A3'))).toBe(undefined) - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('A4'))).not.toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '=SUM(A1:A4)'], - [null, null], - ['2', null], - ['3', null], - ])) - }) - - it('insert row above range', () => { - const engine = HyperFormula.buildFromArray([ - // new row - ['1', '=SUM(A1:A3)'], - ['2', null], - ['3', null], - ]) - - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('A3'))).not.toBe(undefined) - engine.addRows(0, [0, 1]) - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('A3'))).toBe(undefined) - expect(engine.rangeMapping.getRangeVertex(adr('A2'), adr('A4'))).not.toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null], - ['1', '=SUM(A2:A4)'], - ['2', null], - ['3', null], - ])) - }) - - it('insert row below range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=SUM(A1:A3)'], - ['2', null], - ['3', null], - // new row - ]) - - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('A3'))).not.toBe(undefined) - engine.addRows(0, [3, 1]) - expect(engine.rangeMapping.getRangeVertex(adr('A1'), adr('A3'))).not.toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '=SUM(A1:A3)'], - ['2', null], - ['3', null], - [null, null], - ])) - }) - - it('should insert new cell with edge to all ranges below', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=SUM(A1:A1)'], - ['2', '=SUM(A1:A2)'], - // new row - ['3', '=SUM(A1:A3)'], - ['4', '=SUM(A1:A4)'], - ]) - - engine.addRows(0, [2, 1]) - - const a4 = engine.addressMapping.getCell(adr('A4')) - const a3 = engine.addressMapping.getCell(adr('A3')) - const a2 = engine.addressMapping.getCell(adr('A2')) - const a1a4 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A4')) // A1:A4 - const a1a3 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A3')) // A1:A4 - const a1a2 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A2')) // A1:A4 - - expect(engine.graph.existsEdge(a4!, a1a4)).toBe(true) - expect(engine.graph.existsEdge(a3!, a1a3)).toBe(true) - expect(engine.graph.existsEdge(a2!, a1a2)).toBe(true) - expect(engine.graph.adjacentNodesCount(a4!)).toBe(1) - expect(engine.graph.adjacentNodesCount(a3!)).toBe(1) - expect(engine.graph.adjacentNodesCount(a2!)).toBe(1) - }) - - it('should insert new cell with edge to only one range below, shifted by 1', () => { - const engine = HyperFormula.buildFromArray([ - ['1', null], - ['2', '=SUM(A1:A1)'], - ['3', '=SUM(A1:A2)'], - // new row - ['4', '=SUM(A1:A3)'], - ]) - - engine.addRows(0, [3, 1]) - - const a4 = engine.addressMapping.getCell(adr('A4')) - expect(a4).toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null], - ['2', '=SUM(A1:A1)'], - ['3', '=SUM(A1:A2)'], - [null, null], - ['4', '=SUM(A1:A3)'], - ])) - }) - - it('range start in row', () => { - const engine = HyperFormula.buildFromArray([ - ['1', null], - // new row - ['2', '=SUM(A2:A4)'], - ['3', null], - ['4', null], - ]) - - engine.addRows(0, [1, 1]) - - const a2 = engine.addressMapping.getCell(adr('A2')) - expect(a2).toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null], - [null, null], - ['2', '=SUM(A3:A5)'], - ['3', null], - ['4', null], - ])) - }) - - it('range start above row', () => { - const engine = HyperFormula.buildFromArray([ - ['1', null], - // new row - ['2', '=SUM(A1:A4)'], - ['3', null], - ['4', null], - ]) - - engine.addRows(0, [1, 1]) - - const a2 = engine.addressMapping.getCell(adr('A2')) - const range = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A5')) - expect(a2).toBeInstanceOf(EmptyCellVertex) - expect(engine.graph.existsEdge(a2!, range)).toBe(true) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null], - [null, null], - ['2', '=SUM(A1:A5)'], - ['3', null], - ['4', null], - ])) - }) - - it('range start below row', () => { - const engine = HyperFormula.buildFromArray([ - ['1', null], - // new row - ['2', '=SUM(A3:A4)'], - ['3', null], - ['4', null], - ]) - - engine.addRows(0, [1, 1]) - - const a2 = engine.addressMapping.getCell(adr('A2')) - expect(a2).toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null], - [null, null], - ['2', '=SUM(A4:A5)'], - ['3', null], - ['4', null], - ])) - }) - - it('range end above row', () => { - const engine = HyperFormula.buildFromArray([ - ['1', null], - // new row - ['2', '=SUM(A1:A1)'], - ['3', null], - ['4', null], - ]) - - engine.addRows(0, [1, 1]) - - const a2 = engine.addressMapping.getCell(adr('A2')) - expect(a2).toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null], - [null, null], - ['2', '=SUM(A1:A1)'], - ['3', null], - ['4', null], - ])) - }) - - it('range end in a row', () => { - const engine = HyperFormula.buildFromArray([ - ['1', null], - // new row - ['2', '=SUM(A1:A2)'], - ['3', null], - ['4', null], - ]) - - engine.addRows(0, [1, 1]) - - const a2 = engine.addressMapping.getCell(adr('A2')) - - const range = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A3')) - expect(a2).toBeInstanceOf(EmptyCellVertex) - expect(engine.graph.existsEdge(a2!, range)).toBe(true) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null], - [null, null], - ['2', '=SUM(A1:A3)'], - ['3', null], - ['4', null], - ])) - }) - - it('range end below row', () => { - const engine = HyperFormula.buildFromArray([ - ['1', null], - // new row - ['2', '=SUM(A1:A3)'], - ['3', null], - ['4', null], - ]) - - engine.addRows(0, [1, 1]) - - const a2 = engine.addressMapping.getCell(adr('A2')) - - const range = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A4')) - expect(a2).toBeInstanceOf(EmptyCellVertex) - expect(engine.graph.existsEdge(a2!, range)).toBe(true) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null], - [null, null], - ['2', '=SUM(A1:A4)'], - ['3', null], - ['4', null], - ])) - }) - - it('range start and end in a row', () => { - const engine = HyperFormula.buildFromArray([ - ['1', null], - // new row - ['2', '=SUM(A2:A2)'], - ['3', null], - ['4', null], - ]) - - engine.addRows(0, [1, 1]) - - const a2 = engine.addressMapping.getCell(adr('A2')) - expect(a2).toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', null], - [null, null], - ['2', '=SUM(A3:A3)'], - ['3', null], - ['4', null], - ])) - }) -}) - -describe('Adding row, column range', () => { - it('column range should not be affected', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1', '=SUM(A:B)'], - // new row - ['2', '2'], - ['3', '3'], - ]) - - engine.addRows(0, [1, 1]) - - expect(engine.rangeMapping.getRangeVertex(colStart('A'), colEnd('B'))).not.toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '1', '=SUM(A:B)'], - [null, null], - ['2', '2'], - ['3', '3'], - ])) - }) -}) - -describe('Adding row, fixing row ranges', () => { - it('insert row in middle of row range', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - /* new row */ - ['2'], - ['3'], - ['=SUM(1:3)'], - ]) - - expect(engine.rangeMapping.getRangeVertex(rowStart(1), rowEnd(3))).not.toBe(undefined) - - engine.addRows(0, [1, 1]) - - expect(engine.rangeMapping.getRangeVertex(rowStart(1), rowEnd(3))).toBe(undefined) - expect(engine.rangeMapping.getRangeVertex(rowStart(1), rowEnd(4))).not.toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1'], - [null], - ['2'], - ['3'], - ['=SUM(1:4)'], - ])) - }) - - it('insert row before row range', () => { - const engine = HyperFormula.buildFromArray([ - /* new row */ - ['1'], - ['2'], - ['3'], - ['=SUM(1:3)'], - ]) - - expect(engine.rangeMapping.getRangeVertex(rowStart(1), rowEnd(3))).not.toBe(undefined) - engine.addRows(0, [0, 1]) - expect(engine.rangeMapping.getRangeVertex(rowStart(1), rowEnd(3))).toBe(undefined) - expect(engine.rangeMapping.getRangeVertex(rowStart(2), rowEnd(4))).not.toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null], - ['1'], - ['2'], - ['3'], - ['=SUM(2:4)'], - ])) - }) - - it('insert row after row range', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - /* new row */ - ['=SUM(1:3)'], - ]) - - expect(engine.rangeMapping.getRangeVertex(rowStart(1), rowEnd(3))).not.toBe(undefined) - engine.addRows(0, [3, 1]) - expect(engine.rangeMapping.getRangeVertex(rowStart(1), rowEnd(3))).not.toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - [null], - ['=SUM(1:3)'], - ])) - }) -}) diff --git a/test/unit/cruds/adding-row.spec.ts b/test/unit/cruds/adding-row.spec.ts deleted file mode 100644 index 45d0a8651e..0000000000 --- a/test/unit/cruds/adding-row.spec.ts +++ /dev/null @@ -1,481 +0,0 @@ -import {ExportedCellChange, HyperFormula, SheetSizeLimitExceededError} from '../../../src' -import {AbsoluteCellRange} from '../../../src/AbsoluteCellRange' -import {Config} from '../../../src/Config' -import {ArrayFormulaVertex, ScalarFormulaVertex} from '../../../src/DependencyGraph' -import {AlwaysDense} from '../../../src/DependencyGraph/AddressMapping/ChooseAddressMappingPolicy' -import {ColumnIndex} from '../../../src/Lookup/ColumnIndex' -import {adr, expectArrayWithSameContent, expectEngineToBeTheSameAs, extractMatrixRange} from '../testUtils' - -describe('Adding row - checking if its possible', () => { - it('no if starting row is negative', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddRows(0, [-1, 1])).toEqual(false) - }) - - it('no if starting row is not an integer', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddRows(0, [1.5, 1])).toEqual(false) - }) - - it('no if starting row is NaN', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddRows(0, [NaN, 1])).toEqual(false) - expect(engine.isItPossibleToAddRows(0, [Infinity, 1])).toEqual(false) - expect(engine.isItPossibleToAddRows(0, [-Infinity, 1])).toEqual(false) - }) - - it('no if number of rows is not positive', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddRows(0, [0, 0])).toEqual(false) - }) - - it('no if number of rows is not an integer', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddRows(0, [0, 1.5])).toEqual(false) - }) - - it('no if number of rows is NaN', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddRows(0, [0, NaN])).toEqual(false) - expect(engine.isItPossibleToAddRows(0, [0, Infinity])).toEqual(false) - expect(engine.isItPossibleToAddRows(0, [0, -Infinity])).toEqual(false) - }) - - it('no if sheet does not exist', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddRows(1, [0, 1])).toEqual(false) - expect(engine.isItPossibleToAddRows(1.5, [0, 1])).toEqual(false) - expect(engine.isItPossibleToAddRows(-1, [0, 1])).toEqual(false) - expect(engine.isItPossibleToAddRows(NaN, [0, 1])).toEqual(false) - expect(engine.isItPossibleToAddRows(Infinity, [0, 1])).toEqual(false) - expect(engine.isItPossibleToAddRows(-Infinity, [0, 1])).toEqual(false) - }) - - it('no if adding row would exceed sheet size limit', () => { - const engine = HyperFormula.buildFromArray( - Array(Config.defaultConfig.maxRows - 1).fill(['']) - ) - - expect(engine.isItPossibleToAddRows(0, [0, 2])).toEqual(false) - expect(engine.isItPossibleToAddRows(0, [0, 1], [5, 1])).toEqual(false) - }) - - it('yes otherwise', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToAddRows(0, [0, 1])).toEqual(true) - }) -}) - -describe('Adding row - matrix', () => { - it('should be possible to add row crossing matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ['foo', '=TRANSPOSE(A1:C2)'], - ['bar'], - ], {chooseAddressMappingPolicy: new AlwaysDense()}) - - engine.addRows(0, [3, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ['foo', '=TRANSPOSE(A1:C2)'], - [null], - ['bar'], - ])) - }) - - it('should adjust matrix address mapping when adding multiple rows', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ['foo', '=TRANSPOSE(A1:C2)'], - ['bar'], - ], {chooseAddressMappingPolicy: new AlwaysDense()}) - - engine.addRows(0, [3, 3]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ['foo', '=TRANSPOSE(A1:C2)'], - [null], - [null], - [null], - ['bar'], - ])) - }) - - it('should be possible to add row right above matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRANSPOSE(A3:B4)'], - [], - ['1', '2'], - ['3', '4'], - ]) - - engine.addRows(0, [0, 1]) - - expect(engine.getCellValue(adr('A1'))).toBe(null) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('B2'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - }) - - it('should be possible to add row right after matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRANSPOSE(A3:B4)'], - [], - ['1', '2'], - ['3', '4'], - ]) - - engine.addRows(0, [2, 1]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('B1'))).toEqual(3) - expect(engine.getCellValue(adr('A3'))).toBe(null) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - }) -}) - -describe('Adding row - reevaluation', () => { - it('reevaluates cells', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=COUNTBLANK(A1:A2)'], - // new row - ['2'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(0) - engine.addRows(0, [1, 1]) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - }) - - it('dont reevaluate everything', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=COUNTBLANK(A1:A2)', '=SUM(A1:A1)'], - // new row - ['2'], - ]) - const b1 = engine.addressMapping.getCell(adr('B1')) - const c1 = engine.addressMapping.getCell(adr('C1')) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const b1setCellValueSpy = spyOn(b1 as any, 'setCellValue') - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const c1setCellValueSpy = spyOn(c1 as any, 'setCellValue') - - engine.addRows(0, [1, 1]) - - expect(b1setCellValueSpy).toHaveBeenCalled() - expect(c1setCellValueSpy).not.toHaveBeenCalled() - }) - - it('reevaluates cells which are dependent on structure changes', () => { - const engine = HyperFormula.buildFromArray([ - /* */ - ['1', '2', '=COLUMNS(A1:B1)'], - ]) - const c1 = engine.addressMapping.getCell(adr('C1')) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const c1setCellValueSpy = spyOn(c1 as any, 'setCellValue') - - engine.addRows(0, [0, 1]) - - expect(c1setCellValueSpy).toHaveBeenCalled() - }) - - describe('returns array of changes', () => { - it('not including a value cell that was shifted due to adding a row before it', () => { - const engine = HyperFormula.buildFromArray([['1'],]) - - const changes = engine.addRows(0, [0, 1]) - expect(changes.length).toBe(0) - }) - - it('not including a formula cell that was shifted due to adding a row before it, but its value is the same', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2], - ['=SUM(A1:B1)'], - ]) - - const changes = engine.addRows(0, [1, 1]) - expect(changes.length).toBe(0) - }) - - it('not including a formula cell in which the cell references changed (due to the row shift), but its value is the same', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(A2:B2)'], - [1, 2], - ]) - - const changes = engine.addRows(0, [1, 1]) - expect(changes.length).toBe(0) - }) - - it('including a formula cell that was re-evaluated to different result', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2', '=COUNTBLANK(A1:A2)'], - ]) - - const changes = engine.addRows(0, [1, 1]) - - expect(changes.length).toBe(1) - expect(changes).toContainEqual(new ExportedCellChange(adr('B3'), 1)) - }) - - it('not including a formula cell that was re-evaluated to same result', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2', '=ROWS(A1:A2)-COUNTBLANK(A1:A2)'], - ]) - - expect(engine.getCellValue(adr('B2'))).toBe(2) - - const changes = engine.addRows(0, [1, 1]) - - expect(changes.length).toBe(0) - }) - }) -}) - -describe('Adding row - ScalarFormulaVertex#address update', () => { - it('insert row, formula vertex address shifted', () => { - const engine = HyperFormula.buildFromArray([ - // new row - ['=SUM(1, 2)'], - ]) - - let vertex = engine.addressMapping.getCell(adr('A1')) as ScalarFormulaVertex - expect(vertex.getAddress(engine.lazilyTransformingAstService)).toEqual(adr('A1')) - engine.addRows(0, [0, 1]) - vertex = engine.addressMapping.getCell(adr('A2')) as ScalarFormulaVertex - expect(vertex.getAddress(engine.lazilyTransformingAstService)).toEqual(adr('A2')) - }) - - it('adding row in different sheet but same row as formula should not update formula address', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - // new row - ['1'], - ], - Sheet2: [ - ['=Sheet1!A1'], - ], - }) - - engine.addRows(0, [0, 1]) - - const formulaVertex = engine.addressMapping.getCell(adr('A1', 1)) as ScalarFormulaVertex - - expect(formulaVertex.getAddress(engine.lazilyTransformingAstService)).toEqual(adr('A1', 1)) - formulaVertex.getFormula(engine.lazilyTransformingAstService) // force transformations to be applied - expect(formulaVertex.getAddress(engine.lazilyTransformingAstService)).toEqual(adr('A1', 1)) - }) -}) - -describe('Adding row - address mapping', () => { - it('verify sheet dimensions', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - // new row - ['2'], - ]) - - engine.addRows(0, [1, 1]) - - expect(engine.getSheetDimensions(0)).toEqual({ - width: 1, - height: 3, - }) - }) - - it('addRows() leaves the engine in a valid state so other operations are possible afterwards', () => { - const hfInstance = HyperFormula.buildFromArray([['=A1+B5']]) - hfInstance.addRows(0, [1, 1]) - hfInstance.setCellContents({ sheet: 0, col: 0, row: 1 }, '=A1+B5') - hfInstance.setSheetContent(0, [['=A1+B5'], ['=A1+B5']]) - expect(hfInstance.getSheetSerialized(0)).toEqual([['=A1+B5'], ['=A1+B5']]) - }) - - it('addRows() leaves the engine in a valid state so other operations are possible afterwards (2)', () => { - const hfInstance = HyperFormula.buildFromArray([['=A1+B5']]) - hfInstance.addRows(0, [1, 1]) - hfInstance.setCellContents({ sheet: 0, col: 0, row: 1 }, '=A1+B5') - hfInstance.setSheetContent(0, [['=A1+B5'], ['=A1+B5']]) - expect(hfInstance.getSheetSerialized(0)).toEqual([['=A1+B5'], ['=A1+B5']]) - }) -}) - -describe('Adding row - sheet dimensions', () => { - it('should do nothing when adding row outside effective sheet', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - // new row - ]) - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const recalcSpy = spyOn(engine.evaluator as any, 'partialRun') - engine.addRows(0, [1, 1]) - engine.addRows(0, [10, 15]) - - expect(recalcSpy).not.toHaveBeenCalled() - expect(engine.getSheetDimensions(0)).toEqual({ - width: 1, - height: 1, - }) - }) - - it('should throw error when trying to expand sheet beyond limits', () => { - const engine = HyperFormula.buildFromArray(Array(Config.defaultConfig.maxRows - 1).fill([''])) - - expect(() => { - engine.addRows(0, [0, 2]) - }).toThrow(new SheetSizeLimitExceededError()) - - expect(() => { - engine.addRows(0, [0, 1], [5, 1]) - }).toThrow(new SheetSizeLimitExceededError()) - }) -}) - -describe('Adding row - column index', () => { - it('should update column index when adding row', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=VLOOKUP(2, A1:A10, 1, TRUE())'], - ['2'], - ], {useColumnIndex: true}) - - engine.addRows(0, [1, 1]) - - const index = (engine.columnSearch as ColumnIndex) - expectArrayWithSameContent([0], index.getValueIndex(0, 0, 1).index) - expectArrayWithSameContent([2], index.getValueIndex(0, 0, 2).index) - }) -}) - -describe('Adding row - arrays', () => { - it('should be possible to add row above array', () => { - const engine = HyperFormula.buildFromArray([ - ['=-C1:D3'], - [], - [], - ['foo'] - ], {useArrayArithmetic: true}) - - engine.addRows(0, [0, 1]) - - const expected = HyperFormula.buildFromArray([ - [], - ['=-C2:D4'], - [], - [], - ['foo'] - ], {useArrayArithmetic: true}) - - expectEngineToBeTheSameAs(engine, expected) - }) - - it('adding row across array should not change array', () => { - const engine = HyperFormula.buildFromArray([ - [], [], [], - ['=-A1:B3'], - [], [], - ['foo'] - ], {useArrayArithmetic: true}) - - engine.addRows(0, [4, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [], [], [], - ['=-A1:B3'], - [], [], [], - ['foo'] - ], {useArrayArithmetic: true})) - }) - - it('adding row should expand dependent array', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2], - [3, 4], - ['=TRANSPOSE(A1:B2)'] - ], {useArrayArithmetic: true}) - - engine.addRows(0, [1, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2], - [], - [3, 4], - ['=TRANSPOSE(A1:B3)'] - ], {useArrayArithmetic: true})) - }) - - it('undo add row with dependent array', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2], - [3, 4], - ['=TRANSPOSE(A1:B2)'] - ], {useArrayArithmetic: true}) - - engine.addRows(0, [1, 1]) - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2], - [3, 4], - ['=TRANSPOSE(A1:B2)'] - ], {useArrayArithmetic: true})) - }) - - it('ArrayFormulaVertex#formula should be updated', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=TRANSPOSE(A1:B2)'], - ]) - - engine.addRows(0, [1, 1]) - - expect(extractMatrixRange(engine, adr('A4'))).toEqual(new AbsoluteCellRange(adr('A1'), adr('B3'))) - }) - - it('ArrayFormulaVertex#formula should be updated when different sheets', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '2'], - ['3', '4'], - ], - Sheet2: [ - ['=TRANSPOSE(Sheet1!A1:B2)'], - ], - }) - - engine.addRows(0, [1, 1]) - - expect(extractMatrixRange(engine, adr('A1', 1))).toEqual(new AbsoluteCellRange(adr('A1'), adr('B3'))) - }) - - it('ArrayFormulaVertex#address should be updated', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=TRANSPOSE(A1:B2)'], - ]) - - engine.addRows(0, [1, 1]) - - const matrixVertex = engine.addressMapping.getCell(adr('A4')) as ArrayFormulaVertex - expect(matrixVertex.getAddress(engine.lazilyTransformingAstService)).toEqual(adr('A4')) - }) -}) diff --git a/test/unit/cruds/adding-sheet.spec.ts b/test/unit/cruds/adding-sheet.spec.ts deleted file mode 100644 index 71de7eff54..0000000000 --- a/test/unit/cruds/adding-sheet.spec.ts +++ /dev/null @@ -1,495 +0,0 @@ -import {AlwaysSparse, ErrorType, HyperFormula, SheetNameAlreadyTakenError} from '../../../src' -import {plPL} from '../../../src/i18n/languages' -import {adr, detailedError} from '../testUtils' - -describe('Adding sheet - checking if its possible', () => { - it('yes', () => { - const engine = HyperFormula.buildEmpty() - - expect(engine.isItPossibleToAddSheet('Sheet1')).toBe(true) - expect(engine.isItPossibleToAddSheet('~`!@#$%^&*()_-+_=/|?{}[]\\"')).toBe(true) - }) - - it('no', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [], - Foo: [], - }) - - expect(engine.isItPossibleToAddSheet('Sheet1')).toBe(false) - expect(engine.isItPossibleToAddSheet('Foo')).toBe(false) - }) -}) - -describe('add sheet to engine', () => { - it('should add sheet to empty engine', function() { - const engine = HyperFormula.buildEmpty() - - engine.addSheet() - - expect(engine.sheetMapping.numberOfSheets()).toBe(1) - expect(Array.from(engine.sheetMapping.iterateSheetNames())).toEqual(['Sheet1']) - }) - - it('should add sheet to engine with one sheet', function() { - const engine = HyperFormula.buildFromArray([ - ['foo'], - ]) - - engine.addSheet() - - expect(engine.sheetMapping.numberOfSheets()).toBe(2) - expect(Array.from(engine.sheetMapping.iterateSheetNames())).toEqual(['Sheet1', 'Sheet2']) - }) - - it('should be possible to fetch empty cell from newly added sheet', function() { - const engine = HyperFormula.buildEmpty() - - engine.addSheet() - - expect(engine.getCellValue(adr('A1', 0))).toBeNull() - }) - - it('should add sheet with translated sheet name', function() { - HyperFormula.registerLanguage('plPL', plPL) - const engine = HyperFormula.buildEmpty({language: 'plPL'}) - - engine.addSheet() - - expect(engine.sheetMapping.numberOfSheets()).toBe(1) - expect(Array.from(engine.sheetMapping.iterateSheetNames())).toEqual(['Arkusz1']) - }) - - it('should add sheet with given name', function() { - const engine = HyperFormula.buildEmpty() - - engine.addSheet('foo') - - expect(engine.sheetMapping.numberOfSheets()).toBe(1) - expect(Array.from(engine.sheetMapping.iterateSheetNames())).toEqual(['foo']) - }) - - it('cannot add another sheet with same lowercased name', function() { - const engine = HyperFormula.buildEmpty() - engine.addSheet('foo') - - expect(() => { - engine.addSheet('FOO') - }).toThrowError(/already exists/) - - expect(engine.sheetMapping.numberOfSheets()).toBe(1) - expect(Array.from(engine.sheetMapping.iterateSheetNames())).toEqual(['foo']) - }) - - it('should return given name', function() { - const engine = HyperFormula.buildEmpty() - - const sheetName = engine.addSheet('foo') - - expect(sheetName).toBe('foo') - - }) - - it('should return autogenerated name', function() { - const engine = HyperFormula.buildEmpty() - - const sheetName = engine.addSheet() - - expect(sheetName).toBe('Sheet1') - }) - - it('should throw error when sheet name is already taken', () => { - const engine = HyperFormula.buildEmpty() - engine.addSheet('bar') - - expect(() => { - engine.addSheet('bar') - }).toThrow(new SheetNameAlreadyTakenError('bar')) - }) -}) - -describe('recalculates formulas after adding new sheet (issue #1116)', () => { - it('recalculates single cell reference', () => { - const engine = HyperFormula.buildEmpty() - const table1Name = 'table1' - const table2Name = 'table2' - - engine.addSheet(table1Name) - engine.setCellContents(adr('A1', engine.getSheetId(table1Name)), `='${table2Name}'!A1`) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(table1Name)))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet(table2Name) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(table2Name)))).toBeNull() - expect(engine.getCellValue(adr('A1', engine.getSheetId(table1Name)))).toBeNull() - - engine.setCellContents(adr('A1', engine.getSheetId(table2Name)), 10) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(table1Name)))).toBe(10) - }) - - it('recalculates chained dependencies across multiple sheets', () => { - const engine = HyperFormula.buildEmpty() - const sheet1Name = 'Sheet1' - const sheet2Name = 'Sheet2' - const sheet3Name = 'Sheet3' - - engine.addSheet(sheet1Name) - engine.addSheet(sheet2Name) - engine.setCellContents(adr('A1', engine.getSheetId(sheet1Name)), `='${sheet2Name}'!A1+2`) - engine.setCellContents(adr('A1', engine.getSheetId(sheet2Name)), `='${sheet3Name}'!A1*2`) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet2Name)))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet(sheet3Name) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet3Name)))).toBeNull() - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet2Name)))).toBe(0) - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toBe(2) - - engine.setCellContents(adr('A1', engine.getSheetId(sheet3Name)), 42) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet2Name)))).toBe(84) - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toBe(86) - }) - - it('recalculates nested dependencies within same sheet', () => { - const engine = HyperFormula.buildEmpty() - const sheet1Name = 'Sheet1' - const newSheetName = 'NewSheet' - - engine.addSheet(sheet1Name) - engine.setCellContents(adr('B1', engine.getSheetId(sheet1Name)), `='${newSheetName}'!A1`) - engine.setCellContents(adr('A1', engine.getSheetId(sheet1Name)), '=B1*2') - - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet(newSheetName) - - expect(engine.getCellValue(adr('B1', engine.getSheetId(newSheetName)))).toBeNull() - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet1Name)))).toBeNull() - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toBe(0) - - engine.setCellContents(adr('A1', engine.getSheetId(newSheetName)), 15) - - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet1Name)))).toBe(15) - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toBe(30) - }) - - it('recalculates multiple cells from different sheets', () => { - const engine = HyperFormula.buildEmpty() - const sheet1Name = 'Sheet1' - const sheet2Name = 'Sheet2' - const targetSheetName = 'TargetSheet' - - engine.addSheet(sheet1Name) - engine.addSheet(sheet2Name) - engine.setCellContents(adr('A1', engine.getSheetId(sheet1Name)), `='${targetSheetName}'!A1`) - engine.setCellContents(adr('B1', engine.getSheetId(sheet1Name)), `='${targetSheetName}'!B1`) - engine.setCellContents(adr('A1', engine.getSheetId(sheet2Name)), `='${targetSheetName}'!A1+10`) - engine.setCellContents(adr('B1', engine.getSheetId(sheet2Name)), `='${targetSheetName}'!B1+20`) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet2Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet2Name)))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet(targetSheetName) - engine.setCellContents(adr('A1', engine.getSheetId(targetSheetName)), 5) - engine.setCellContents(adr('B1', engine.getSheetId(targetSheetName)), 7) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toBe(5) - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet1Name)))).toBe(7) - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet2Name)))).toBe(15) - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet2Name)))).toBe(27) - }) - - it('recalculates formulas with mixed operations', () => { - const engine = HyperFormula.buildEmpty() - const sheet1Name = 'Sheet1' - const newSheetName = 'NewSheet' - - engine.addSheet(sheet1Name) - engine.setCellContents(adr('A1', engine.getSheetId(sheet1Name)), 100) - engine.setCellContents(adr('B1', engine.getSheetId(sheet1Name)), `='${newSheetName}'!A1 + A1`) - engine.setCellContents(adr('C1', engine.getSheetId(sheet1Name)), `='${newSheetName}'!B1 * 2`) - - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('C1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet(newSheetName) - engine.setCellContents(adr('A1', engine.getSheetId(newSheetName)), 50) - engine.setCellContents(adr('B1', engine.getSheetId(newSheetName)), 25) - - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet1Name)))).toBe(150) - expect(engine.getCellValue(adr('C1', engine.getSheetId(sheet1Name)))).toBe(50) - }) - - it('recalculates formulas with range references', () => { - const engine = HyperFormula.buildEmpty() - const sheet1Name = 'Sheet1' - const dataSheetName = 'DataSheet' - - engine.addSheet(sheet1Name) - engine.setCellContents(adr('A1', engine.getSheetId(sheet1Name)), `=SUM('${dataSheetName}'!A1:B5)`) - engine.setCellContents(adr('A2', engine.getSheetId(sheet1Name)), `=MEDIAN('${dataSheetName}'!A1:B5)`) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A2', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet(dataSheetName) - const dataSheetId = engine.getSheetId(dataSheetName) - engine.setCellContents(adr('A1', dataSheetId), 1) - engine.setCellContents(adr('B1', dataSheetId), 2) - engine.setCellContents(adr('A2', dataSheetId), 3) - engine.setCellContents(adr('B2', dataSheetId), 4) - engine.setCellContents(adr('A3', dataSheetId), 5) - engine.setCellContents(adr('B3', dataSheetId), 6) - engine.setCellContents(adr('A4', dataSheetId), 7) - engine.setCellContents(adr('B4', dataSheetId), 8) - engine.setCellContents(adr('A5', dataSheetId), 9) - engine.setCellContents(adr('B5', dataSheetId), 10) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toBe(55) - expect(engine.getCellValue(adr('A2', engine.getSheetId(sheet1Name)))).toBe(5.5) - }) - - it('recalculates named expressions', () => { - const engine = HyperFormula.buildEmpty() - const sheet1Name = 'Sheet1' - const newSheetName = 'NewSheet' - - engine.addSheet(sheet1Name) - engine.addNamedExpression('MyValue', `='${newSheetName}'!$A$1`) - engine.setCellContents(adr('A1', engine.getSheetId(sheet1Name)), '=MyValue') - engine.setCellContents(adr('A2', engine.getSheetId(sheet1Name)), '=MyValue*2') - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A2', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet(newSheetName) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toBeNull() - expect(engine.getCellValue(adr('A2', engine.getSheetId(sheet1Name)))).toBe(0) - - engine.setCellContents(adr('A1', engine.getSheetId(newSheetName)), 99) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toBe(99) - expect(engine.getCellValue(adr('A2', engine.getSheetId(sheet1Name)))).toBe(198) - }) - - it('setCellContents adds formula referencing existing sheet after it was added', () => { - const engine = HyperFormula.buildFromSheets({ - 'Main': [[1]], - }) - const mainId = engine.getSheetId('Main')! - - engine.addSheet('NewSheet') - const newSheetId = engine.getSheetId('NewSheet')! - engine.setCellContents(adr('A1', newSheetId), 42) - - engine.setCellContents(adr('B1', mainId), '=NewSheet!A1') - - expect(engine.getCellValue(adr('B1', mainId))).toBe(42) - - engine.setCellContents(adr('C1', mainId), '=FutureSheet!A1') - - expect(engine.getCellValue(adr('C1', mainId))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet('FutureSheet') - engine.setCellContents(adr('A1', engine.getSheetId('FutureSheet')), 99) - - expect(engine.getCellValue(adr('C1', mainId))).toBe(99) - }) - - describe('when using ranges with', () => { - it('function using `runFunction`', () => { - const engine = HyperFormula.buildFromSheets({ - 'FirstSheet': [['=MEDIAN(NewSheet!A1:A1)', '=MEDIAN(NewSheet!A1:A2)', '=MEDIAN(NewSheet!A1:A3)', '=MEDIAN(NewSheet!A1:A4)']], - }) - const sheet1Id = engine.getSheetId('FirstSheet')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('C1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('D1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet('NewSheet') - engine.setSheetContent(engine.getSheetId('NewSheet')!, [[1], [2], [3], [4]]) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(1.5) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(2) - expect(engine.getCellValue(adr('D1', sheet1Id))).toBe(2.5) - }) - - it('function not using `runFunction`', () => { - const engine = HyperFormula.buildFromSheets({ - 'FirstSheet': [['=SUM(NewSheet!A1:A1)', '=SUM(NewSheet!A1:A2)', '=SUM(NewSheet!A1:A3)', '=SUM(NewSheet!A1:A4)']], - }, {useArrayArithmetic: false}) - const sheet1Id = engine.getSheetId('FirstSheet')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('C1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('D1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet('NewSheet') - engine.setSheetContent(engine.getSheetId('NewSheet')!, [[1], [2], [3], [4]]) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(3) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(6) - expect(engine.getCellValue(adr('D1', sheet1Id))).toBe(10) - }) - - it('function using `runFunction` referencing range indirectly', () => { - const engine = HyperFormula.buildFromSheets({ - 'FirstSheet': [ - ['=MEDIAN(A2)', '=MEDIAN(B2)', '=MEDIAN(C2)', '=MEDIAN(D2)'], - ['=\'NewSheet\'!A1:A1', '=\'NewSheet\'!A1:B2', '=\'NewSheet\'!A1:A3', '=\'NewSheet\'!A1:A4'], - ], - }, {useArrayArithmetic: false}) - const sheet1Id = engine.getSheetId('FirstSheet')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('C1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('D1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet('NewSheet') - engine.setSheetContent(engine.getSheetId('NewSheet')!, [[1], [2], [3], [4]]) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(1.5) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(2) - expect(engine.getCellValue(adr('D1', sheet1Id))).toBe(2.5) - }) - - it('function not using `runFunction` referencing range indirectly', () => { - const engine = HyperFormula.buildFromSheets({ - 'FirstSheet': [ - ['=SUM(A2)', '=SUM(B2)', '=SUM(C2)', '=SUM(D2)'], - ['=\'NewSheet\'!A1:A1', '=\'NewSheet\'!A1:B2', '=\'NewSheet\'!A1:A3', '=\'NewSheet\'!A1:A4'], - ], - }, {useArrayArithmetic: false}) - const sheet1Id = engine.getSheetId('FirstSheet')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('C1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('D1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet('NewSheet') - engine.setSheetContent(engine.getSheetId('NewSheet')!, [[1], [2], [3], [4]]) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(3) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(6) - expect(engine.getCellValue(adr('D1', sheet1Id))).toBe(10) - }) - - it('function calling a named expression', () => { - const engine = HyperFormula.buildFromSheets({ - 'FirstSheet': [['=\'NewSheet\'!A1:A4']], - }, {useArrayArithmetic: false}, [ - { name: 'ExprA', expression: '=MEDIAN(NewSheet!$A$1:$A$1)' }, - { name: 'ExprB', expression: '=MEDIAN(NewSheet!$A$1:$A$2)' }, - { name: 'ExprC', expression: '=MEDIAN(NewSheet!$A$1:$A$3)' }, - { name: 'ExprD', expression: '=MEDIAN(FirstSheet!$A$1)' }, - ]) - - expect(engine.getNamedExpressionValue('ExprA')).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getNamedExpressionValue('ExprB')).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getNamedExpressionValue('ExprC')).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getNamedExpressionValue('ExprD')).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet('NewSheet') - engine.setSheetContent(engine.getSheetId('NewSheet')!, [[1], [2], [3], [4]]) - - expect(engine.getNamedExpressionValue('ExprA')).toBe(1) - expect(engine.getNamedExpressionValue('ExprB')).toBe(1.5) - expect(engine.getNamedExpressionValue('ExprC')).toBe(2) - expect(engine.getNamedExpressionValue('ExprD')).toBe(2.5) - }) - }) - - it('should convert placeholder sheet strategy when adding referenced sheet', () => { - const engine = HyperFormula.buildFromSheets({ - 'MainSheet': [['=PlaceholderSheet!A1']], - }, { chooseAddressMappingPolicy: new AlwaysSparse()}) - - const mainId = engine.getSheetId('MainSheet')! - - expect(engine.getCellValue(adr('A1', mainId))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet('PlaceholderSheet') - const placeholderId = engine.getSheetId('PlaceholderSheet')! - - engine.setCellContents(adr('A1', placeholderId), 42) - - expect(engine.getCellValue(adr('A1', mainId))).toBe(42) - }) - - it('should handle adding sheet that resolves references with ranges in placeholder', () => { - const engine = HyperFormula.buildFromSheets({ - Main: [['=SUM(Data!A1:A3)']], - }) - - const mainId = engine.getSheetId('Main')! - - expect(engine.getCellValue(adr('A1', mainId))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet('Data') - const dataId = engine.getSheetId('Data')! - engine.setCellContents(adr('A1', dataId), 1) - engine.setCellContents(adr('A2', dataId), 2) - engine.setCellContents(adr('A3', dataId), 3) - - expect(engine.getCellValue(adr('A1', mainId))).toBe(6) - }) - - it('should handle remove and add sheet cycle with range references', () => { - const engine = HyperFormula.buildFromSheets({ - Main: [['=SUM(Data!A1:A3)']], - Data: [[1], [2], [3]], - }) - - const mainId = engine.getSheetId('Main')! - const dataId = engine.getSheetId('Data')! - - expect(engine.getCellValue(adr('A1', mainId))).toBe(6) - - engine.removeSheet(dataId) - - expect(engine.getCellValue(adr('A1', mainId))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet('Data') - const newDataId = engine.getSheetId('Data')! - engine.setCellContents(adr('A1', newDataId), 10) - engine.setCellContents(adr('A2', newDataId), 20) - engine.setCellContents(adr('A3', newDataId), 30) - - expect(engine.getCellValue(adr('A1', mainId))).toBe(60) - }) - - it('should correctly merge sheets when adding sheet that was previously referenced', () => { - const engine = HyperFormula.buildFromSheets({ - Main: [['=NewSheet!A1 + NewSheet!B1']], - }) - - const mainId = engine.getSheetId('Main')! - - expect(engine.getCellValue(adr('A1', mainId))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet('NewSheet') - const newSheetId = engine.getSheetId('NewSheet')! - engine.setCellContents(adr('A1', newSheetId), 100) - engine.setCellContents(adr('B1', newSheetId), 50) - - expect(engine.getCellValue(adr('A1', mainId))).toBe(150) - }) -}) diff --git a/test/unit/cruds/batch-operations.spec.ts b/test/unit/cruds/batch-operations.spec.ts deleted file mode 100644 index f4735a5e0f..0000000000 --- a/test/unit/cruds/batch-operations.spec.ts +++ /dev/null @@ -1,139 +0,0 @@ -import {HyperFormula} from '../../../src' -import {normalizeAddedIndexes, normalizeRemovedIndexes} from '../../../src/Operations' -import {adr, expectArrayWithSameContent} from '../testUtils' - -describe('batch cruds', () => { - it('should run batch cruds and call recompute only once', () => { - const engine = HyperFormula.buildFromArray([ - // - ['foo'], - // - ['bar'], - ]) - - const evaluatorSpy = spyOn(engine.evaluator, 'partialRun') - - engine.batch(() => { - engine.setCellContents(adr('B1'), [['=A1']]) - engine.addRows(0, [0, 1], [1, 1]) - engine.removeRows(0, [0, 1]) - }) - - expect(evaluatorSpy).toHaveBeenCalledTimes(1) - expect(engine.getCellValue(adr('A1'))).toEqual('foo') - expect(engine.getCellValue(adr('A2'))).toBe(null) - expect(engine.getCellValue(adr('A3'))).toEqual('bar') - }) - - it('should run batch cruds unitl fail and call recompute only once', () => { - const engine = HyperFormula.buildFromArray([ - // - ['foo'], - // - ['bar'], - ]) - - const evaluatorSpy = spyOn(engine.evaluator, 'partialRun') - - try { - engine.batch(() => { - engine.setCellContents(adr('B1'), [['=A1']]) - engine.addRows(0, [0, 1], [1, 1]) - engine.removeRows(0, [0, 1]) - engine.addRows(1, [0, 1]) // fail - engine.addRows(0, [0, 1]) - }) - } catch (e) { - // empty line - } - - expect(evaluatorSpy).toHaveBeenCalledTimes(1) - expect(engine.getCellValue(adr('A1'))).toEqual('foo') - expect(engine.getCellValue(adr('A2'))).toBe(null) - expect(engine.getCellValue(adr('A3'))).toEqual('bar') - }) -}) - -describe('normalize added indexes', () => { - it('should return empty array', () => { - const normalized = normalizeAddedIndexes([]) - expectArrayWithSameContent(normalized, []) - }) - - it('should return unchanged one element array', () => { - const normalized = normalizeAddedIndexes([[3, 8]]) - expectArrayWithSameContent(normalized, [[3, 8]]) - }) - - it('should return shifted further indexes when expanding', () => { - const normalized = normalizeAddedIndexes([[3, 3], [7, 3]]) - expectArrayWithSameContent(normalized, [[3, 3], [10, 3]]) - }) - - it('should merge indexes with same start', () => { - const normalized = normalizeAddedIndexes([[3, 3], [3, 7]]) - expectArrayWithSameContent(normalized, [[3, 7]]) - }) - - it('should return shift further indexes - more arguments', () => { - const normalized = normalizeAddedIndexes([[3, 3], [7, 3], [11, 2]]) - expectArrayWithSameContent(normalized, [[3, 3], [10, 3], [17, 2]]) - }) - - it('should return shift further indexes even when they overlap', () => { - const normalized = normalizeAddedIndexes([[3, 5], [8, 5]]) - expectArrayWithSameContent(normalized, [[3, 5], [13, 5]]) - }) - - it('should normalize unsorted indexes', () => { - const normalized = normalizeAddedIndexes([[5, 9], [3, 5]]) - expectArrayWithSameContent(normalized, [[3, 5], [10, 9]]) - }) - - it('mixed case', () => { - const normalized = normalizeAddedIndexes([[3, 7], [3, 2], [2, 1], [15, 15]]) - expectArrayWithSameContent(normalized, [[2, 1], [4, 7], [23, 15]]) - }) -}) - -describe('normalize removed indexes', () => { - it('should return empty array', () => { - const normalized = normalizeRemovedIndexes([]) - expectArrayWithSameContent(normalized, []) - }) - - it('should return unchanged one element array', () => { - const normalized = normalizeRemovedIndexes([[3, 8]]) - expectArrayWithSameContent(normalized, [[3, 8]]) - }) - - it('should return shifted further indexes', () => { - const normalized = normalizeRemovedIndexes([[3, 3], [7, 3]]) - expectArrayWithSameContent(normalized, [[3, 3], [4, 3]]) - }) - - it('should return shift further indexes - more arguments', () => { - const normalized = normalizeRemovedIndexes([[3, 3], [7, 3], [11, 2]]) - expectArrayWithSameContent(normalized, [[3, 3], [4, 3], [5, 2]]) - }) - - it('should normalize adjacent indexes', () => { - const normalized = normalizeRemovedIndexes([[3, 5], [8, 5]]) - expectArrayWithSameContent(normalized, [[3, 10]]) - }) - - it('should normalize overlapping indexes', () => { - const normalized = normalizeRemovedIndexes([[3, 5], [5, 9]]) - expectArrayWithSameContent(normalized, [[3, 11]]) - }) - - it('should normalize unsorted indexes', () => { - const normalized = normalizeRemovedIndexes([[5, 9], [3, 5]]) - expectArrayWithSameContent(normalized, [[3, 11]]) - }) - - it('mixed case', () => { - const normalized = normalizeRemovedIndexes([[3, 7], [4, 8], [1, 1], [15, 5]]) - expectArrayWithSameContent(normalized, [[1, 1], [2, 9], [5, 5]]) - }) -}) diff --git a/test/unit/cruds/change-cell-content.spec.ts b/test/unit/cruds/change-cell-content.spec.ts deleted file mode 100644 index cc083a0d8c..0000000000 --- a/test/unit/cruds/change-cell-content.spec.ts +++ /dev/null @@ -1,1253 +0,0 @@ -import { - CellValueDetailedType, - ErrorType, - ExportedCellChange, - HyperFormula, - InvalidAddressError, - NoSheetWithIdError, - SimpleCellAddress, - SheetSizeLimitExceededError, - ArraySize, - ExpectedValueOfTypeError, -} from '../../../src' -import {AbsoluteCellRange} from '../../../src/AbsoluteCellRange' -import {simpleCellAddress} from '../../../src/Cell' -import {Config} from '../../../src/Config' -import {ArrayFormulaVertex, EmptyCellVertex, ValueCellVertex} from '../../../src/DependencyGraph' -import {ErrorMessage} from '../../../src/error-message' -import {ColumnIndex} from '../../../src/Lookup/ColumnIndex' -import { - adr, - colEnd, - colStart, - detailedError, - expectArrayWithSameContent, - expectEngineToBeTheSameAs, - expectVerticesOfTypes, - noSpace, - rowEnd, - rowStart -} from '../testUtils' - -describe('Changing cell content - checking if its possible', () => { - it('address should have valid coordinates', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToSetCellContents(simpleCellAddress(0, -1, 0))).toEqual(false) - }) - - it('address should be in existing sheet', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToSetCellContents(adr('A1', 1))).toEqual(false) - }) - - it('yes if there is an array', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=TRANSPOSE(A1:B2)'], - [], - ['13'], - ]) - - expect(engine.isItPossibleToSetCellContents(adr('A3'))).toBe(true) - expect(engine.isItPossibleToSetCellContents(AbsoluteCellRange.spanFrom(adr('A3'), 1, 1))).toBe(true) - expect(engine.isItPossibleToSetCellContents(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2))).toBe(true) - expect(engine.isItPossibleToSetCellContents(AbsoluteCellRange.spanFrom(adr('A2'), 2, 2))).toBe(true) - }) - - it('no if content exceeds sheet size limits', () => { - const engine = HyperFormula.buildFromArray([]) - const cellInLastColumn = simpleCellAddress(0, Config.defaultConfig.maxColumns - 1, 0) - const cellInLastRow = simpleCellAddress(0, 0, Config.defaultConfig.maxRows - 1) - expect(engine.isItPossibleToSetCellContents(AbsoluteCellRange.spanFrom(cellInLastColumn, 1, 1))).toEqual(true) - expect(engine.isItPossibleToSetCellContents(AbsoluteCellRange.spanFrom(cellInLastColumn, 2, 1))).toEqual(false) - expect(engine.isItPossibleToSetCellContents(AbsoluteCellRange.spanFrom(cellInLastRow, 1, 1))).toEqual(true) - expect(engine.isItPossibleToSetCellContents(AbsoluteCellRange.spanFrom(cellInLastRow, 1, 2))).toEqual(false) - }) - - it('yes otherwise', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToSetCellContents(adr('A1'))).toEqual(true) - }) - - it('should throw error if testing with a malformed address', () => { - const engine = HyperFormula.buildEmpty() - expect(() => { - engine.isItPossibleToSetCellContents({} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress | SimpleCellRange', 'address')) - }) -}) - -describe('changing cell content', () => { - it('update formula vertex', () => { - const sheet = [ - ['1', '2', '=A1'], - ] - const engine = HyperFormula.buildFromArray(sheet) - const a1 = engine.addressMapping.getCell(adr('A1')) - const b1 = engine.addressMapping.getCell(adr('B1')) - let c1 = engine.addressMapping.getCell(adr('C1')) - - expect(engine.graph.existsEdge(a1!, c1!)).toBe(true) - expect(engine.getCellValue(adr('C1'))).toBe(1) - - engine.setCellContents(adr('C1'), [['=B1']]) - - c1 = engine.addressMapping.getCell(adr('C1')) - expect(engine.graph.existsEdge(a1!, c1!)).toBe(false) - expect(engine.graph.existsEdge(b1!, c1!)).toBe(true) - - expect(engine.getCellValue(adr('C1'))).toBe(2) - }) - - it('update formula to number cell vertex', () => { - const sheet = [ - ['1', '=A1'], - ] - const engine = HyperFormula.buildFromArray(sheet) - const a1 = engine.addressMapping.getCell(adr('A1')) - const b1 = engine.addressMapping.getCell(adr('B1')) - - expect(engine.graph.existsEdge(a1!, b1!)).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(1) - engine.setCellContents(adr('B1'), [['7']]) - expect(engine.getCellValue(adr('B1'))).toBe(7) - expect(engine.graph.existsEdge(a1!, b1!)).toBe(false) - }) - - it('update formula to plain text cell vertex', () => { - const sheet = [ - ['1', '=A1'], - ] - const engine = HyperFormula.buildFromArray(sheet) - const a1 = engine.addressMapping.getCell(adr('A1')) - const b1 = engine.addressMapping.getCell(adr('B1')) - - expect(engine.graph.existsEdge(a1!, b1!)).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(1) - engine.setCellContents(adr('B1'), [['foo']]) - expect(engine.getCellValue(adr('B1'))).toBe('foo') - expect(engine.graph.existsEdge(a1!, b1!)).toBe(false) - }) - - it('set vertex with edge to empty cell', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=A1'], - ]) - - engine.setCellContents(adr('A1'), [[null]]) - - const a1 = engine.addressMapping.getCell(adr('A1')) - const a2 = engine.addressMapping.getCell(adr('B1')) - expect(a1).toBeInstanceOf(EmptyCellVertex) - expect(engine.graph.existsEdge(a1!, a2!)).toBe(true) - expect(engine.getCellValue(adr('A1'))).toBe(null) - }) - - it('update formula to empty cell', () => { - const sheet = [ - ['1', '=A1'], - ] - const engine = HyperFormula.buildFromArray(sheet) - const a1 = engine.addressMapping.getCellOrThrow(adr('A1')) - const b1 = engine.addressMapping.getCellOrThrow(adr('B1')) - - expect(engine.graph.existsEdge(a1, b1)).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(1) - engine.setCellContents(adr('B1'), [[null]]) - expect(engine.getCellValue(adr('B1'))).toBe(null) - expect([ ...engine.graph.getNodes() ]).not.toContain(b1) - expect(engine.graph.existsEdge(a1, b1)).toBe(false) - }) - - it('update value cell to formula', () => { - const sheet = [ - ['1', '2'], - ] - const engine = HyperFormula.buildFromArray(sheet) - const a1 = engine.addressMapping.getCell(adr('A1')) - let b1 = engine.addressMapping.getCell(adr('B1')) - - expect(engine.graph.existsEdge(a1!, b1!)).toBe(false) - expect(engine.getCellValue(adr('B1'))).toBe(2) - engine.setCellContents(adr('B1'), [['=A1']]) - - b1 = engine.addressMapping.getCell(adr('B1')) - expect(engine.graph.existsEdge(a1!, b1!)).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(1) - }) - - it('update value cell to value cell', () => { - const sheet = [ - ['1', '2'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - expect(engine.getCellValue(adr('B1'))).toBe(2) - engine.setCellContents(adr('B1'), [['3']]) - expect(engine.getCellValue(adr('B1'))).toBe(3) - }) - - it('update value cell to value cell with the same value', () => { - const sheet = [ - ['1', '2', '=SUM(A1:B1)'], - ] - const engine = HyperFormula.buildFromArray(sheet) - const c1 = engine.addressMapping.getCell(adr('C1')) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const c1setCellValueSpy = spyOn(c1 as any, 'setCellValue') - - engine.setCellContents(adr('B1'), [['2']]) - - expect(c1setCellValueSpy).not.toHaveBeenCalled() - }) - - it('update value cell to empty', () => { - const sheet = [ - ['1', '2'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - expect(engine.getCellValue(adr('B1'))).toBe(2) - engine.setCellContents(adr('B1'), null) - expect(engine.addressMapping.getCell(adr('B1'))).toBe(undefined) - expect(engine.getCellValue(adr('B1'))).toBe(null) - }) - - it('update value cell to error literal', () => { - const engine = HyperFormula.buildFromArray([ - ['1'] - ]) - - engine.setCellContents(adr('A1'), '#DIV/0!') - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('update value cell to error-like literal', () => { - const engine = HyperFormula.buildFromArray([ - ['1'] - ]) - - engine.setCellContents(adr('A1'), '#FOO!') - - expect(engine.getCellValue(adr('A1'))).toEqual('#FOO!') - }) - - it('update value cell to invalid formula', () => { - const engine = HyperFormula.buildFromArray([[1]]) - - engine.setCellContents(adr('A1'), '=SUM(') - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - expect(engine.getCellFormula(adr('A1'))).toEqual('=SUM(') - }) - - it('changing value inside range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '0'], - ['2', '0'], - ['3', '=SUM(A1:A3)'], - ]) - expect(engine.getCellValue(adr('B3'))).toEqual(6) - - engine.setCellContents({sheet: 0, col: 0, row: 0}, '3') - expect(engine.getCellValue(adr('B3'))).toEqual(8) - }) - - it('changing value inside column range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '0'], - ['2', '0'], - ['3', '0', '=SUM(A:B)'], - ]) - expect(engine.getCellValue(adr('C3'))).toEqual(6) - - engine.setCellContents({sheet: 0, col: 1, row: 0}, '3') - expect(engine.getCellValue(adr('C3'))).toEqual(9) - }) - - it('changing value inside row range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '0'], - ['2', '0'], - ['=SUM(1:2)'], - ]) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - - engine.setCellContents({sheet: 0, col: 1, row: 0}, '3') - expect(engine.getCellValue(adr('A3'))).toEqual(6) - }) - - it('set formula for the first time', () => { - const sheet = [ - ['42', ''], - ] - const engine = HyperFormula.buildFromArray(sheet) - - engine.setCellContents(adr('B1'), '=A1') - - const a1 = engine.addressMapping.getCell(adr('A1')) - const b1 = engine.addressMapping.getCell(adr('B1')) - expect(engine.graph.existsEdge(a1!, b1!)).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(42) - }) - - it('set nothing again (2)', () => { - const sheet = [ - [null], - ] - const engine = HyperFormula.buildFromArray(sheet) - - engine.setCellContents(adr('A1'), null) - const a1 = engine.addressMapping.getCell(adr('A1')) - expect(a1).toBe(undefined) - expect(engine.getCellValue(adr('A1'))).toBe(null) - }) - - it('set number for the first time', () => { - const sheet = [ - [null], - ] - const engine = HyperFormula.buildFromArray(sheet) - - engine.setCellContents(adr('A1'), '7') - - expect(engine.getCellValue(adr('A1'))).toBe(7) - }) - - it('set text for the first time', () => { - const sheet = [ - [null], - ] - const engine = HyperFormula.buildFromArray(sheet) - - engine.setCellContents(adr('A1'), 'foo') - - expect(engine.getCellValue(adr('A1'))).toBe('foo') - }) - - it('change empty to formula', () => { - const sheet = [ - ['42', null, '=B1'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - engine.setCellContents(adr('B1'), '=A1') - - const a1 = engine.addressMapping.getCell(adr('A1')) - const b1 = engine.addressMapping.getCell(adr('B1')) - const c1 = engine.addressMapping.getCell(adr('C1')) - expect(engine.graph.existsEdge(a1!, b1!)).toBe(true) - expect(engine.graph.existsEdge(b1!, c1!)).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(42) - expect(engine.getCellValue(adr('C1'))).toBe(42) - }) - - it('set nothing again', () => { - const sheet = [ - [null, '=A1'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - engine.setCellContents(adr('A1'), null) - - const a1 = engine.addressMapping.getCell(adr('A1')) - const b1 = engine.addressMapping.getCell(adr('B1')) - expect(a1).toBeInstanceOf(EmptyCellVertex) - expect(engine.graph.existsEdge(a1!, b1!)).toBe(true) - }) - - it('change EMPTY to NUMBER', () => { - const sheet = [ - [null, '=A1'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - engine.setCellContents(adr('A1'), '7') - - const a1 = engine.addressMapping.getCell(adr('A1')) - const b1 = engine.addressMapping.getCell(adr('B1')) - expect(engine.graph.existsEdge(a1!, b1!)).toBe(true) - expect(engine.getCellValue(adr('A1'))).toBe(7) - }) - - it('change EMPTY to TEXT', () => { - const sheet = [ - [null, '=A1'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - engine.setCellContents(adr('A1'), 'foo') - - const a1 = engine.addressMapping.getCell(adr('A1')) - const b1 = engine.addressMapping.getCell(adr('B1')) - expect(engine.graph.existsEdge(a1!, b1!)).toBe(true) - expect(engine.getCellValue(adr('A1'))).toBe('foo') - }) - - it('ensure that only part of the tree is evaluated', () => { - const sheet = [ - ['1', '2'], - ['=A1', '=B1'], - ] - const engine = HyperFormula.buildFromArray(sheet) - const a2 = engine.addressMapping.getCell(adr('A2')) - const b2 = engine.addressMapping.getCell(adr('B2')) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const a2setCellValueSpy = spyOn(a2 as any, 'setCellValue') - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const b2setCellValueSpy = spyOn(b2 as any, 'setCellValue') - - engine.setCellContents(adr('A1'), '3') - expect(a2setCellValueSpy).toHaveBeenCalled() - expect(b2setCellValueSpy).not.toHaveBeenCalled() - }) - - it('is not possible to set cell content in sheet which does not exist', () => { - const sheet = [ - ['1', '2'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - expect(() => { - engine.setCellContents(adr('B1', 1), '3') - }).toThrow(new NoSheetWithIdError(1)) - }) - - it('is not possible to set cell content with invalid address', () => { - const sheet = [ - ['1', '2'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - const address = {row: -1, col: 0, sheet: 0} - expect(() => { - engine.setCellContents(address, '3') - }).toThrow(new InvalidAddressError(address)) - }) - - it('remembers if the new formula is structure dependent', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=TRUE()'], - ['1'], - ]) - - engine.setCellContents(adr('C1'), '=COLUMNS(A1:B1)') - const c1 = engine.addressMapping.getCell(adr('C1')) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const c1setCellValueSpy = spyOn(c1 as any, 'setCellValue') - engine.removeRows(0, [1, 1]) - - expect(c1setCellValueSpy).toHaveBeenCalled() - }) - - it('returns cell value change', () => { - const sheet = [ - ['1'], - ] - - const engine = HyperFormula.buildFromArray(sheet) - - const changes = engine.setCellContents(adr('A1'), '2') - - expect(changes.length).toBe(1) - expect(changes).toContainEqual(new ExportedCellChange(adr('A1'), 2)) - }) - - it('returns dependent formula value change', () => { - const sheet = [ - ['1', '=A1'], - ] - - const engine = HyperFormula.buildFromArray(sheet) - - const changes = engine.setCellContents(adr('A1'), '2') - - expect(changes.length).toBe(2) - expect(changes[0]).toMatchObject(new ExportedCellChange(adr('A1'), 2)) - expect(changes[1]).toMatchObject(new ExportedCellChange(adr('B1'), 2)) - }) - - it('returns dependent matrix value changes', () => { - const sheet = [ - ['1', '2'], - ['3', '4'], - ['=MMULT(A1:B2,A1:B2)'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - const changes = engine.setCellContents(adr('A1'), '2') - - expect(changes.length).toBe(5) - expectArrayWithSameContent(changes.map((change) => change.newValue), [2, 10, 12, 18, 22]) - }) - - it('update empty cell to parsing error', () => { - const engine = HyperFormula.buildFromArray([]) - - engine.setCellContents(adr('A1'), '=SUM(') - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - }) - - it('update dependency value cell to parsing error', () => { - const sheet = [ - ['1', '=SUM(A1)'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - engine.setCellContents(adr('A1'), '=SUM(') - - const a1 = engine.addressMapping.getCell(adr('A1')) - const b1 = engine.addressMapping.getCell(adr('B1')) - expect(engine.graph.existsEdge(a1!, b1!)).toBe(true) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - }) - - it('update formula cell to parsing error', () => { - const sheet = [ - ['1', '=SUM(A1)'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - engine.setCellContents(adr('B1'), '=SUM(') - - const a1 = engine.addressMapping.getCell(adr('A1')) - const b1 = engine.addressMapping.getCell(adr('B1')) - expect(engine.graph.existsEdge(a1!, b1!)).toBe(false) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - }) - - it('update parsing error to formula', () => { - const sheet = [ - ['1', '=SUM('], - ] - const engine = HyperFormula.buildFromArray(sheet) - - engine.setCellContents(adr('B1'), '=SUM(A1)') - - expect(engine.getCellValue(adr('B1'))).toEqual(1) - }) - - it('update empty cell to unparsable matrix formula', () => { - const engine = HyperFormula.buildFromArray([]) - - engine.setCellContents(adr('A1'), '=TRANSPOSE(') - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - expect(engine.getCellFormula(adr('A1'))).toEqual('=TRANSPOSE(') - }) - - it('should throw when trying to set cell content outside sheet limits', () => { - const engine = HyperFormula.buildFromArray([]) - const cellInLastColumn = simpleCellAddress(0, Config.defaultConfig.maxColumns, 0) - const cellInLastRow = simpleCellAddress(0, 0, Config.defaultConfig.maxRows) - - expect(() => engine.setCellContents(cellInLastColumn, '1')).toThrow(new SheetSizeLimitExceededError()) - expect(() => engine.setCellContents(cellInLastRow, '1')).toThrow(new SheetSizeLimitExceededError()) - }) - - it('setting empty cells outside sheet limits does not produce error', () => { - const engine = HyperFormula.buildFromArray([]) - const cellInLastColumn = simpleCellAddress(0, Config.defaultConfig.maxColumns, 0) - const cellInLastRow = simpleCellAddress(0, 0, Config.defaultConfig.maxRows) - - expect(() => engine.setCellContents(cellInLastColumn, null)).not.toThrow() - expect(() => engine.setCellContents(cellInLastRow, null)).not.toThrow() - }) - - it('should set matrix with range out of current sheet scope', () => { - const sheet = [ - ['1', '2'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.setCellContents(adr('C1'), '=MMULT(A1:B2,A1:B2)') - }) - - it('should set the cell value type based on the format of the input value', () => { - const engine = HyperFormula.buildFromArray([]) - - engine.setCellContents({ sheet: 0, col: 0, row: 0 }, '42') - engine.setCellContents({ sheet: 0, col: 0, row: 1 }, '$42') - engine.setCellContents({ sheet: 0, col: 0, row: 2 }, '42%') - - expect(engine.getCellValueDetailedType({ sheet: 0, col: 0, row: 0 })).toEqual(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType({ sheet: 0, col: 0, row: 1 })).toEqual(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType({ sheet: 0, col: 0, row: 2 })).toEqual(CellValueDetailedType.NUMBER_PERCENT) - }) - - it('should work in scenario from issue https://github.com/handsontable/hyperformula/issues/1297', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, '=SUM(A1:B1)'], - ['=SUM(1:1)', 0, 0], - ['=SUM(1:2)', 0, 0], - ]) - - engine.setCellContents(adr('C1'), [['=SUM(A1:B1)']]) - engine.setCellContents(adr('A3'), [['=SUM(A1:C2)']]) - engine.setCellContents(adr('B1'), [[null]]) - - expect(engine.getCellValue(adr('A3'))).toEqual(4) - }) -}) - -describe('change multiple cells contents', () => { - it('works for one', () => { - const sheet = [ - ['1', '2'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - engine.setCellContents(adr('B1'), [['3']]) - expect(engine.getCellValue(adr('B1'))).toBe(3) - }) - - it('works for many', () => { - const sheet = [ - ['1', '2', '3'], - ['4', '5', '6'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - engine.setCellContents(adr('B1'), [ - ['12', '13'], - ['15', '16'], - ['18', '19'], - ]) - expect(engine.getCellValue(adr('B1'))).toBe(12) - expect(engine.getCellValue(adr('C1'))).toBe(13) - expect(engine.getCellValue(adr('B2'))).toBe(15) - expect(engine.getCellValue(adr('C2'))).toBe(16) - expect(engine.getCellValue(adr('B3'))).toBe(18) - expect(engine.getCellValue(adr('C3'))).toBe(19) - }) - - it('recompute only once', () => { - const sheet = [ - ['1', '2', '3'], - ['4', '5', '6'], - ] - const engine = HyperFormula.buildFromArray(sheet) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const evaluatorCallSpy = spyOn(engine.evaluator as any, 'partialRun') - - engine.setCellContents(adr('B1'), [ - ['12', '13'], - ['15', '16'], - ['18', '19'], - ]) - - expect(evaluatorCallSpy).toHaveBeenCalledTimes(1) - }) - - it('possible to change matrices', () => { - const sheet = [ - ['1', '2'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - engine.setCellContents(adr('A1'), [['42', '18', '=MMULT(A1:B1,TRANSPOSE(A1:B1))']]) - expect(engine.getCellValue(adr('A1'))).toBe(42) - expect(engine.getCellValue(adr('B1'))).toBe(18) - expect(engine.getCellValue(adr('C1'))).toBe(2088) - }) - - it('returns changes of multiple values', () => { - const sheet = [ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - const changes = engine.setCellContents(adr('A1'), [['7', '8'], ['9', '10']]) - - expect(changes.length).toEqual(4) - expectArrayWithSameContent(changes.map((change) => change.newValue), [7, 8, 9, 10]) - }) - - it('returns changes of multiple values dependent formulas', () => { - const sheet = [ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ['=SUM(A1:B1)', '=SUM(B1:B2)'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - const changes = engine.setCellContents(adr('A1'), [['7', '8'], ['9', '10']]) - - expect(changes.length).toEqual(6) - expectArrayWithSameContent(changes.map((change) => change.newValue), [7, 8, 9, 10, 15, 18]) - }) - - it('should throw when trying to set cell contents outside sheet limits', () => { - const engine = HyperFormula.buildFromArray([]) - const cellInLastColumn = simpleCellAddress(0, Config.defaultConfig.maxColumns - 1, 0) - const cellInLastRow = simpleCellAddress(0, 0, Config.defaultConfig.maxRows - 1) - - expect(() => engine.setCellContents(cellInLastColumn, [['1', '2']])).toThrow(new SheetSizeLimitExceededError()) - expect(() => engine.setCellContents(cellInLastRow, [['1'], ['2']])).toThrow(new SheetSizeLimitExceededError()) - }) -}) - -describe('updating column index', () => { - it('should update column index when changing simple value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '15'], - ], {useColumnIndex: true}) - - engine.setCellContents(adr('B2'), '8') - - expectArrayWithSameContent((engine.columnSearch as ColumnIndex).getValueIndex(0, 1, 15).index, []) - expectArrayWithSameContent((engine.columnSearch as ColumnIndex).getValueIndex(0, 1, 8).index, [1]) - }) - - it('should update column index when clearing cell content', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ], {useColumnIndex: true}) - - engine.setCellContents(adr('B1'), null) - - expectArrayWithSameContent((engine.columnSearch as ColumnIndex).getValueIndex(0, 1, 2).index, []) - }) - - it('should update column index when changing to ParsingError', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ], {useColumnIndex: true}) - - engine.setCellContents(adr('B1'), '=SUM(') - - expectArrayWithSameContent((engine.columnSearch as ColumnIndex).getValueIndex(0, 1, 2).index, []) - }) - - it('should update column index when changing to formula', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ], {useColumnIndex: true}) - - engine.setCellContents(adr('B1'), '=SUM(A1)') - - expectArrayWithSameContent((engine.columnSearch as ColumnIndex).getValueIndex(0, 1, 2).index, []) - expectArrayWithSameContent((engine.columnSearch as ColumnIndex).getValueIndex(0, 1, 1).index, [0]) - }) -}) - -describe('column ranges', () => { - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=SUM(A:B)'] - ]) - - engine.setCellContents(adr('A1'), '3') - - expect(engine.getCellValue(adr('C1'))).toEqual(5) - }) - - it('works when new content is added beyond previous sheet size', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=SUM(A:B)'] - ]) - - engine.setCellContents(adr('A2'), '3') - - const range = engine.rangeMapping.getVertexOrThrow(colStart('A'), colEnd('B')) - const a2 = engine.addressMapping.getCell(adr('A2')) - expect(engine.graph.existsEdge(a2!, range)).toEqual(true) - expect(engine.getCellValue(adr('C1'))).toEqual(6) - }) - - it('works when adding matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(B:C)'], - ['1'], - ['2'], - ]) - - engine.setCellContents(adr('B1'), '=TRANSPOSE(A2:A3)') - - const range = engine.rangeMapping.getVertexOrThrow(colStart('B'), colEnd('C')) - const b1 = engine.addressMapping.getCell(adr('B1')) - const c1 = engine.addressMapping.getCell(adr('C1')) - expect(engine.graph.existsEdge(b1!, range)).toEqual(true) - expect(engine.graph.existsEdge(c1!, range)).toEqual(true) - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) -}) - -describe('row ranges', () => { - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['=SUM(1:2)'] - ]) - - engine.setCellContents(adr('A1'), '3') - - expect(engine.getCellValue(adr('A3'))).toEqual(5) - }) - - it('works when new content is added beyond previous sheet size', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['=SUM(1:2)'] - ]) - - engine.setCellContents(adr('B1'), '3') - - const range = engine.rangeMapping.getVertexOrThrow(rowStart(1), rowEnd(2)) - const b1 = engine.addressMapping.getCell(adr('B1')) - expect(engine.graph.existsEdge(b1!, range)).toEqual(true) - expect(engine.getCellValue(adr('A3'))).toEqual(6) - }) - - it('works when adding matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(2:3)', '1', '2'], - ]) - - engine.setCellContents(adr('A2'), '=TRANSPOSE(B1:C1)') - - const range = engine.rangeMapping.getVertexOrThrow(rowStart(2), rowEnd(3)) - const a2 = engine.addressMapping.getCell(adr('A2')) - const a3 = engine.addressMapping.getCell(adr('A3')) - expect(engine.graph.existsEdge(a2!, range)).toEqual(true) - expect(engine.graph.existsEdge(a3!, range)).toEqual(true) - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) -}) - -describe('arrays', () => { - it('should set array to cell', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2], - [3, 4], - ], {useArrayArithmetic: true}) - - engine.setCellContents(adr('C1'), [['=-A1:B2']]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2, '=-A1:B2'], - [3, 4], - ], {useArrayArithmetic: true})) - }) - - it('should be REF array if no space for result', () => { - const engine = HyperFormula.buildFromArray([ - [], - [1], - ], {useArrayArithmetic: true}) - - engine.setCellContents(adr('A1'), [['=-B2:B3']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(noSpace()) - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['=-B2:B3'], - [1], - ], {useArrayArithmetic: true})) - }) - - it('should be REF array if no space and potential cycle', () => { - const engine = HyperFormula.buildFromArray([ - [], - [1], - ], {useArrayArithmetic: true}) - - engine.setCellContents(adr('A1'), [['=-A2:A3']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(noSpace()) - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['=-A2:A3'], - [1], - ], {useArrayArithmetic: true})) - }) - - it('should shrink to one vertex if there is more content colliding with array', () => { - const engine = HyperFormula.buildFromArray([], {useArrayArithmetic: true}) - - engine.setCellContents(adr('A1'), [ - ['=-C1:D2'], - [1] - ]) - - expect(engine.arrayMapping.getArrayByCorner(adr('A1'))?.array.size).toEqual(ArraySize.error()) - expectVerticesOfTypes(engine, [ - [ArrayFormulaVertex, undefined], - [ValueCellVertex, undefined], - ]) - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['=-C1:D2', null], - [1, null] - ], {useArrayArithmetic: true})) - }) - - it('should be separate arrays', () => { - const engine = HyperFormula.buildFromArray([], {useArrayArithmetic: true}) - - engine.setCellContents(adr('A1'), [ - ['=TRANSPOSE(D1:E2)', '=TRANSPOSE(D1:E2)'], - ['=TRANSPOSE(D1:E2)', '=TRANSPOSE(D1:E2)'], - ]) - - expectVerticesOfTypes(engine, [ - [ArrayFormulaVertex, ArrayFormulaVertex, undefined], - [ArrayFormulaVertex, ArrayFormulaVertex, ArrayFormulaVertex], - [undefined, ArrayFormulaVertex, ArrayFormulaVertex], - ]) - expect(engine.arrayMapping.arrayMapping.size).toEqual(4) - }) - - it('should REF last array', () => { - const engine = HyperFormula.buildFromArray([ - [null, null, null, 1, 2], - [null, null, null, 1, 2], - ], {useArrayArithmetic: true}) - - engine.setCellContents(adr('A1'), [ - ['=TRANSPOSE(D1:E2)', '=TRANSPOSE(D1:E2)'], - ['=TRANSPOSE(D1:E2)'], - ]) - - expectVerticesOfTypes(engine, [ - [ArrayFormulaVertex, ArrayFormulaVertex, ArrayFormulaVertex], - [ArrayFormulaVertex, ArrayFormulaVertex, ArrayFormulaVertex], - [undefined, undefined], - ]) - expect(engine.getSheetValues(0)).toEqual([ - [noSpace(), 1, 1, 1, 2], - [noSpace(), 2, 2, 1, 2], - ]) - expect(engine.arrayMapping.arrayMapping.size).toEqual(3) - }) - - it('should make existing array REF and change cell content to simple value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4', '=B4'], - ['=-A1:B2'], - ], {useArrayArithmetic: true}) - - engine.setCellContents(adr('B4'), [['foo']]) - - expect(engine.getSheetValues(0)).toEqual([ - [1, 2], - [3, 4, 'foo'], - [noSpace()], - [null, 'foo'] - ]) - }) - - it('should not change cell to empty if part of an array', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4', '=B4'], - ['=-A1:B2'], - ], {useArrayArithmetic: true}) - - engine.setCellContents(adr('B4'), [[null]]) - - expect(engine.getSheetValues(0)).toEqual([ - [1, 2], - [3, 4, -4], - [-1, -2], - [-3, -4], - ]) - }) - - it('should make existing array REF and change cell content to formula', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4', '=B4'], - ['=-A1:B2'], - ], {useArrayArithmetic: true}) - - engine.setCellContents(adr('B4'), [['=SUM(A1:B2)']]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2], - [3, 4, '=B4'], - ['=-A1:B2'], - [undefined, '=SUM(A1:B2)'] - ], {useArrayArithmetic: true})) - expect(engine.getSheetValues(0)).toEqual([ - [1, 2], - [3, 4, 10], - [noSpace()], - [null, 10] - ]) - }) - - it('should make existing array REF and change cell content to parsing error', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4', '=B4'], - ['=-A1:B2'], - ], {useArrayArithmetic: true}) - - engine.setCellContents(adr('B4'), [['=SUM(']]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2], - [3, 4, '=B4'], - ['=-A1:B2'], - [null, '=SUM('] - ], {useArrayArithmetic: true})) - }) - - it('should make existing matrix REF and set new array', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4', '=B4'], - ['=-A1:B2'], - ], {useArrayArithmetic: true}) - - engine.setCellContents(adr('B3'), [['=+A1:B2']]) - - expect(engine.getSheetValues(0)).toEqual([ - [1, 2], - [3, 4, 3], - [noSpace(), 1, 2], - [null, 3, 4] - ]) - }) - - it('should replace one array with another', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=-A1:B2'], - ], {useArrayArithmetic: true}) - - engine.setCellContents(adr('A3'), [['=2*A1:B2']]) - - expect(engine.getSheetValues(0)).toEqual([ - [1, 2], - [3, 4], - [2, 4], - [6, 8], - ]) - }) - - it('should adjust dependent formulas after shrinking array', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3, '=-A1:C1', null, null, 4], - ['=D1', '=E1', '=SUM(E1, F1:G1)'], - ], {useArrayArithmetic: true}) - - engine.setCellContents(adr('E1'), [['foo']]) - engine.setCellContents(adr('D1'), [[4, 5, 6]]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2, 3, 4, 5, 6, 4], - ['=D1', '=E1', '=SUM(E1, F1:G1)'] - ], {useArrayArithmetic: true})) - }) - - it('should adjust dependent ranges after shrinking array taking smaller vertices into account', () => { - const engine = HyperFormula.buildFromArray([ - [1, '=-A1:A3', '=SUM(B1:B2)', '=SUM(B1:B3)'], - [2], - [3], - ], {useArrayArithmetic: true}) - - engine.setCellContents(adr('B1'), 'foo') - engine.setCellContents(adr('B1'), [[4], [5], [6]]) - - const b1 = engine.dependencyGraph.getCell(adr('b1'))! - const b2 = engine.dependencyGraph.getCell(adr('b2'))! - const b3 = engine.dependencyGraph.getCell(adr('b3'))! - const b1b2 = engine.rangeMapping.getRangeVertex(adr('b1'), adr('b2'))! - const b1b3 = engine.rangeMapping.getRangeVertex(adr('b1'), adr('b3'))! - - expect(engine.graph.existsEdge(b1, b1b2)).toBe(true) - expect(engine.graph.existsEdge(b2, b1b2)).toBe(true) - expect(engine.graph.existsEdge(b1b2, b1b3)).toBe(true) - expect(engine.graph.existsEdge(b1, b1b3)).toBe(false) - expect(engine.graph.existsEdge(b2, b1b3)).toBe(false) - expect(engine.graph.existsEdge(b3, b1b3)).toBe(true) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 4, '=SUM(B1:B2)', '=SUM(B1:B3)'], - [2, 5], - [3, 6], - ], {useArrayArithmetic: true})) - }) - - it('should return values of a range in changes', () => { - const engine = HyperFormula.buildFromArray([[1, 2]], {useArrayArithmetic: true}) - - const changes = engine.setCellContents(adr('A2'), [['=-A1:B1']]) - - expect(changes.length).toEqual(2) - expect(changes).toContainEqual(new ExportedCellChange(adr('A2'), -1)) - expect(changes).toContainEqual(new ExportedCellChange(adr('B2'), -2)) - }) - - it('should return changed content when replacing array left corner', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=-A1:B1'], - ], {useArrayArithmetic: true}) - - const changes = engine.setCellContents(adr('A2'), [['foo']]) - - expect(changes.length).toEqual(2) - expect(changes).toContainEqual(new ExportedCellChange(adr('A2'), 'foo')) - expect(changes).toContainEqual(new ExportedCellChange(adr('B2'), null)) - }) - - it('should return changed content when replacing any array cell with simple value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['=-A1:C1'], - ], {useArrayArithmetic: true}) - - const changes = engine.setCellContents(adr('B2'), [['foo']]) - - expect(changes.length).toEqual(3) - expect(changes).toContainEqual(new ExportedCellChange(adr('A2'), noSpace())) - expect(changes).toContainEqual(new ExportedCellChange(adr('B2'), 'foo')) - expect(changes).toContainEqual(new ExportedCellChange(adr('C2'), null)) - }) - - it('should return changed content when replacing any array cell with parsing error', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['=-A1:C1'], - ], {useArrayArithmetic: true}) - - const changes = engine.setCellContents(adr('B2'), [['=SUM(']]) - - expect(changes.length).toEqual(3) - expect(changes).toContainEqual(new ExportedCellChange(adr('A2'), noSpace())) - expect(changes).toContainEqual(new ExportedCellChange(adr('B2'), detailedError(ErrorType.ERROR, "Parsing error. Expecting token of type --> RParen <-- but found --> '' <--"))) - expect(changes).toContainEqual(new ExportedCellChange(adr('C2'), null)) - }) - - it('should return changed content when clearing array left corner', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=-A1:B1'], - ], {useArrayArithmetic: true}) - - const changes = engine.setCellContents(adr('A2'), null) - - expect(changes.length).toEqual(2) - expect(changes).toContainEqual(new ExportedCellChange(adr('A2'), null)) - expect(changes).toContainEqual(new ExportedCellChange(adr('B2'), null)) - }) - - it('should return no changes when trying to clear array cell', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=-A1:B1'], - ], {useArrayArithmetic: true}) - - const changes = engine.setCellContents(adr('B2'), null) - - expect(changes.length).toEqual(0) - }) - - it('should return changed content when replacing array to another smaller one', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['=-A1:C1'], - ], {useArrayArithmetic: true}) - - const changes = engine.setCellContents(adr('A2'), [['=+A1:B1']]) - - expect(changes.length).toEqual(3) - expect(changes).toContainEqual(new ExportedCellChange(adr('A2'), 1)) - expect(changes).toContainEqual(new ExportedCellChange(adr('B2'), 2)) - expect(changes).toContainEqual(new ExportedCellChange(adr('C2'), null)) - }) - - it('should return changed content when replacing array to smaller one even if values are same', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['=-A1:C1'], - ], {useArrayArithmetic: true}) - - const changes = engine.setCellContents(adr('A2'), [['=-A1:B1']]) - - expect(changes.length).toEqual(3) - expect(changes).toContainEqual(new ExportedCellChange(adr('A2'), -1)) - expect(changes).toContainEqual(new ExportedCellChange(adr('B2'), -2)) - expect(changes).toContainEqual(new ExportedCellChange(adr('C2'), null)) - }) - - it('should return REF in changes', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3, '=-A1:C1'], - ], {useArrayArithmetic: true}) - - const changes = engine.setCellContents(adr('E1'), [['foo']]) - - expect(changes).toContainEqual(new ExportedCellChange(adr('D1'), noSpace())) - expect(changes).toContainEqual(new ExportedCellChange(adr('E1'), 'foo')) - expect(changes).toContainEqual(new ExportedCellChange(adr('F1'), null)) - }) - - it('should undo REF', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3, '=-A1:C1', null, null, 4], - ['=D1', '=E1', '=SUM(F1:F1)', '=SUM(F1:G1)'], - ], {useArrayArithmetic: true}) - - engine.setCellContents(adr('E1'), [['foo']]) - engine.setCellContents(adr('D1'), [[4, 5, 6]]) - engine.undo() - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2, 3, '=-A1:C1', null, null, 4], - ['=D1', '=E1', '=SUM(F1:F1)', '=SUM(F1:G1)'], - ], {useArrayArithmetic: true})) - - expect(engine.getCellValue(adr('A2'))).toEqual(-1) - expect(engine.getCellValue(adr('B2'))).toEqual(-2) - expect(engine.getCellValue(adr('C2'))).toEqual(-3) - expect(engine.getCellValue(adr('D2'))).toEqual(1) - }) - - it('should redo REF', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3, '=-A1:C1', null, null, 4], - ['=D1', '=E1', '=SUM(F1:F1)', '=SUM(F1:G1)'], - ], {useArrayArithmetic: true}) - - engine.setCellContents(adr('E1'), [['foo']]) - engine.undo() - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2, 3, '=-A1:C1', 'foo', null, 4], - ['=D1', '=E1', '=SUM(F1:F1)', '=SUM(F1:G1)'], - ], {useArrayArithmetic: true})) - }) - - xit('should recalculate matrix if space is available', () => { - const engine = HyperFormula.buildFromArray([ - ['=+C1:D1', 'foo', 1, 2] - ], {useArrayArithmetic: true}) - expect(engine.getCellValue(adr('A1'))).toEqual(noSpace()) - - engine.setCellContents(adr('B1'), null) - - expect(engine.getSheetValues(0)).toEqual([ - [1, 2, 1, 2] - ]) - }) -}) diff --git a/test/unit/cruds/clear-sheet.spec.ts b/test/unit/cruds/clear-sheet.spec.ts deleted file mode 100644 index 2e69f2eaea..0000000000 --- a/test/unit/cruds/clear-sheet.spec.ts +++ /dev/null @@ -1,105 +0,0 @@ -import {HyperFormula, NoSheetWithIdError} from '../../../src' -import {adr} from '../testUtils' - -describe('Clear sheet - checking if its possible', () => { - it('no if theres no such sheet', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToClearSheet(1)).toEqual(false) - }) - - it('yes otherwise', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToClearSheet(0)).toEqual(true) - }) -}) - -describe('Clear sheet content', () => { - it('should throw error when trying to clear not existing sheet', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(() => { - engine.clearSheet(1) - }).toThrow(new NoSheetWithIdError(1)) - }) - - it('should clear sheet content', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'foo'], - ]) - - engine.clearSheet(0) - - expect(engine.getCellValue(adr('A1'))).toBe(null) - expect(engine.getCellValue(adr('B1'))).toBe(null) - }) - - it('should recalculate and return changes', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1'], - ], - Sheet2: [ - ['=Sheet1!A1'], - ['=SUM(1, Sheet1!A1)'], - ], - }) - - const changes = engine.clearSheet(0) - - expect(engine.getCellValue(adr('A1', 1))).toBe(null) - expect(engine.getCellValue(adr('A2', 1))).toEqual(1) - - expect(changes.length).toEqual(2) - }) - - it('should clear sheet with matrix', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - ], - Sheet2: [ - ['=Sheet1!A2'], - ['=Sheet1!A3'], - ], - }) - - const changes = engine.clearSheet(0) - - expect(engine.getCellValue(adr('A1', 1))).toBe(null) - expect(engine.getCellValue(adr('A2', 1))).toBe(null) - - expect(changes.length).toEqual(2) - }) - - it('should clear sheet and dont break edge between cells', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1'], - ], - Sheet2: [ - ['=Sheet1!A1'], - ], - }) - - engine.clearSheet(0) - engine.setCellContents(adr('A1'), '2') - - expect(engine.getCellValue(adr('A1', 1))).toEqual(2) - }) - - it('should clear sheet and dont break edge between cells, case with range', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [[ '1' ]], - Sheet2: [[ '=SUM(Sheet1!A1:B1)' ]], - }) - - engine.clearSheet(0) - engine.setCellContents(adr('A1'), '2') - engine.setCellContents(adr('B1'), '3') - - expect(engine.getCellValue(adr('A1', 1))).toEqual(5) - }) -}) diff --git a/test/unit/cruds/copy-paste.spec.ts b/test/unit/cruds/copy-paste.spec.ts deleted file mode 100644 index 44674cd862..0000000000 --- a/test/unit/cruds/copy-paste.spec.ts +++ /dev/null @@ -1,525 +0,0 @@ -import {ExportedCellChange, HyperFormula, NothingToPasteError, SimpleCellAddress} from '../../../src' -import {AbsoluteCellRange, SimpleCellRange} from '../../../src/AbsoluteCellRange' -import {ErrorType, simpleCellAddress} from '../../../src/Cell' -import {Config} from '../../../src/Config' -import {SheetSizeLimitExceededError} from '../../../src/errors' -import {CellAddress} from '../../../src/parser' -import { - adr, - colEnd, - colStart, - detailedError, - expectArrayWithSameContent, - extractReference, - rowEnd, - rowStart, -} from '../testUtils' -import { DependencyGraph } from '../../../src/DependencyGraph' -import { Statistics } from '../../../src/statistics' -import { FunctionRegistry } from '../../../src/interpreter/FunctionRegistry' -import { LazilyTransformingAstService } from '../../../src/LazilyTransformingAstService' -import { NamedExpressions } from '../../../src/NamedExpressions' -import { Operations } from '../../../src/Operations' -import { buildColumnSearchStrategy } from '../../../src/Lookup/SearchStrategy' -import { CellContentParser } from '../../../src/CellContentParser' -import { DateTimeHelper } from '../../../src/DateTimeHelper' -import { NumberLiteralHelper } from '../../../src/NumberLiteralHelper' -import { ParserWithCaching } from '../../../src/parser' -import { ArraySizePredictor } from '../../../src/ArraySize' -import { NoSheetWithIdError} from '../../../src' -import {ExpectedValueOfTypeError} from '../../../src/errors' - -describe('Copy - paste integration', () => { - it('copy should validate arguments', () => { - const engine = HyperFormula.buildFromArray([]) - - expect(() => { - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 0, 42)) - }).toThrowError('Invalid arguments, expected width to be positive integer.') - - expect(() => { - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), -1, 42)) - }).toThrowError('Invalid arguments, expected width to be positive integer.') - - expect(() => { - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 3.14, 42)) - }).toThrowError('Invalid arguments, expected width to be positive integer.') - - expect(() => { - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 42, 0)) - }).toThrowError('Invalid arguments, expected height to be positive integer.') - - expect(() => { - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 42, -1)) - }).toThrowError('Invalid arguments, expected height to be positive integer.') - - expect(() => { - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 42, 3.14)) - }).toThrowError('Invalid arguments, expected height to be positive integer.') - - expect(() => { - engine.copy({} as SimpleCellRange) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellRange', 'source')) - }) - - it('paste raise error when there is nothing in clipboard', () => { - const engine = HyperFormula.buildFromArray([]) - - expect(() => { - engine.paste(adr('A2')) - }).toThrow(new NothingToPasteError()) - }) - - it('copy should return values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['foo', '=A1'], - ]) - - const values = engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - - expectArrayWithSameContent([1, 2], values[0]) - expectArrayWithSameContent(['foo', 1], values[1]) - }) - - it('copy should round return values', () => { - const engine = HyperFormula.buildFromArray([ - ['1.0000000001', '1.000000000000001'], - ]) - - const values = engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 2, 1)) - - expectArrayWithSameContent([1.0000000001, 1], values[0]) - }) - - it('should copy empty cell vertex', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=A1'] - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - const changes = engine.paste(adr('A2')) - - expectArrayWithSameContent([new ExportedCellChange(adr('A2'), null)], changes) - }) - - it('should work for single number', () => { - const engine = HyperFormula.buildFromArray([ - ['1'] - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('B1')) - - expect(engine.getCellValue(adr('B1'))).toEqual(1) - }) - - it('should work for parsing error', () => { - const sheet = [ - ['=SUM('], - ] - const engine = HyperFormula.buildFromArray(sheet) - - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('B1')) - - expect(engine.getCellFormula(adr('B1'))).toEqual('=SUM(') - }) - - it('should work for area', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['foo', 'bar'], - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - engine.paste(adr('C1')) - - expect(engine.getCellValue(adr('C1'))).toEqual(1) - expect(engine.getCellValue(adr('D1'))).toEqual(2) - expect(engine.getCellValue(adr('C2'))).toEqual('foo') - expect(engine.getCellValue(adr('D2'))).toEqual('bar') - }) - - it('should not round here', () => { - const engine = HyperFormula.buildFromArray([ - ['1.0000000001', '1.000000000000001'], - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 2, 1)) - engine.paste(adr('A2')) - - expect(engine.dependencyGraph.getCellValue(adr('A2'))).toEqual(1.0000000001) - expect(engine.dependencyGraph.getCellValue(adr('B2'))).toEqual(1.000000000000001) - }) - - it('should work for cell reference inside copied area', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=A1'], - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 2, 1)) - engine.paste(adr('A2')) - - const a1 = engine.dependencyGraph.fetchCell(adr('A1')) - const a2 = engine.dependencyGraph.fetchCell(adr('A2')) - const b2 = engine.dependencyGraph.fetchCell(adr('B2')) - - expect(engine.dependencyGraph.existsEdge(a2, b2)).toBe(true) - expect(engine.dependencyGraph.existsEdge(a1, b2)).toBe(false) - expect(engine.getCellValue(adr('B2'))).toEqual(1) - }) - - it('should work for absolute cell reference', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=$A$1'], - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('B1'), 1, 1)) - engine.paste(adr('B2')) - - const a1 = engine.dependencyGraph.fetchCell(adr('A1')) - const b1 = engine.dependencyGraph.fetchCell(adr('B1')) - const b2 = engine.dependencyGraph.fetchCell(adr('B2')) - - expect(engine.dependencyGraph.existsEdge(a1, b1)).toBe(true) - expect(engine.dependencyGraph.existsEdge(a1, b2)).toBe(true) - expect(engine.getCellValue(adr('B2'))).toEqual(1) - }) - - it('should work for cell reference pointing outside copied area', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=A1'], - ['2', ''], - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('B1'), 1, 1)) - engine.paste(adr('B2')) - - expect(engine.getCellValue(adr('B2'))).toEqual(2) - }) - - it('should return ref when pasted reference is out of scope', () => { - const engine = HyperFormula.buildFromArray([ - [null, null], - [null, '=A1'], - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('B2'), 1, 1)) - engine.paste(adr('A1')) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellSerialized(adr('A1'))).toEqual('=#REF!') - }) - - it('should return ref when pasted range is out of scope', () => { - const engine = HyperFormula.buildFromArray([ - [null, null], - [null, '=A1:B2'], - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('B2'), 1, 1)) - engine.paste(adr('A1')) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellSerialized(adr('A1'))).toEqual('=#REF!') - }) - - it('should return ref when pasted range is out of scope 2', () => { - const engine = HyperFormula.buildFromArray([ - [null, null, null], - [null, null, null], - [null, null, '=SUM(A1:B2)'], - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('C3'), 1, 1)) - engine.paste(adr('B2')) - - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellSerialized(adr('B2'))).toEqual('=SUM(#REF!)') - }) - - it('should return ref when pasted column range is out of scope', () => { - const engine = HyperFormula.buildFromArray([ - [null, null], - [null, '=A:B'], - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('B2'), 1, 1)) - engine.paste(adr('A1')) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellSerialized(adr('A1'))).toEqual('=#REF!') - }) - - it('should return ref when pasted row range is out of scope', () => { - const engine = HyperFormula.buildFromArray([ - [null, null], - [null, '=1:2'], - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('B2'), 1, 1)) - engine.paste(adr('A1')) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellSerialized(adr('A1'))).toEqual('=#REF!') - }) - - it('should create new range vertex - cell range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '3'], - ['2', '4'], - ['=SUM(A1:A2)'] - ]) - expect(Array.from(engine.dependencyGraph.rangeMapping.rangesInSheet(0)).length).toBe(1) - - engine.copy(AbsoluteCellRange.spanFrom(adr('A3'), 1, 1)) - engine.paste(adr('B3')) - - expect(engine.getCellValue(adr('B3'))).toEqual(7) - expect(Array.from(engine.dependencyGraph.rangeMapping.rangesInSheet(0)).length).toBe(2) - expect(engine.dependencyGraph.getRange(adr('B1'), adr('B2'))).not.toBeUndefined() - }) - - it('should create new range vertex - column range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '3', '5', '=SUM(A:B)'], - ['2', '4', '6', null], - ]) - expect(Array.from(engine.dependencyGraph.rangeMapping.rangesInSheet(0)).length).toBe(1) - - engine.copy(AbsoluteCellRange.spanFrom(adr('D1'), 1, 1)) - engine.paste(adr('E1')) - - expect(engine.getCellValue(adr('E1'))).toEqual(18) - expect(Array.from(engine.dependencyGraph.rangeMapping.rangesInSheet(0)).length).toBe(2) - expect(engine.dependencyGraph.getRange(colStart('B'), colEnd('C'))).not.toBeUndefined() - }) - - it('should create new range vertex - row range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ['=SUM(1:2)'] - ]) - expect(Array.from(engine.dependencyGraph.rangeMapping.rangesInSheet(0)).length).toBe(1) - - engine.copy(AbsoluteCellRange.spanFrom(adr('A4'), 1, 1)) - engine.paste(adr('A5')) - - expect(engine.getCellValue(adr('A5'))).toEqual(18) - expect(Array.from(engine.dependencyGraph.rangeMapping.rangesInSheet(0)).length).toBe(2) - expect(engine.dependencyGraph.getRange(rowStart(2), rowEnd(3))).not.toBeUndefined() - }) - - it('should update edges between infinite range and pasted values', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(2:3)', '1', '=SUM(1, 2)'] - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('B1'), 1, 1)) - engine.paste(adr('A2')) - engine.copy(AbsoluteCellRange.spanFrom(adr('C1'), 1, 1)) - engine.paste(adr('A3')) - - const range = engine.rangeMapping.getVertexOrThrow(rowStart(2), rowEnd(3)) - const a2 = engine.addressMapping.getCell(adr('A2')) - const a3 = engine.addressMapping.getCell(adr('A3')) - expect(engine.graph.existsEdge(a2!, range)).toBe(true) - expect(engine.graph.existsEdge(a3!, range)).toBe(true) - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('paste should return newly pasted values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=A1'], - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 2, 1)) - const changes = engine.paste(adr('A2')) - - expectArrayWithSameContent([ - new ExportedCellChange(adr('A2'), 1), - new ExportedCellChange(adr('B2'), 1), - ], changes) - }) - - it('should copy values from formula matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=TRANSPOSE(A1:B2)'], - ]) - expect(engine.arrayMapping.arrayMapping.size).toEqual(1) - - engine.copy(AbsoluteCellRange.spanFrom(adr('A3'), 2, 2)) - engine.paste(adr('A5')) - - expect(engine.arrayMapping.arrayMapping.size).toEqual(1) - expect(engine.getCellFormula(adr('A5'))).toBe(undefined) - expect(engine.getCellValue(adr('A5'))).toEqual(1) - expect(engine.getCellValue(adr('B5'))).toEqual(3) - expect(engine.getCellValue(adr('A6'))).toEqual(2) - expect(engine.getCellValue(adr('B6'))).toEqual(4) - }) - - it('should not be possible to paste onto formula matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=TRANSPOSE(A1:B2)'], - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - - expect(() => { - engine.paste(adr('A3')) - }).toThrowError('It is not possible to paste onto an array') - }) - - it('should not be possible to paste to not existing sheet', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=Sheet1!A2', '=Sheet2!A2']], - }) - - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 2, 1)) - - expect(() => { - engine.paste(adr('A1', 1)) - }).toThrowError('Invalid arguments, expected a valid target address.') - }) - - it('should copy references with absolute sheet id', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=Sheet1!A2', '=Sheet2!A2']], - 'Sheet2': [] - }) - - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 2, 1)) - engine.paste(adr('A1', 1)) - - expect(extractReference(engine, adr('A1', 1))).toEqual(CellAddress.relative(0, 1, 0)) - expect(extractReference(engine, adr('B1', 1))).toEqual(CellAddress.relative(-1, 1, 1)) - }) - - it('sheet reference should stay "relative" to other sheet', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=A2']], - 'Sheet2': [] - }) - - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A1', 1)) - - expect(extractReference(engine, adr('A1', 1))).toEqual(CellAddress.relative(0, 1)) - }) - - it('should throw error when trying to paste beyond sheet size limit', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ]) - - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - - expect(() => engine.paste(simpleCellAddress(0, Config.defaultConfig.maxColumns, 0))).toThrow(new SheetSizeLimitExceededError()) - expect(() => engine.paste(simpleCellAddress(0, 0, Config.defaultConfig.maxRows))).toThrow(new SheetSizeLimitExceededError()) - }) - - it('should throw error when trying to paste when targetLeftCorner is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildEmpty() - expect(() => { - engine.paste({} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'targetLeftCorner')) - }) -}) - -describe('Copy - paste integration - actions at the Operations layer', () => { - let operations: Operations - - beforeEach(() => { - const config = new Config() - const stats = new Statistics() - const namedExpressions = new NamedExpressions() - const functionRegistry = new FunctionRegistry(config) - const lazilyTransformingAstService = new LazilyTransformingAstService(stats) - const dependencyGraph = DependencyGraph.buildEmpty(lazilyTransformingAstService, config, functionRegistry, namedExpressions, stats) - const columnSearch = buildColumnSearchStrategy(dependencyGraph, config, stats) - const dateTimeHelper = new DateTimeHelper(config) - const numberLiteralHelper = new NumberLiteralHelper(config) - const cellContentParser = new CellContentParser(config, dateTimeHelper, numberLiteralHelper) - const parser = new ParserWithCaching( - config, - functionRegistry, - dependencyGraph.sheetReferenceRegistrar.ensureSheetRegistered.bind(dependencyGraph.sheetReferenceRegistrar) - ) - const arraySizePredictor = new ArraySizePredictor(config, functionRegistry) - operations = new Operations(config, dependencyGraph, columnSearch, cellContentParser, parser, stats, lazilyTransformingAstService, namedExpressions, arraySizePredictor) - }) - - it('should throw an error if you try and copy from a sheet that does not exist', () => { - const sheetId = 5 - expect(() => { - operations.getOldContent(simpleCellAddress(sheetId, 0, 0)) - }).toThrow(new NoSheetWithIdError(sheetId)) - }) -}) - -describe('isClipboardEmpty', () => { - it('when just engine initialized', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ]) - - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('after copy', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ]) - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - - expect(engine.isClipboardEmpty()).toBe(false) - }) - - it('after copy-paste', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ]) - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A2')) - - expect(engine.isClipboardEmpty()).toBe(false) - }) - - it('after cut', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ]) - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - - expect(engine.isClipboardEmpty()).toBe(false) - }) - - it('after cut-paste', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ]) - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A2')) - - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('after clearClipboard', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=A1'], - ]) - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 2, 1)) - engine.clearClipboard() - - expect(engine.isClipboardEmpty()).toBe(true) - }) -}) diff --git a/test/unit/cruds/cut-paste.spec.ts b/test/unit/cruds/cut-paste.spec.ts deleted file mode 100644 index 37a5e454bb..0000000000 --- a/test/unit/cruds/cut-paste.spec.ts +++ /dev/null @@ -1,1024 +0,0 @@ -import {ErrorType, HyperFormula, NoSheetWithIdError} from '../../../src' -import {AbsoluteCellRange, SimpleCellRange} from '../../../src/AbsoluteCellRange' -import {EmptyCellVertex} from '../../../src/DependencyGraph' -import {EmptyValue} from '../../../src/interpreter/InterpreterValue' -import {ColumnIndex} from '../../../src/Lookup/ColumnIndex' -import {CellAddress} from '../../../src/parser' -import { - adr, - detailedError, - expectArrayWithSameContent, - expectEngineToBeTheSameAs, - extractMatrixRange, - extractRange, - extractReference, graphEdgesCount, -} from '../testUtils' -import {ExpectedValueOfTypeError} from '../../../src/errors' - -describe('Address dependencies, moved formulas', () => { - it('should update dependency to external cell when not overriding it', () => { - const engine = HyperFormula.buildFromArray([ - ['foo'], - ['=A1'], - ['=$A1'], - ['=A$1'], - ['=$A$1'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A2'), 1, 4)) - engine.paste(adr('B1')) - - expect(extractReference(engine, adr('B1'))).toEqual(CellAddress.relative(-1, 0)) - expect(extractReference(engine, adr('B2'))).toEqual(CellAddress.absoluteCol(0, -1)) - expect(extractReference(engine, adr('B3'))).toEqual(CellAddress.absoluteRow(-1, 0)) - expect(extractReference(engine, adr('B4'))).toEqual(CellAddress.absolute(0, 0)) - }) - - it('should return #CYCLE when overriding referred dependency to external cell', () => { - const engine = HyperFormula.buildFromArray([ - ['=B1', '1'], - ['=$B2', '2'], - ['=B$3', '3'], - ['=$B$4', '4'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 4)) - engine.paste(adr('B1')) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('B3'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('B4'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellFormula(adr('B1'))).toEqual('=B1') - expect(engine.getCellFormula(adr('B2'))).toEqual('=$B2') - expect(engine.getCellFormula(adr('B3'))).toEqual('=B$3') - expect(engine.getCellFormula(adr('B4'))).toEqual('=$B$4') - }) - - it('should work when overriding moved dependency', () => { - const engine = HyperFormula.buildFromArray([ - ['=B2', '1'], - ['3', '2'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 2), adr('B1')) - - expect(engine.getCellValue(adr('B1'))).toEqual(3) - expect(engine.getCellValue(adr('B2'))).toEqual(3) - }) - - it('should update internal dependency when overriding dependent cell', () => { - const engine = HyperFormula.buildFromArray([ - ['=B$2', null], - [null, null], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - engine.paste(adr('B2')) - - expect(extractReference(engine, adr('B2'))).toEqual(CellAddress.absoluteRow(1, 2)) - }) - - it('should update coordinates to internal dependency', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=A1'], - ['2', '=$A2'], - ['3', '=A$3'], - ['4', '=$A$4'], - ]) - - expect(extractReference(engine, adr('B3'))).toEqual(CellAddress.absoluteRow(-1, 2)) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 2, 4)) - engine.paste(adr('B2')) - - expect(extractReference(engine, adr('C2'))).toEqual(CellAddress.relative(-1, 0)) - expect(extractReference(engine, adr('C3'))).toEqual(CellAddress.absoluteCol(1, 0)) - expect(extractReference(engine, adr('C4'))).toEqual(CellAddress.absoluteRow(-1, 3)) - expect(extractReference(engine, adr('C5'))).toEqual(CellAddress.absolute(1, 4)) - }) - - it('should evaluate formula when overriding external formula dependency', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ['=SUM(B1:B2)'], - ['=B3'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 3)) - engine.paste(adr('B1')) - - expect(engine.getCellValue(adr('A4'))).toEqual(4) - expect(engine.getCellValue(adr('A5'))).toEqual(5) - }) -}) - -describe('Move cells', () => { - it('should move static content', () => { - const engine = HyperFormula.buildFromArray([ - ['foo'], - [null], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A2')) - - expect(engine.getCellValue(adr('A2'))).toEqual('foo') - }) - - it('should update reference of moved formula when moving to other sheet', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['foo'], - ['=A1'], - ], - Sheet2: [ - [null /* =A1 */], - ], - }) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A2'), 1, 1)) - engine.paste(adr('B1', 1)) - - expect(extractReference(engine, adr('B1', 1))).toEqual(CellAddress.relative(-1, 0)) - }) - - it('should update reference', () => { - const engine = HyperFormula.buildFromArray([ - ['foo' /* foo */], - ['=A1'], - ['=$A1'], - ['=A$1'], - ['=$A$1'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('B1')) - - expect(extractReference(engine, adr('A2'))).toEqual(CellAddress.relative(1, -1)) - expect(extractReference(engine, adr('A3'))).toEqual(CellAddress.absoluteCol(1, -2)) - expect(extractReference(engine, adr('A4'))).toEqual(CellAddress.absoluteRow(1, 0)) - expect(extractReference(engine, adr('A5'))).toEqual(CellAddress.absolute(1, 0)) - }) - - it('value moved has appropriate edges', () => { - const engine = HyperFormula.buildFromArray([ - ['foo' /* foo */], - ['=A1'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('B1')) - - const movedVertex = engine.dependencyGraph.fetchCell(adr('B1')) - expect(engine.graph.existsEdge(movedVertex, engine.dependencyGraph.fetchCell(adr('A2')))).toBe(true) - }) - - it('should update reference when moving to different sheet', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['foo'], - ['=A1'], - ], - Sheet2: [], - }) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('B1', 1)) - - const reference = extractReference(engine, adr('A2')) - expect(reference).toEqual(CellAddress.relative(1, -1)) - }) - - it('should override and remove formula', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['=A1'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A2')) - - expect(graphEdgesCount(engine.graph)).toBe(0) - expect(engine.graph.getNodes().length).toBe(1) - expect(engine.getCellValue(adr('A1'))).toBe(null) - expect(engine.getCellValue(adr('A2'))).toBe(1) - }) - - it('moving empty vertex', () => { - const engine = HyperFormula.buildFromArray([ - [null, '42'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('B1')) - - expect(engine.addressMapping.getCell(adr('A1'))).toBe(undefined) - expect(engine.addressMapping.getCell(adr('B1'))).toBe(undefined) - }) - - it('replacing formula dependency with null one', () => { - const engine = HyperFormula.buildFromArray([ - [null, '42'], - ['=B1'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('B1')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null], - ['=B1'], - ])) - }) - - it('moving empty vertex to empty vertex', () => { - const engine = HyperFormula.buildFromArray([ - [null, null], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('B1')) - - expect(engine.addressMapping.getCell(adr('A1'))).toBe(undefined) - expect(engine.addressMapping.getCell(adr('B1'))).toBe(undefined) - }) - - it('should adjust edges properly', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=A1'], - ['2', '=A2'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A2')) - - const b1 = engine.addressMapping.getCell(adr('B1')) - const b2 = engine.addressMapping.getCell(adr('B2')) - const source = engine.addressMapping.getCell(adr('A1')) - const target = engine.addressMapping.getCell(adr('A2')) - - expect(graphEdgesCount(engine.graph)).toBe( - 2, // A2 -> B1, A2 -> B2 - ) - expect(engine.graph.getNodes().length).toBe( - +2 // formulas - + 1, // A2 - ) - - expect(source).toBe(undefined) - expect(engine.graph.existsEdge(target!, b2!)).toBe(true) - expect(engine.graph.existsEdge(target!, b1!)).toBe(true) - expect(engine.getCellValue(adr('A2'))).toBe(1) - }) -}) - -describe('moving ranges', () => { - it('should not update range when only part of it is moved', () => { - const engine = HyperFormula.buildFromArray([ - ['1' /* 1 */], - ['2'], - ['=SUM(A1:A2)'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('B1')) - - const range = extractRange(engine, adr('A3')) - expect(range.start).toEqual(adr('A1')) - expect(range.end).toEqual(adr('A2')) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - - const a1 = engine.addressMapping.getCell(adr('A1')) - const a2 = engine.addressMapping.getCell(adr('A2')) - const a1a2 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A2')) - expect(a1).toBeInstanceOf(EmptyCellVertex) - expect(engine.graph.existsEdge(a1!, a1a2)).toBe(true) - expect(engine.graph.existsEdge(a2!, a1a2)).toBe(true) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '1'], - ['2'], - ['=SUM(A1:A2)'], - ])) - }) - - it('should update moved range', () => { - const engine = HyperFormula.buildFromArray([ - ['1' /* 1 */], - ['2' /* 2 */], - ['=SUM(A1:A2)'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 2)) - engine.paste(adr('B1')) - - expect(engine.rangeMapping.getRangeVertex(adr('B1'), adr('B2'))).not.toBe(undefined) - - const range = extractRange(engine, adr('A3')) - expect(range.start).toEqual(adr('B1')) - expect(range.end).toEqual(adr('B2')) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - - expect(engine.addressMapping.getCell(adr('A1'))).toBe(undefined) - expect(engine.addressMapping.getCell(adr('A2'))).toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '1'], - [null, '2'], - ['=SUM(B1:B2)'], - ])) - }) - - it('should not be possible to move area with matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - ]) - - expect(() => { - engine.cut(AbsoluteCellRange.spanFrom(adr('A2'), 2, 2)) - engine.paste(adr('C1')) - }).toThrowError('Cannot perform this operation, source location has an array inside.') - }) - - it('should not be possible to move cells to area with matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - ]) - - expect(() => { - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 2, 1)) - engine.paste(adr('A2')) - }).toThrowError('Cannot perform this operation, target location has an array inside.') - }) - - it('should not be possible to cut when source is a malformed SimpleCellRange', () => { - const engine = HyperFormula.buildEmpty() - expect(() => { - engine.cut({} as SimpleCellRange) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellRange', 'source')) - }) - - it('should adjust edges when moving part of range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=SUM(A1:A2)'], - ['2', '=A2'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A2')) - - const b1 = engine.addressMapping.getCell(adr('B1')) - const b2 = engine.addressMapping.getCell(adr('B2')) - const source = engine.addressMapping.getCell(adr('A1')) - const target = engine.addressMapping.getCell(adr('A2')) - const range = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A2')) - - expect(source).toBeInstanceOf(EmptyCellVertex) - expect(source!.getCellValue()).toBe(EmptyValue) - expect(engine.graph.getNodes().length).toBe( - +2 // formulas - + 1 // A2 - + 1 // A1 (Empty) - + 1, // A1:A2 range - ) - expect(graphEdgesCount(engine.graph)).toBe( - +2 // A1 (Empty) -> A1:A2, A2 -> A1:A2 - + 1 // A1:A2 -> B1 - + 1, // A2 -> B2 - ) - expect(engine.graph.existsEdge(target!, b2!)).toBe(true) - expect(engine.graph.existsEdge(source!, range)).toBe(true) - expect(engine.graph.existsEdge(target!, range)).toBe(true) - expect(engine.graph.existsEdge(range, b1!)).toBe(true) - expect(engine.getCellValue(adr('A2'))).toBe(1) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '=SUM(A1:A2)'], - ['1', '=A2'], - ])) - }) - - it('should adjust edges when moving whole range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=SUM(A1:A2)'], - ['2', '=A2'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 2)) - engine.paste(adr('C1')) - - const a1 = engine.addressMapping.getCell(adr('A1')) - const a2 = engine.addressMapping.getCell(adr('A2')) - const b1 = engine.addressMapping.getCell(adr('B1')) - const c1 = engine.addressMapping.getCell(adr('C1')) - const c2 = engine.addressMapping.getCell(adr('C2')) - const range = engine.rangeMapping.getVertexOrThrow(adr('C1'), adr('C2')) - - expect(a1).toBe(undefined) - expect(a2).toBe(undefined) - - expect(engine.graph.getNodes().length).toBe( - +2 // formulas - + 2 // C1, C2 - + 1, // C1:C2 range - ) - expect(graphEdgesCount(engine.graph)).toBe( - +2 // C1 -> C1:C2, C2 -> C1:C2 - + 1 // C1:C2 -> B1 - + 1, // C2 -> B2 - ) - - expect(engine.graph.existsEdge(c1!, range)).toBe(true) - expect(engine.graph.existsEdge(c2!, range)).toBe(true) - expect(engine.graph.existsEdge(range, b1!)).toBe(true) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '=SUM(C1:C2)', '1'], - [null, '=C2', '2'], - ])) - }) - - it('should adjust edges when moving smaller range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', null /* 1 */], - ['2', '=SUM(A1:A2)' /* 2 */], - ['3', '=SUM(A1:A3)'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 2)) - engine.paste(adr('C1')) - - /* ranges in formulas*/ - expect(extractRange(engine, adr('B2'))).toEqual(new AbsoluteCellRange( - adr('C1'), - adr('C2'), - )) - expect(extractRange(engine, adr('B3'))).toEqual(new AbsoluteCellRange( - adr('A1'), - adr('A3'), - )) - - /* edges */ - const c1c2 = engine.rangeMapping.getVertexOrThrow(adr('C1'), adr('C2')) - const a1a3 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A3')) - expect(engine.graph.existsEdge(c1c2, a1a3)).toBe(false) - - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('A1'))!, a1a3)).toBe(true) - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('A2'))!, a1a3)).toBe(true) - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('A3'))!, a1a3)).toBe(true) - - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('C1'))!, c1c2)).toBe(true) - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('C2'))!, c1c2)).toBe(true) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null, '1'], - [null, '=SUM(C1:C2)', '2'], - ['3', '=SUM(A1:A3)'], - ])) - }) - - it('should adjust edges when moving smaller ranges - more complex', () => { - const engine = HyperFormula.buildFromArray([ - ['1', null /* 1 */], - ['2', '=SUM(A1:A2)' /* 2 */], - ['3', '=SUM(A1:A3)' /* 3 */], - ['4', '=SUM(A1:A4)'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 3)) - engine.paste(adr('C1')) - - /* edges */ - const c1c2 = engine.rangeMapping.getVertexOrThrow(adr('C1'), adr('C2')) - const c1c3 = engine.rangeMapping.getVertexOrThrow(adr('C1'), adr('C3')) - const a1a4 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A4')) - - expect(engine.graph.existsEdge(c1c2, c1c3)).toBe(true) - expect(engine.graph.existsEdge(c1c3, a1a4)).toBe(false) - - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('A1'))!, a1a4)).toBe(true) - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('A2'))!, a1a4)).toBe(true) - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('A3'))!, a1a4)).toBe(true) - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('A4'))!, a1a4)).toBe(true) - - const c1 = engine.addressMapping.getCell(adr('C1')) - const c2 = engine.addressMapping.getCell(adr('C2')) - const c3 = engine.addressMapping.getCell(adr('C3')) - expect(engine.graph.existsEdge(c1!, c1c2)).toBe(true) - expect(engine.graph.existsEdge(c2!, c1c2)).toBe(true) - expect(engine.graph.existsEdge(c1!, c1c3)).toBe(false) - expect(engine.graph.existsEdge(c2!, c1c3)).toBe(false) - expect(engine.graph.existsEdge(c3!, c1c3)).toBe(true) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null, '1'], - [null, '=SUM(C1:C2)', '2'], - [null, '=SUM(C1:C3)', '3'], - ['4', '=SUM(A1:A4)'], - ])) - }) - - it('move wider dependent ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ['=SUM(A1:B1)', '=SUM(A1:B2)', '=SUM(A1:B3)'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - engine.paste(adr('C1')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null, '1', '2'], - [null, null, '3', '4'], - ['5', '6'], - ['=SUM(C1:D1)', '=SUM(C1:D2)', '=SUM(A1:B3)'], - ])) - }) -}) - -describe('overlapping areas', () => { - it('overlapped rows', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - engine.paste(adr('A2')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null], - ['1', '2'], - ['3', '4'], - ])) - }) - - it('overlapped rows - opposite way', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A2'), 2, 2)) - engine.paste(adr('A1')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['3', '4'], - ['5', '6'], - [null, null], - ])) - }) - - it('overlapped columns', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - engine.paste(adr('B1')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '1', '2'], - [null, '4', '5'], - ])) - }) - - it('overlapped columns - opposite way', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('B1'), 2, 2)) - engine.paste(adr('A1')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['2', '3', null], - ['5', '6', null], - ])) - }) - - it('moving along diagonal', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 3, 2)) - engine.paste(adr('B2')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null, null, null], - [null, '1', '2', '3'], - [null, '4', '5', '6'], - ])) - }) - - it('overlapped rows with ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ['=SUM(A1:B2)', '=SUM(A1:B3)', '=SUM(A2:B2)'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - engine.paste(adr('A2')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null], - ['1', '2'], - ['3', '4'], - ['=SUM(A2:B3)', '=SUM(A1:B3)', '=SUM(A3:B3)'], - ])) - }) - - it('overlapped columns with ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ['=SUM(A1:B2)', '=SUM(A1:C2)', '=SUM(B1:B2)'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - engine.paste(adr('B1')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '1', '2'], - [null, '4', '5'], - ['=SUM(B1:C2)', '=SUM(A1:C2)', '=SUM(C1:C2)'], - ])) - }) - - it('expecting range to be same when moving part of a range inside this range', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - ['=SUM(A1:A3)'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A2')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null], - ['1'], - ['3'], - ['=SUM(A1:A3)'], - ])) - }) - - it('expecting range to be same when moving part of a range outside of this range', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - [null], - ['=SUM(A1:A3)'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A4')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null], - ['2'], - ['3'], - ['1'], - ['=SUM(A1:A3)'], - ])) - }) - - it('expecting range to be same when moving part of a range outside of this range - row', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', null], - ['=SUM(A1:C1)'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('D1')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '2', '3', '1'], - ['=SUM(A1:C1)'], - ])) - }) - - it('ArrayFormulaVertex#formula should be updated', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=TRANSPOSE(A1:B2)'], - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - engine.paste(adr('C1', 0)) - - expect(extractMatrixRange(engine, adr('A3'))).toEqual(new AbsoluteCellRange(adr('C1'), adr('D2'))) - }) - - it('ArrayFormulaVertex#formula should be updated when different sheets', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '2'], - ['3', '4'], - ], - Sheet2: [ - ['=TRANSPOSE(Sheet1!A1:B2)'], - ], - }) - - expect(extractMatrixRange(engine, adr('A1', 1))).toEqual(new AbsoluteCellRange(adr('A1'), adr('B2'))) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - engine.paste(adr('C1', 0)) - - expect(extractMatrixRange(engine, adr('A1', 1))).toEqual(new AbsoluteCellRange(adr('C1'), adr('D2'))) - }) -}) - -describe('column index', () => { - it('should update column index when moving cell', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['1'], - ['=VLOOKUP(1, A1:A2, 1, TRUE())'], - ], {useColumnIndex: true}) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('B1')) - - const index = engine.columnSearch as ColumnIndex - expectArrayWithSameContent([1, 2], index.getValueIndex(0, 0, 1).index) - expectArrayWithSameContent([0], index.getValueIndex(0, 1, 1).index) - }) - - it('should update column index when moving cell - REFs', () => { - const engine = HyperFormula.buildFromArray([ - ['=B1', '1'], - ['3', '2'], - ], {useColumnIndex: true}) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 2)) - engine.paste(adr('B1')) - - const index = engine.columnSearch as ColumnIndex - expectArrayWithSameContent([], index.getValueIndex(0, 0, 2).index) - expectArrayWithSameContent([], index.getValueIndex(0, 0, 3).index) - expectArrayWithSameContent([], index.getValueIndex(0, 1, 1).index) - expectArrayWithSameContent([1], index.getValueIndex(0, 1, 3).index) - }) - - it('should update column index when source and target overlaps', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4', '5'], - [null, '6', '7'], - ], {useColumnIndex: true}) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - engine.paste(adr('B2')) - - const index = engine.columnSearch as ColumnIndex - expect(index.getColumnMap(0, 0).size).toEqual(0) - expect(index.getColumnMap(0, 1).size).toEqual(2) - expectArrayWithSameContent([1], index.getValueIndex(0, 1, 1).index) - expectArrayWithSameContent([2], index.getValueIndex(0, 1, 3).index) - expect(index.getColumnMap(0, 2).size).toEqual(2) - expectArrayWithSameContent([1], index.getValueIndex(0, 2, 2).index) - expectArrayWithSameContent([2], index.getValueIndex(0, 2, 4).index) - }) - - it('should update column index when source and target overlaps - oposite way', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4', '5'], - [null, '6', '7'], - ], {useColumnIndex: true}) - - engine.cut(AbsoluteCellRange.spanFrom(adr('B2'), 2, 2)) - engine.paste(adr('A1')) - - const index = engine.columnSearch as ColumnIndex - expect(index.getColumnMap(0, 0).size).toEqual(2) - expectArrayWithSameContent([0], index.getValueIndex(0, 0, 4).index) - expectArrayWithSameContent([1], index.getValueIndex(0, 0, 6).index) - expect(index.getColumnMap(0, 0).size).toEqual(2) - expectArrayWithSameContent([0], index.getValueIndex(0, 1, 5).index) - expectArrayWithSameContent([1], index.getValueIndex(0, 1, 7).index) - expect(index.getColumnMap(0, 2).size).toEqual(0) - }) -}) - -describe('move cells with matrices', () => { - it('should not be possible to move part of formula matrix', function() { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - ]) - - expect(() => { - engine.cut(AbsoluteCellRange.spanFrom(adr('A2'), 1, 1)) - engine.paste(adr('A3')) - }).toThrowError('Cannot perform this operation, source location has an array inside.') - }) - - it('should not be possible to move formula matrix at all', function() { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - ]) - - expect(() => { - engine.cut(AbsoluteCellRange.spanFrom(adr('A2'), 2, 1)) - engine.paste(adr('A3')) - }).toThrowError('Cannot perform this operation, source location has an array inside.') - }) -}) - -describe('aborting cut paste', () => { - it('should be aborted when addRows is done before paste', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'] - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.addRows(0, [1, 1]) - - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('should be aborted when removeRows is done before paste', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'] - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.removeRows(0, [1, 1]) - - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('should be aborted when addColumns is done before paste', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'] - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.addColumns(0, [1, 1]) - - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('should be aborted when removeColumns is done before paste', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'] - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.removeColumns(0, [1, 1]) - - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('should be aborted when moveCells is done before paste', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'] - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.moveCells(AbsoluteCellRange.spanFrom(adr('B1'), 1, 1), adr('C1')) - - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('should be aborted when sheet is removed', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['1']], - 'Sheet2': [] - }) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.removeSheet(1) - - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('should be aborted when setCellContents is done', () => { - const engine = HyperFormula.buildFromArray([ - ['1'] - ]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.setCellContents(adr('B1'), 'foo') - - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('should be aborted when sheet is cleared', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['1']], - 'Sheet2': [] - }) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.clearSheet(1) - - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('should be aborted when sheet content is replaced', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['1']], - 'Sheet2': [] - }) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.setSheetContent(1, []) - - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('should not be aborted when adding new sheet', () => { - const engine = HyperFormula.buildFromArray([['1']]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.addSheet() - - expect(engine.isClipboardEmpty()).toBe(false) - }) - - it('should not be aborted when addRows is not successful', () => { - const engine = HyperFormula.buildFromArray([['1']]) - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - - expect(() => { - engine.addRows(1, [1, 1]) - }).toThrow(new NoSheetWithIdError(1)) - - expect(engine.isClipboardEmpty()).toBe(false) - }) - - it('should be aborted when doing undo', () => { - const engine = HyperFormula.buildFromArray([['1']]) - engine.setCellContents(adr('A1'), 42) - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - - engine.undo() - - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('should be aborted when doing redo', () => { - const engine = HyperFormula.buildFromArray([['1']]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - - engine.redo() - - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('should be aborted when swapping rows', () => { - const engine = HyperFormula.buildFromArray([['1']]) - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.swapRowIndexes(0, [[0, 0]]) - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('should be aborted when swapping columns', () => { - const engine = HyperFormula.buildFromArray([['1']]) - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.swapColumnIndexes(0, [[0, 0]]) - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('should be aborted when setting row order', () => { - const engine = HyperFormula.buildFromArray([['1']]) - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.setRowOrder(0, [0]) - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('should be aborted when setting column order', () => { - const engine = HyperFormula.buildFromArray([['1']]) - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.setColumnOrder(0, [0]) - expect(engine.isClipboardEmpty()).toBe(true) - }) -}) diff --git a/test/unit/cruds/move-cells.spec.ts b/test/unit/cruds/move-cells.spec.ts deleted file mode 100644 index e05a75742f..0000000000 --- a/test/unit/cruds/move-cells.spec.ts +++ /dev/null @@ -1,1118 +0,0 @@ -import {ErrorType, HyperFormula, SimpleCellAddress, SimpleCellRange} from '../../../src' -import {AbsoluteCellRange} from '../../../src/AbsoluteCellRange' -import {simpleCellAddress} from '../../../src/Cell' -import {Config} from '../../../src/Config' -import {EmptyCellVertex, ScalarFormulaVertex} from '../../../src/DependencyGraph' -import {SheetSizeLimitExceededError} from '../../../src/errors' -import {EmptyValue} from '../../../src/interpreter/InterpreterValue' -import {ColumnIndex} from '../../../src/Lookup/ColumnIndex' -import {CellAddress} from '../../../src/parser' -import { - adr, - colEnd, - colStart, - detailedError, - expectArrayWithSameContent, - expectEngineToBeTheSameAs, - extractColumnRange, - extractMatrixRange, - extractRange, - extractReference, - extractRowRange, graphEdgesCount, - rowEnd, - rowStart, -} from '../testUtils' -import {ExpectedValueOfTypeError} from '../../../src/errors' - -describe('Moving rows - checking if its possible', () => { - it('source top left corner should have valid coordinates', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToMoveCells(AbsoluteCellRange.spanFrom(simpleCellAddress(0, -1, 0), 1, 1), adr('A2'))).toEqual(false) - }) - - it('source top left corner should be in existing sheet', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToMoveCells(AbsoluteCellRange.spanFrom(adr('A1', 1), 1, 1), adr('A2'))).toEqual(false) - }) - - it('target top left corner should have valid coordinates', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToMoveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), simpleCellAddress(0, -1, 0))).toEqual(false) - }) - - it('target top left corner should be in existing sheet', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToMoveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2', 1))).toEqual(false) - }) - - it('no if we move the range which overlaps with matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=TRANSPOSE(A1:B2)'], - [], - ['13'], - ]) - - expect(engine.isItPossibleToMoveCells(AbsoluteCellRange.spanFrom(adr('A2'), 1, 2), adr('A10'))).toBe(false) - }) - - it('no if we move to range which overlaps with matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=TRANSPOSE(A1:B2)'], - [], - ['13'], - ]) - - expect(engine.isItPossibleToMoveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 2), adr('B2'))).toBe(false) - }) - - it('no if we move beyond sheet size limits', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ]) - - const cellInLastColumn = simpleCellAddress(0, Config.defaultConfig.maxColumns - 1, 0) - const cellInLastRow = simpleCellAddress(0, 0, Config.defaultConfig.maxRows - 1) - expect(engine.isItPossibleToMoveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), cellInLastColumn)).toEqual(true) - expect(engine.isItPossibleToMoveCells(AbsoluteCellRange.spanFrom(adr('A1'), 2, 1), cellInLastColumn)).toEqual(false) - expect(engine.isItPossibleToMoveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), cellInLastRow)).toEqual(true) - expect(engine.isItPossibleToMoveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 2), cellInLastRow)).toEqual(false) - }) - - it('yes otherwise', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToMoveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2'))).toBe(true) - }) - - it('should throw error if testing with a source that is a malformed SimpleCellRange', () => { - const engine = HyperFormula.buildFromArray([ - [null, null], - ]) - expect(() => { - engine.isItPossibleToMoveCells({} as SimpleCellRange, adr('A2')) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellRange', 'source')) - }) - - it('should throw error if testing with a destinationLeftCorner that is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildFromArray([ - [null, null], - ]) - expect(() => { - engine.isItPossibleToMoveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), {} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'destinationLeftCorner')) - }) -}) - -describe('Address dependencies, moved formulas', () => { - it('should update dependency to external cell when not overriding it', () => { - const engine = HyperFormula.buildFromArray([ - ['foo'], - ['=A1'], - ['=$A1'], - ['=A$1'], - ['=$A$1'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A2'), 1, 4), adr('B1')) - - expect(extractReference(engine, adr('B1'))).toEqual(CellAddress.relative(-1, 0)) - expect(extractReference(engine, adr('B2'))).toEqual(CellAddress.absoluteCol(0, -1)) - expect(extractReference(engine, adr('B3'))).toEqual(CellAddress.absoluteRow(-1, 0)) - expect(extractReference(engine, adr('B4'))).toEqual(CellAddress.absolute(0, 0)) - }) - - it('should return #CYCLE when overriding referred dependency to external cell', () => { - const engine = HyperFormula.buildFromArray([ - ['=B1', '1'], - ['=$B2', '2'], - ['=B$3', '3'], - ['=$B$4', '4'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 4), adr('B1')) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('B3'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('B4'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellFormula(adr('B1'))).toEqual('=B1') - expect(engine.getCellFormula(adr('B2'))).toEqual('=$B2') - expect(engine.getCellFormula(adr('B3'))).toEqual('=B$3') - expect(engine.getCellFormula(adr('B4'))).toEqual('=$B$4') - }) - - it('should work when overriding moved dependency', () => { - const engine = HyperFormula.buildFromArray([ - ['=B2', '1'], - ['3', '2'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 2), adr('B1')) - - expect(engine.getCellValue(adr('B1'))).toEqual(3) - expect(engine.getCellValue(adr('B2'))).toEqual(3) - }) - - it('should update internal dependency when overriding dependent cell', () => { - const engine = HyperFormula.buildFromArray([ - ['=B$2', null], - [null, null], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2), adr('B2')) - - expect(extractReference(engine, adr('B2'))).toEqual(CellAddress.absoluteRow(1, 2)) - }) - - it('should update coordinates to internal dependency', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=A1'], - ['2', '=$A2'], - ['3', '=A$3'], - ['4', '=$A$4'], - ]) - - expect(extractReference(engine, adr('B3'))).toEqual(CellAddress.absoluteRow(-1, 2)) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 2, 4), adr('B2')) - - expect(extractReference(engine, adr('C2'))).toEqual(CellAddress.relative(-1, 0)) - expect(extractReference(engine, adr('C3'))).toEqual(CellAddress.absoluteCol(1, 0)) - expect(extractReference(engine, adr('C4'))).toEqual(CellAddress.absoluteRow(-1, 3)) - expect(extractReference(engine, adr('C5'))).toEqual(CellAddress.absolute(1, 4)) - }) - - it('should evaluate formula when overriding external formula dependency', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ['=SUM(B1:B2)'], - ['=B3'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 3), adr('B1')) - - expect(engine.getCellValue(adr('A4'))).toEqual(4) - expect(engine.getCellValue(adr('A5'))).toEqual(5) - }) -}) - -describe('Move cells', () => { - it('should move static content', () => { - const engine = HyperFormula.buildFromArray([ - ['foo'], - [null], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2')) - - expect(engine.getCellValue(adr('A2'))).toEqual('foo') - }) - - it('should update reference of moved formula when moving to other sheet', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['foo'], - ['=A1'], - ], - Sheet2: [ - [null /* =A1 */], - ], - }) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A2'), 1, 1), adr('B1', 1)) - - expect(extractReference(engine, adr('B1', 1))).toEqual(CellAddress.relative(-1, 0)) - }) - - it('should update address in vertex', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['foo'], - ['=A1'], - ], - Sheet2: [ - [null /* =A1 */], - ], - }) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A2'), 1, 1), adr('B1', 1)) - - const vertex = engine.dependencyGraph.fetchCell(adr('B1', 1)) as ScalarFormulaVertex - expect(vertex.getAddress(engine.lazilyTransformingAstService)).toEqual(adr('B1', 1)) - }) - - it('should update reference', () => { - const engine = HyperFormula.buildFromArray([ - ['foo' /* foo */], - ['=A1'], - ['=$A1'], - ['=A$1'], - ['=$A$1'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('B1')) - - expect(extractReference(engine, adr('A2'))).toEqual(CellAddress.relative(1, -1)) - expect(extractReference(engine, adr('A3'))).toEqual(CellAddress.absoluteCol(1, -2)) - expect(extractReference(engine, adr('A4'))).toEqual(CellAddress.absoluteRow(1, 0)) - expect(extractReference(engine, adr('A5'))).toEqual(CellAddress.absolute(1, 0)) - }) - - it('value moved has appropriate edges', () => { - const engine = HyperFormula.buildFromArray([ - ['foo' /* foo */], - ['=A1'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('B1')) - - const movedVertex = engine.dependencyGraph.fetchCell(adr('B1')) - expect(engine.graph.existsEdge(movedVertex, engine.dependencyGraph.fetchCell(adr('A2')))).toBe(true) - }) - - it('should update reference when moving to different sheet', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['foo'], - ['=A1'], - ], - Sheet2: [], - }) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('B1', 1)) - - const reference = extractReference(engine, adr('A2')) - expect(reference).toEqual(CellAddress.relative(1, -1)) - }) - - it('should override and remove formula', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['=A1'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2')) - - expect(graphEdgesCount(engine.graph)).toBe(0) - expect(engine.graph.getNodes().length).toBe(1) - expect(engine.getCellValue(adr('A1'))).toBe(null) - expect(engine.getCellValue(adr('A2'))).toBe(1) - }) - - it('moving empty vertex', () => { - const engine = HyperFormula.buildFromArray([ - [null, '42'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('B1')) - - expect(engine.addressMapping.getCell(adr('A1'))).toBe(undefined) - expect(engine.addressMapping.getCell(adr('B1'))).toBe(undefined) - }) - - it('replacing formula dependency with null one', () => { - const engine = HyperFormula.buildFromArray([ - [null, '42'], - ['=B1'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('B1')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null], - ['=B1'], - ])) - }) - - it('moving empty vertex to empty vertex', () => { - const engine = HyperFormula.buildFromArray([ - [null, null], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('B1')) - - expect(engine.addressMapping.getCell(adr('A1'))).toBe(undefined) - expect(engine.addressMapping.getCell(adr('B1'))).toBe(undefined) - }) - - it('should adjust edges properly', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=A1'], - ['2', '=A2'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2')) - - const b1 = engine.addressMapping.getCell(adr('B1')) - const b2 = engine.addressMapping.getCell(adr('B2')) - const source = engine.addressMapping.getCell(adr('A1')) - const target = engine.addressMapping.getCell(adr('A2')) - - expect(graphEdgesCount(engine.graph)).toBe( - 2, // A2 -> B1, A2 -> B2 - ) - expect(engine.graph.getNodes().length).toBe( - +2 // formulas - + 1, // A2 - ) - - expect(source).toBe(undefined) - expect(engine.graph.existsEdge(target!, b2!)).toBe(true) - expect(engine.graph.existsEdge(target!, b1!)).toBe(true) - expect(engine.getCellValue(adr('A2'))).toBe(1) - }) - - it('should throw error trying to move cells beyond sheet limits', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ]) - - const cellInLastColumn = simpleCellAddress(0, Config.defaultConfig.maxColumns - 1, 0) - const cellInLastRow = simpleCellAddress(0, 0, Config.defaultConfig.maxRows - 1) - - expect(() => engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 2, 1), cellInLastColumn)).toThrow(new SheetSizeLimitExceededError()) - expect(() => engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 2), cellInLastRow)).toThrow(new SheetSizeLimitExceededError()) - }) - - it('should throw error trying to move cells when source is a malformed SimpleCellRange', () => { - const engine = HyperFormula.buildFromArray([ - [null, null], - ]) - expect(() => { - engine.moveCells({} as SimpleCellRange, adr('A2')) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellRange', 'source')) - }) - - it('should throw error trying to move cells when destinationLeftCorner is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildFromArray([ - [null, null], - ]) - expect(() => { - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), {} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'destinationLeftCorner')) - }) - - it('leaves the engine in a valid state so other operations are possible afterwards', () => { - const engine = HyperFormula.buildFromArray([[null, '=A1', 42]]) - engine.moveCells({ start: adr('B1'), end: adr('B1')}, adr('A1')) - engine.setCellContents(adr('A1'), '=A2') - expect(engine.getSheetSerialized(0)).toEqual([['=A2', null, 42]]) - }) - - it('leaves the engine in a valid state so other operations are possible afterwards (with a range)', () => { - const engine = HyperFormula.buildFromArray([[null, null, null, '=SUM(A1:C1)', 42]]) - engine.moveCells({ start: adr('D1'), end: adr('D1')}, adr('A1')) - engine.setCellContents(adr('A1'), '=SUM(B1:D1)') - expect(engine.getSheetSerialized(0)).toEqual([['=SUM(B1:D1)', null, null, null, 42]]) - }) -}) - -describe('moving ranges', () => { - it('should not update range when only part of it is moved', () => { - const engine = HyperFormula.buildFromArray([ - ['1' /* 1 */], - ['2'], - ['=SUM(A1:A2)'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('B1')) - - const range = extractRange(engine, adr('A3')) - expect(range.start).toEqual(adr('A1')) - expect(range.end).toEqual(adr('A2')) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - - const a1 = engine.addressMapping.getCell(adr('A1')) - const a2 = engine.addressMapping.getCell(adr('A2')) - const a1a2 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A2')) - expect(a1).toBeInstanceOf(EmptyCellVertex) - expect(engine.graph.existsEdge(a1!, a1a2)).toBe(true) - expect(engine.graph.existsEdge(a2!, a1a2)).toBe(true) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '1'], - ['2'], - ['=SUM(A1:A2)'], - ])) - }) - - it('should update moved range', () => { - const engine = HyperFormula.buildFromArray([ - ['1' /* 1 */], - ['2' /* 2 */], - ['=SUM(A1:A2)'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 2), adr('B1')) - - expect(engine.rangeMapping.getRangeVertex(adr('B1'), adr('B2'))).not.toBe(undefined) - - const range = extractRange(engine, adr('A3')) - expect(range.start).toEqual(adr('B1')) - expect(range.end).toEqual(adr('B2')) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - - expect(engine.addressMapping.getCell(adr('A1'))).toBe(undefined) - expect(engine.addressMapping.getCell(adr('A2'))).toBe(undefined) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '1'], - [null, '2'], - ['=SUM(B1:B2)'], - ])) - }) - - it('should not be possible to move area with matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - ]) - - expect(() => { - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A2'), 2, 2), adr('C1')) - }).toThrowError('Cannot perform this operation, source location has an array inside.') - }) - - it('should not be possible to move cells to area with matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - ]) - - expect(() => { - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 2, 1), adr('A2')) - }).toThrowError('Cannot perform this operation, target location has an array inside.') - }) - - it('should adjust edges when moving part of range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=SUM(A1:A2)'], - ['2', '=A2'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2')) - - const b1 = engine.addressMapping.getCell(adr('B1')) - const b2 = engine.addressMapping.getCell(adr('B2')) - const source = engine.addressMapping.getCell(adr('A1')) - const target = engine.addressMapping.getCell(adr('A2')) - const range = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A2')) - - expect(source).toBeInstanceOf(EmptyCellVertex) - expect(source!.getCellValue()).toBe(EmptyValue) - expect(engine.graph.getNodes().length).toBe( - +2 // formulas - + 1 // A2 - + 1 // A1 (Empty) - + 1, // A1:A2 range - ) - expect(graphEdgesCount(engine.graph)).toBe( - +2 // A1 (Empty) -> A1:A2, A2 -> A1:A2 - + 1 // A1:A2 -> B1 - + 1, // A2 -> B2 - ) - expect(engine.graph.existsEdge(target!, b2!)).toBe(true) - expect(engine.graph.existsEdge(source!, range)).toBe(true) - expect(engine.graph.existsEdge(target!, range)).toBe(true) - expect(engine.graph.existsEdge(range, b1!)).toBe(true) - expect(engine.getCellValue(adr('A2'))).toBe(1) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '=SUM(A1:A2)'], - ['1', '=A2'], - ])) - }) - - it('should adjust edges when moving whole range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=SUM(A1:A2)'], - ['2', '=A2'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 2), adr('C1')) - - const a1 = engine.addressMapping.getCell(adr('A1')) - const a2 = engine.addressMapping.getCell(adr('A2')) - const b1 = engine.addressMapping.getCell(adr('B1')) - const c1 = engine.addressMapping.getCell(adr('C1')) - const c2 = engine.addressMapping.getCell(adr('C2')) - const range = engine.rangeMapping.getVertexOrThrow(adr('C1'), adr('C2')) - - expect(a1).toBe(undefined) - expect(a2).toBe(undefined) - - expect(engine.graph.getNodes().length).toBe( - +2 // formulas - + 2 // C1, C2 - + 1, // C1:C2 range - ) - expect(graphEdgesCount(engine.graph)).toBe( - +2 // C1 -> C1:C2, C2 -> C1:C2 - + 1 // C1:C2 -> B1 - + 1, // C2 -> B2 - ) - - expect(engine.graph.existsEdge(c1!, range)).toBe(true) - expect(engine.graph.existsEdge(c2!, range)).toBe(true) - expect(engine.graph.existsEdge(range, b1!)).toBe(true) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '=SUM(C1:C2)', '1'], - [null, '=C2', '2'], - ])) - }) - - it('should adjust edges when moving smaller range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', null /* 1 */], - ['2', '=SUM(A1:A2)' /* 2 */], - ['3', '=SUM(A1:A3)'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 2), adr('C1')) - - /* ranges in formulas*/ - expect(extractRange(engine, adr('B2'))).toEqual(new AbsoluteCellRange( - adr('C1'), - adr('C2'), - )) - expect(extractRange(engine, adr('B3'))).toEqual(new AbsoluteCellRange( - adr('A1'), - adr('A3'), - )) - - /* edges */ - const c1c2 = engine.rangeMapping.getVertexOrThrow(adr('C1'), adr('C2')) - const a1a3 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A3')) - expect(engine.graph.existsEdge(c1c2, a1a3)).toBe(false) - - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('A1'))!, a1a3)).toBe(true) - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('A2'))!, a1a3)).toBe(true) - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('A3'))!, a1a3)).toBe(true) - - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('C1'))!, c1c2)).toBe(true) - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('C2'))!, c1c2)).toBe(true) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null, '1'], - [null, '=SUM(C1:C2)', '2'], - ['3', '=SUM(A1:A3)'], - ])) - }) - - it('should adjust edges when moving smaller ranges - more complex', () => { - const engine = HyperFormula.buildFromArray([ - ['1', null /* 1 */], - ['2', '=SUM(A1:A2)' /* 2 */], - ['3', '=SUM(A1:A3)' /* 3 */], - ['4', '=SUM(A1:A4)'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 3), adr('C1')) - - /* edges */ - const c1c2 = engine.rangeMapping.getVertexOrThrow(adr('C1'), adr('C2')) - const c1c3 = engine.rangeMapping.getVertexOrThrow(adr('C1'), adr('C3')) - const a1a4 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A4')) - - expect(engine.graph.existsEdge(c1c2, c1c3)).toBe(true) - expect(engine.graph.existsEdge(c1c3, a1a4)).toBe(false) - - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('A1'))!, a1a4)).toBe(true) - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('A2'))!, a1a4)).toBe(true) - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('A3'))!, a1a4)).toBe(true) - expect(engine.graph.existsEdge(engine.addressMapping.getCell(adr('A4'))!, a1a4)).toBe(true) - - const c1 = engine.addressMapping.getCell(adr('C1')) - const c2 = engine.addressMapping.getCell(adr('C2')) - const c3 = engine.addressMapping.getCell(adr('C3')) - expect(engine.graph.existsEdge(c1!, c1c2)).toBe(true) - expect(engine.graph.existsEdge(c2!, c1c2)).toBe(true) - expect(engine.graph.existsEdge(c1!, c1c3)).toBe(false) - expect(engine.graph.existsEdge(c2!, c1c3)).toBe(false) - expect(engine.graph.existsEdge(c3!, c1c3)).toBe(true) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null, '1'], - [null, '=SUM(C1:C2)', '2'], - [null, '=SUM(C1:C3)', '3'], - ['4', '=SUM(A1:A4)'], - ])) - }) - - it('move wider dependent ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ['=SUM(A1:B1)', '=SUM(A1:B2)', '=SUM(A1:B3)'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2), adr('C1')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null, '1', '2'], - [null, null, '3', '4'], - ['5', '6'], - ['=SUM(C1:D1)', '=SUM(C1:D2)', '=SUM(A1:B3)'], - ])) - }) -}) - -describe('overlapping areas', () => { - it('overlapped rows', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2), adr('A2')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null], - ['1', '2'], - ['3', '4'], - ])) - }) - - it('overlapped rows - opposite way', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A2'), 2, 2), adr('A1')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['3', '4'], - ['5', '6'], - [null, null], - ])) - }) - - it('overlapped columns', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2), adr('B1')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '1', '2'], - [null, '4', '5'], - ])) - }) - - it('overlapped columns - opposite way', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('B1'), 2, 2), adr('A1')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['2', '3', null], - ['5', '6', null], - ])) - }) - - it('moving along diagonal', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 3, 2), adr('B2')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null, null, null], - [null, '1', '2', '3'], - [null, '4', '5', '6'], - ])) - }) - - it('overlapped rows with ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ['=SUM(A1:B2)', '=SUM(A1:B3)', '=SUM(A2:B2)'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2), adr('A2')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, null], - ['1', '2'], - ['3', '4'], - ['=SUM(A2:B3)', '=SUM(A1:B3)', '=SUM(A3:B3)'], - ])) - }) - - it('overlapped columns with ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ['=SUM(A1:B2)', '=SUM(A1:C2)', '=SUM(B1:B2)'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2), adr('B1')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '1', '2'], - [null, '4', '5'], - ['=SUM(B1:C2)', '=SUM(A1:C2)', '=SUM(C1:C2)'], - ])) - }) - - it('expecting range to be same when moving part of a range inside this range', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - ['=SUM(A1:A3)'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null], - ['1'], - ['3'], - ['=SUM(A1:A3)'], - ])) - }) - - it('expecting range to be same when moving part of a range outside of this range', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - [null], - ['=SUM(A1:A3)'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A4')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null], - ['2'], - ['3'], - ['1'], - ['=SUM(A1:A3)'], - ])) - }) - - it('expecting range to be same when moving part of a range outside of this range - row', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', null], - ['=SUM(A1:C1)'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('D1')) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null, '2', '3', '1'], - ['=SUM(A1:C1)'], - ])) - }) - - it('ArrayFormulaVertex#formula should be updated', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=TRANSPOSE(A1:B2)'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2), adr('C1', 0)) - - expect(extractMatrixRange(engine, adr('A3'))).toEqual(new AbsoluteCellRange(adr('C1'), adr('D2'))) - }) - - it('ArrayFormulaVertex#formula should be updated when different sheets', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '2'], - ['3', '4'], - ], - Sheet2: [ - ['=TRANSPOSE(Sheet1!A1:B2)'], - ], - }) - - expect(extractMatrixRange(engine, adr('A1', 1))).toEqual(new AbsoluteCellRange(adr('A1'), adr('B2'))) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2), adr('C1', 0)) - - expect(extractMatrixRange(engine, adr('A1', 1))).toEqual(new AbsoluteCellRange(adr('C1'), adr('D2'))) - }) - - it('overlapped formulas', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2'], - ['42'] - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2')) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.CYCLE)) - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null], - ['=A2'] - ])) - }) - - it('overlapped formula with range', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2:B2'], - ['42'] - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2')) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.CYCLE)) - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [null], - ['=A2:B2'] - ])) - }) -}) - -describe('column index', () => { - it('should update column index when moving cell', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['1'], - ['=VLOOKUP(1, A1:A2, 1, TRUE())'], - ], {useColumnIndex: true}) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('B1')) - - const index = engine.columnSearch as ColumnIndex - expectArrayWithSameContent([1, 2], index.getValueIndex(0, 0, 1).index) - expectArrayWithSameContent([0], index.getValueIndex(0, 1, 1).index) - }) - - it('should update column index when moving cell - REFs', () => { - const engine = HyperFormula.buildFromArray([ - ['=B1', '1'], - ['3', '2'], - ], {useColumnIndex: true}) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 2), adr('B1')) - - const index = engine.columnSearch as ColumnIndex - expectArrayWithSameContent([], index.getValueIndex(0, 0, 2).index) - expectArrayWithSameContent([], index.getValueIndex(0, 0, 3).index) - expectArrayWithSameContent([], index.getValueIndex(0, 1, 1).index) - expectArrayWithSameContent([], index.getValueIndex(0, 1, 2).index) - expectArrayWithSameContent([1], index.getValueIndex(0, 1, 3).index) - }) - - it('should update column index when source and target overlaps', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4', '5'], - [null, '6', '7'], - ], {useColumnIndex: true}) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2), adr('B2')) - - const index = engine.columnSearch as ColumnIndex - expect(index.getColumnMap(0, 0).size).toEqual(0) - expect(index.getColumnMap(0, 1).size).toEqual(2) - expectArrayWithSameContent([1], index.getValueIndex(0, 1, 1).index) - expectArrayWithSameContent([2], index.getValueIndex(0, 1, 3).index) - expect(index.getColumnMap(0, 2).size).toEqual(2) - expectArrayWithSameContent([1], index.getValueIndex(0, 2, 2).index) - expectArrayWithSameContent([2], index.getValueIndex(0, 2, 4).index) - }) - - it('should update column index when source and target overlaps - oposite way', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4', '5'], - [null, '6', '7'], - ], {useColumnIndex: true}) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('B2'), 2, 2), adr('A1')) - - const index = engine.columnSearch as ColumnIndex - expect(index.getColumnMap(0, 0).size).toEqual(2) - expectArrayWithSameContent([0], index.getValueIndex(0, 0, 4).index) - expectArrayWithSameContent([1], index.getValueIndex(0, 0, 6).index) - expect(index.getColumnMap(0, 0).size).toEqual(2) - expectArrayWithSameContent([0], index.getValueIndex(0, 1, 5).index) - expectArrayWithSameContent([1], index.getValueIndex(0, 1, 7).index) - expect(index.getColumnMap(0, 2).size).toEqual(0) - }) -}) - -describe('move cells with matrices', () => { - it('should not be possible to move part of formula matrix', function() { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - ]) - - expect(() => { - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A2'), 1, 1), adr('A3')) - }).toThrowError('Cannot perform this operation, source location has an array inside.') - }) - - it('should not be possible to move formula matrix at all', function() { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - ]) - - expect(() => { - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A2'), 2, 1), adr('A3')) - }).toThrowError('Cannot perform this operation, source location has an array inside.') - }) -}) - -describe('cell ranges', () => { - it('should transform relative references', () => { - const engine = HyperFormula.buildFromArray([[1, 2, '=SUM(A1:B1)', '=SUM($A1:B$1)', '=SUM(A$1:$B$1)']]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('C1'), 3, 1), adr('D2')) - - expect(engine.getCellFormula(adr('D2'))).toEqual('=SUM(A1:B1)') - expect(engine.getCellFormula(adr('E2'))).toEqual('=SUM($A1:B$1)') - expect(engine.getCellFormula(adr('F2'))).toEqual('=SUM(A$1:$B$1)') - }) - - it('move formula and one end of a range', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2], - [null, '=SUM(A1:B1)'] - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('B1'), 1, 2), adr('C1')) - - expect(engine.getCellFormula(adr('C2'))).toEqual('=SUM(A1:B1)') - }) - - it('should be #CYCLE! if one of ends is in target range', () => { - const engine = HyperFormula.buildFromArray([[1, 2, '=SUM(A1:B1)']]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('C1'), 1, 1), adr('B1')) - - expect(engine.getCellFormula(adr('B1'))).toEqual('=SUM(A1:B1)') - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.CYCLE)) - }) -}) - -describe('column ranges', () => { - it('should not update range when only part of it is moved', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '3', '=SUM(A:B)'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('C2')) - - const range = extractColumnRange(engine, adr('C1')) - expect(range.start).toEqual(colStart('A')) - expect(range.end).toEqual(colEnd('B')) - expect(engine.getCellValue(adr('C1'))).toEqual(3) - - const a1 = engine.addressMapping.getCell(adr('A1')) - const b1 = engine.addressMapping.getCell(adr('B1')) - const ab = engine.rangeMapping.getVertexOrThrow(colStart('A'), colEnd('B')) - expect(a1).toBeInstanceOf(EmptyCellVertex) - expect(engine.graph.existsEdge(a1!, ab)).toBe(true) - expect(engine.graph.existsEdge(b1!, ab)).toBe(true) - }) - - it('should transform relative column references', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(C:D)', '', '1', '2'] - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('B2')) - - const range = extractColumnRange(engine, adr('B2')) - expect(engine.getCellValue(adr('B2'))).toEqual(3) - expect(range.start).toEqual(colStart('C')) - expect(range.end).toEqual(colEnd('D')) - }) - - it('should be #CYCLE! if one of ends is in target range', () => { - const engine = HyperFormula.buildFromArray([[1, 2, '=SUM(A:B)']]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('C1'), 1, 1), adr('B1')) - - expect(engine.getCellFormula(adr('B1'))).toEqual('=SUM(A:B)') - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.CYCLE)) - }) -}) - -describe('row ranges', () => { - it('should not update range when only part of it is moved', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['3'], - ['=SUM(1:2)'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('B3')) - - const range = extractRowRange(engine, adr('A3')) - expect(range.start).toEqual(rowStart(1)) - expect(range.end).toEqual(rowEnd(2)) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - - const a1 = engine.addressMapping.getCell(adr('A1')) - const a2 = engine.addressMapping.getCell(adr('A2')) - const ab = engine.rangeMapping.getVertexOrThrow(rowStart(1), rowEnd(2)) - expect(a1).toBeInstanceOf(EmptyCellVertex) - expect(engine.graph.existsEdge(a1!, ab)).toBe(true) - expect(engine.graph.existsEdge(a2!, ab)).toBe(true) - }) - - it('should transform relative column references', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(3:4)'], - [null], - ['1'], - ['2'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('B2')) - - const range = extractRowRange(engine, adr('B2')) - expect(engine.getCellValue(adr('B2'))).toEqual(3) - expect(range.start).toEqual(rowStart(3)) - expect(range.end).toEqual(rowEnd(4)) - }) - - it('should be #CYCLE! if one of ends is in target range', () => { - const engine = HyperFormula.buildFromArray([ - [1], - [2], - ['=SUM(1:2)'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A3'), 1, 1), adr('A2')) - - expect(engine.getCellFormula(adr('A2'))).toEqual('=SUM(1:2)') - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.CYCLE)) - }) -}) diff --git a/test/unit/cruds/move-columns.spec.ts b/test/unit/cruds/move-columns.spec.ts deleted file mode 100644 index 0e4a69cc56..0000000000 --- a/test/unit/cruds/move-columns.spec.ts +++ /dev/null @@ -1,331 +0,0 @@ -import {ExportedCellChange, HyperFormula, InvalidArgumentsError} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {CellAddress} from '../../../src/parser' -import { - adr, - colEnd, - colStart, - detailedError, - extractColumnRange, - extractReference, - extractRowRange, - rowEnd, - rowStart -} from '../testUtils' - -describe('Ensure it is possible to move columns', () => { - it('should return false when target makes no sense', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ]) - - expect(engine.isItPossibleToMoveColumns(0, 0, 1, -1)).toEqual(false) - expect(engine.isItPossibleToMoveColumns(0, 0, 1, 1)).toEqual(false) - expect(engine.isItPossibleToMoveColumns(0, 0, 1, 0)).toEqual(false) - expect(engine.isItPossibleToMoveColumns(0, 0, 2, 0)).toEqual(false) - expect(engine.isItPossibleToMoveColumns(0, 0, 2, 1)).toEqual(false) - expect(engine.isItPossibleToMoveColumns(0, 0, 2, 2)).toEqual(false) - }) - - it('should not be possible to move columns when sheet does not exists', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ]) - - expect(engine.isItPossibleToMoveColumns(1, 0, 1, 2)).toEqual(false) - }) - - it('should not be possible to move columns when number of columns is non-positive', () => { - const engine = HyperFormula.buildFromArray([ - ['1'] - ]) - - expect(engine.isItPossibleToMoveColumns(0, 0, 0, 1)).toEqual(false) - expect(engine.isItPossibleToMoveColumns(0, 0, -5, 1)).toEqual(false) - }) - - it('should be possible to move columns', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '1', '2'], - ]) - - expect(engine.isItPossibleToMoveColumns(0, 1, 1, 0)).toEqual(true) - expect(engine.isItPossibleToMoveColumns(0, 1, 1, 3)).toEqual(true) - expect(engine.isItPossibleToMoveColumns(0, 1, 1, 4)).toEqual(true) - expect(engine.isItPossibleToMoveColumns(0, 1, 2, 0)).toEqual(true) - expect(engine.isItPossibleToMoveColumns(0, 1, 2, 4)).toEqual(true) - expect(engine.isItPossibleToMoveColumns(0, 1, 2, 5)).toEqual(true) - }) - - it('should not be possible to move row with formula matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=TRANSPOSE(A1:A2)'], - ['2'], - ]) - - expect(engine.isItPossibleToMoveColumns(0, 1, 1, 5)).toBe(false) - expect(engine.isItPossibleToMoveColumns(0, 1, 2, 5)).toBe(false) - }) - - it('should not be possible to move row inside formula matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '', '=TRANSPOSE(A1:A2)'], - ['2'], - ]) - - expect(engine.isItPossibleToMoveColumns(0, 0, 1, 2)).toBe(true) - expect(engine.isItPossibleToMoveColumns(0, 0, 1, 3)).toBe(false) - expect(engine.isItPossibleToMoveColumns(0, 0, 1, 4)).toBe(true) - }) -}) - -describe('Move columns', () => { - it('should throw error when target makes no sense', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ]) - - expect(() => engine.moveColumns(0, 0, 1, -1)).toThrow(new InvalidArgumentsError('column number to be nonnegative and number of columns to add to be positive.')) - expect(() => engine.moveColumns(0, 0, 1, 1)).toThrow(new InvalidArgumentsError('a valid range of columns to move.')) - expect(() => engine.moveColumns(0, 0, 1, 0)).toThrow(new InvalidArgumentsError('a valid range of columns to move.')) - expect(() => engine.moveColumns(0, 0, 2, 0)).toThrow(new InvalidArgumentsError('a valid range of columns to move.')) - expect(() => engine.moveColumns(0, 0, 2, 1)).toThrow(new InvalidArgumentsError('a valid range of columns to move.')) - expect(() => engine.moveColumns(0, 0, 2, 2)).toThrow(new InvalidArgumentsError('a valid range of columns to move.')) - }) - - it('should move one column', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '4'] - ]) - - engine.moveColumns(0, 1, 1, 3) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('B1'))).toEqual(3) - expect(engine.getCellValue(adr('C1'))).toEqual(2) - expect(engine.getCellValue(adr('D1'))).toEqual(4) - }) - - it('should move column when moving to left', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '4'] - ]) - - engine.moveColumns(0, 2, 1, 1) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('B1'))).toEqual(3) - expect(engine.getCellValue(adr('C1'))).toEqual(2) - expect(engine.getCellValue(adr('D1'))).toEqual(4) - }) - - it('should move multiple columns', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '4'] - ]) - - engine.moveColumns(0, 0, 3, 4) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('C1'))).toEqual(2) - expect(engine.getCellValue(adr('D1'))).toEqual(3) - }) - - it('should work when moving multiple columns far away', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'] - ]) - - engine.moveColumns(0, 1, 2, 5) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('B1'))).toBe(null) - expect(engine.getCellValue(adr('C1'))).toBe(null) - expect(engine.getCellValue(adr('D1'))).toEqual(2) - expect(engine.getCellValue(adr('E1'))).toEqual(3) - }) - - it('should adjust reference when swapping formula with dependency', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=A1'], - ['=B2', '1'], - ]) - - engine.moveColumns(0, 1, 1, 0) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('B2'))).toEqual(1) - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(1, 0)) - expect(extractReference(engine, adr('B2'))).toEqual(CellAddress.relative(-1, 0)) - }) - - it('should adjust absolute references', () => { - const engine = HyperFormula.buildFromArray([ - ['=$B$1'], - ['=B2'] - ]) - - engine.moveColumns(0, 0, 1, 2) - - expect(extractReference(engine, adr('B1'))).toEqual(CellAddress.absolute(0, 0)) - expect(extractReference(engine, adr('B2'))).toEqual(CellAddress.relative(-1, 0)) - }) - - it('should adjust range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['', '=COUNTBLANK(A1:B1)'], - ]) - - engine.moveColumns(0, 1, 1, 3) - - expect(engine.getCellFormula(adr('C2'))).toEqual('=COUNTBLANK(A1:A1)') - expect(engine.getCellValue(adr('C2'))).toEqual(0) - }) - - it('should return changes', () => { - const engine = HyperFormula.buildFromArray([ - ['1', null], - ['', '=COUNTBLANK(A1:B1)'], - ]) - - const changes = engine.moveColumns(0, 1, 1, 3) - - expect(changes.length).toEqual(1) - expect(changes).toContainEqual(new ExportedCellChange(adr('C2'), 0)) - }) - - it('should return #CYCLE when moving formula onto referred range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '=AVERAGE(A1:C1)', '=SUM(A1:C1)'] - ]) - - engine.moveColumns(0, 3, 1, 1) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.CYCLE)) - }) - - it('should work with moving formulas', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '4', '=SUM(A1:C1)'] - ]) - - engine.moveColumns(0, 3, 1, 1) - - expect(engine.getCellValue(adr('E1'))).toEqual(10) - }) - - it('should return #CYCLE when moving formula onto referred range, simple case', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(B1:C1)', '1', '2'] - ]) - - engine.moveColumns(0, 0, 1, 2) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellFormula(adr('B1'))).toEqual('=SUM(A1:C1)') - }) - - it('should produce only one history entry', () => { - const engine = HyperFormula.buildFromArray([[0, 1, 2, 3]]) - - const version = engine.lazilyTransformingAstService.version() - - engine.moveColumns(0, 1, 1, 3) - - expect(engine.lazilyTransformingAstService.version()).toEqual(version + 1) - }) - - it('leaves the engine in a valid state so other operations are possible afterwards', () => { - const engine = HyperFormula.buildFromArray([[ null, '=A1' ]]) - engine.moveColumns(0, 1, 1, 0) - expect(engine.getCellSerialized(adr('A1'))).toEqual('=B1') - expect(engine.getCellSerialized(adr('B1'))).toEqual(null) - - engine.setCellContents(adr('A1'), '=B1') - expect(engine.getCellSerialized(adr('A1'))).toEqual('=B1') - expect(engine.getCellSerialized(adr('B1'))).toEqual(null) - }) - - it('leaves the engine in a valid state so other operations are possible afterwards (with a range)', () => { - const engine = HyperFormula.buildFromArray([[null, null, null, '=SUM(A1:C1)', 42]]) - engine.moveColumns(0, 3, 1, 0) - engine.setCellContents(adr('A1'), '=SUM(B1:D1)') - expect(engine.getSheetSerialized(0)).toEqual([['=SUM(B1:D1)', null, null, null, 42]]) - }) -}) - -describe('Move columns - column ranges', () => { - it('should adjust relative references of dependent formulas', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(B:C)', '1', '2'] - ]) - - engine.moveColumns(0, 1, 2, 4) - - const range = extractColumnRange(engine, adr('A1')) - expect(range.start).toEqual(colStart('C')) - expect(range.end).toEqual(colEnd('D')) - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('should adjust relative dependencies of moved formulas', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(B:C)', '1', '2'] - ]) - - engine.moveColumns(0, 0, 1, 3) - - const range = extractColumnRange(engine, adr('C1')) - expect(range.start).toEqual(colStart('A')) - expect(range.end).toEqual(colEnd('B')) - expect(engine.getCellValue(adr('C1'))).toEqual(3) - }) - - it('should return #CYCLE when moving formula onto referred range', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(B:C)', '1', '2'] - ]) - - engine.moveColumns(0, 0, 1, 2) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellFormula(adr('B1'))).toEqual('=SUM(A:C)') - }) -}) - -describe('Move columns - row ranges', () => { - it('should not affect moved row range', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(2:3)'], - ['1'], - ['2'] - ]) - - engine.moveColumns(0, 0, 1, 2) - - const range = extractRowRange(engine, adr('B1')) - expect(range.start).toEqual(rowStart(2)) - expect(range.end).toEqual(rowEnd(3)) - expect(engine.getCellValue(adr('B1'))).toEqual(3) - }) - - it('should not affect dependent row range', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(2:3)'], - ['1', '3'], - ['2', '4'] - ]) - - engine.moveColumns(0, 1, 1, 3) - - const range = extractRowRange(engine, adr('A1')) - expect(range.start).toEqual(rowStart(2)) - expect(range.end).toEqual(rowEnd(3)) - expect(engine.getCellValue(adr('A1'))).toEqual(10) - }) -}) diff --git a/test/unit/cruds/move-rows.spec.ts b/test/unit/cruds/move-rows.spec.ts deleted file mode 100644 index 9d43d45d8f..0000000000 --- a/test/unit/cruds/move-rows.spec.ts +++ /dev/null @@ -1,326 +0,0 @@ -import {ExportedCellChange, HyperFormula, InvalidArgumentsError} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {CellAddress} from '../../../src/parser' -import { - adr, - colEnd, - colStart, - detailedError, - extractColumnRange, - extractReference, - extractRowRange, - rowEnd, - rowStart, -} from '../testUtils' - -describe('Ensure it is possible to move rows', () => { - it('should return false when target makes no sense', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'] - ]) - - expect(engine.isItPossibleToMoveRows(0, 0, 1, -1)).toEqual(false) - expect(engine.isItPossibleToMoveRows(0, 0, 1, 1)).toEqual(false) - expect(engine.isItPossibleToMoveRows(0, 0, 1, 0)).toEqual(false) - expect(engine.isItPossibleToMoveRows(0, 0, 2, 0)).toEqual(false) - expect(engine.isItPossibleToMoveRows(0, 0, 2, 1)).toEqual(false) - expect(engine.isItPossibleToMoveRows(0, 0, 2, 2)).toEqual(false) - }) - - it('should not be possible to move rows when sheet does not exists', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ]) - - expect(engine.isItPossibleToMoveRows(1, 0, 1, 2)).toEqual(false) - }) - - it('should not be possible to move rows when number of rows is non-positive', () => { - const engine = HyperFormula.buildFromArray([ - ['1'] - ]) - - expect(engine.isItPossibleToMoveRows(0, 0, 0, 1)).toEqual(false) - expect(engine.isItPossibleToMoveRows(0, 0, -5, 1)).toEqual(false) - }) - - it('should be possible to move rows', () => { - const engine = HyperFormula.buildFromArray([ - ['0'], - ['1'], - ['2'], - ]) - - expect(engine.isItPossibleToMoveRows(0, 1, 1, 0)).toEqual(true) - expect(engine.isItPossibleToMoveRows(0, 1, 1, 3)).toEqual(true) - expect(engine.isItPossibleToMoveRows(0, 1, 1, 4)).toEqual(true) - expect(engine.isItPossibleToMoveRows(0, 1, 2, 0)).toEqual(true) - expect(engine.isItPossibleToMoveRows(0, 1, 2, 4)).toEqual(true) - expect(engine.isItPossibleToMoveRows(0, 1, 2, 5)).toEqual(true) - }) - - it('should not be possible to move row with formula matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - ]) - - expect(engine.isItPossibleToMoveRows(0, 1, 1, 5)).toBe(false) - expect(engine.isItPossibleToMoveRows(0, 1, 2, 5)).toBe(false) - }) - - it('should not be possible to move row inside formula matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - [''], - ['=TRANSPOSE(A1:B1)'], - ]) - - expect(engine.isItPossibleToMoveRows(0, 0, 1, 2)).toBe(true) - expect(engine.isItPossibleToMoveRows(0, 0, 1, 3)).toBe(false) - expect(engine.isItPossibleToMoveRows(0, 0, 1, 4)).toBe(true) - }) -}) - -describe('Move rows', () => { - it('should throw error when target makes no sense', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'] - ]) - - expect(() => engine.moveRows(0, 0, 1, -1)).toThrow(new InvalidArgumentsError('row number to be nonnegative and number of rows to add to be positive.')) - expect(() => engine.moveRows(0, 0, 1, 1)).toThrow(new InvalidArgumentsError('a valid range of rows to move.')) - expect(() => engine.moveRows(0, 0, 1, 0)).toThrow(new InvalidArgumentsError('a valid range of rows to move.')) - expect(() => engine.moveRows(0, 0, 2, 0)).toThrow(new InvalidArgumentsError('a valid range of rows to move.')) - expect(() => engine.moveRows(0, 0, 2, 1)).toThrow(new InvalidArgumentsError('a valid range of rows to move.')) - expect(() => engine.moveRows(0, 0, 2, 2)).toThrow(new InvalidArgumentsError('a valid range of rows to move.')) - }) - - it('should move one row', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - ['4'], - ]) - - engine.moveRows(0, 1, 1, 3) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(3) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - expect(engine.getCellValue(adr('A4'))).toEqual(4) - }) - - it('should move row when moving upward', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - ['4'], - ]) - - engine.moveRows(0, 2, 1, 1) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(3) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - expect(engine.getCellValue(adr('A4'))).toEqual(4) - }) - - it('should move multiple rows', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - ['4'], - ]) - - engine.moveRows(0, 0, 3, 4) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - expect(engine.getCellValue(adr('A4'))).toEqual(3) - }) - - it('should work when moving multiple rows far away', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - ]) - - engine.moveRows(0, 1, 2, 5) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toBe(null) - expect(engine.getCellValue(adr('A3'))).toBe(null) - expect(engine.getCellValue(adr('A4'))).toEqual(2) - expect(engine.getCellValue(adr('A5'))).toEqual(3) - }) - - it('should adjust reference when swapping formula with dependency', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['=A1'], - ]) - - engine.moveRows(0, 1, 1, 0) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 1)) - }) - - it('should adjust absolute references', () => { - const engine = HyperFormula.buildFromArray([ - ['=$A$2', '=B2'] - ]) - - engine.moveRows(0, 0, 1, 2) - - expect(extractReference(engine, adr('A2'))).toEqual(CellAddress.absolute(0, 0)) - expect(extractReference(engine, adr('B2'))).toEqual(CellAddress.relative(0, -1)) - }) - - it('should adjust range', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2', '=COUNTBLANK(A1:A2)'], - ]) - - engine.moveRows(0, 1, 1, 3) - - expect(engine.getCellFormula(adr('B3'))).toEqual('=COUNTBLANK(A1:A1)') - expect(engine.getCellValue(adr('B3'))).toEqual(0) - }) - - it('should return changes', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - [null, '=COUNTBLANK(A1:A2)'], - ]) - - const changes = engine.moveRows(0, 1, 1, 3) - - expect(changes.length).toEqual(1) - expect(changes).toContainEqual(new ExportedCellChange(adr('B3'), 0)) - }) - - it('should return #CYCLE when moving formula onto referred range', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - ['=SUM(A1:A3)'], - ['=AVERAGE(A1:A3)'], - ]) - - engine.moveRows(0, 3, 1, 1) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.CYCLE)) - }) - - it('should produce only one history entry', () => { - const engine = HyperFormula.buildFromArray([[0], [1], [2], [3]]) - - const version = engine.lazilyTransformingAstService.version() - - engine.moveRows(0, 1, 1, 3) - - expect(engine.lazilyTransformingAstService.version()).toEqual(version + 1) - }) - - it('leaves the engine in a valid state so other operations are possible afterwards', () => { - const engine = HyperFormula.buildFromArray([[null], ['=A1']]) - engine.moveRows(0, 1, 1, 0) - engine.setCellContents(adr('A1'), '=A2') - expect(engine.getSheetSerialized(0)).toEqual([['=A2']]) - }) - - it('leaves the engine in a valid state so other operations are possible afterwards (with a range)', () => { - const engine = HyperFormula.buildFromArray([[null, null, null], ['=SUM(A1:C1)']]) - engine.moveRows(0, 1, 1, 0) - engine.setCellContents(adr('A1'), '=SUM(A2:C2)') - expect(engine.getSheetSerialized(0)).toEqual([['=SUM(A2:C2)']]) - }) -}) - -describe('Move rows - row ranges', () => { - it('should adjust relative references of dependent formulas', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(2:3)'], - ['1'], - ['2'] - ]) - - engine.moveRows(0, 1, 2, 4) - - const range = extractRowRange(engine, adr('A1')) - expect(range.start).toEqual(rowStart(3)) - expect(range.end).toEqual(rowEnd(4)) - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('should adjust relative dependencies of moved formulas', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(2:3)'], - ['1'], - ['2'] - ]) - - engine.moveRows(0, 0, 1, 3) - - const range = extractRowRange(engine, adr('A3')) - expect(range.start).toEqual(rowStart(1)) - expect(range.end).toEqual(rowEnd(2)) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - }) - - it('should return #CYCLE when moving formula onto referred range', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(2:3)'], - ['1'], - ['2'] - ]) - - engine.moveRows(0, 0, 1, 2) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellFormula(adr('A2'))).toEqual('=SUM(1:3)') - }) -}) - -describe('Move rows - column ranges', () => { - it('should not affect moved column range', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(B:C)', '1', '2'], - ]) - - engine.moveRows(0, 0, 1, 2) - - const range = extractColumnRange(engine, adr('A2')) - expect(range.start).toEqual(colStart('B')) - expect(range.end).toEqual(colEnd('C')) - expect(engine.getCellValue(adr('A2'))).toEqual(3) - }) - - it('should not affect dependent column range', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(B:C)', '1', '2'], - [null, '3', '4'], - ]) - - engine.moveRows(0, 1, 1, 3) - - const range = extractColumnRange(engine, adr('A1')) - expect(range.start).toEqual(colStart('B')) - expect(range.end).toEqual(colEnd('C')) - expect(engine.getCellValue(adr('A1'))).toEqual(10) - }) -}) diff --git a/test/unit/cruds/removing-columns.spec.ts b/test/unit/cruds/removing-columns.spec.ts deleted file mode 100644 index fb278786e5..0000000000 --- a/test/unit/cruds/removing-columns.spec.ts +++ /dev/null @@ -1,1020 +0,0 @@ -import {AlwaysDense, AlwaysSparse, ExportedCellChange, HyperFormula} from '../../../src' -import {AbsoluteCellRange} from '../../../src/AbsoluteCellRange' -import {ArrayFormulaVertex, RangeVertex} from '../../../src/DependencyGraph' -import {ColumnIndex} from '../../../src/Lookup/ColumnIndex' -import {CellAddress} from '../../../src/parser' -import { - adr, - expectArrayWithSameContent, - expectEngineToBeTheSameAs, - expectFunctionToHaveRefError, - expectReferenceToHaveRefError, - extractMatrixRange, - extractRange, - extractReference, - noSpace, - verifyRangesInSheet, - verifyValues, -} from '../testUtils' - -describe('Removing columns - checking if its possible', () => { - it('no if starting column is negative', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToRemoveColumns(0, [-1, 1])).toEqual(false) - }) - - it('no if starting column is not an integer', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToRemoveColumns(0, [1.5, 2])).toEqual(false) - expect(engine.isItPossibleToRemoveColumns(0, [NaN, 2])).toEqual(false) - expect(engine.isItPossibleToRemoveColumns(0, [Infinity, 2])).toEqual(false) - expect(engine.isItPossibleToRemoveColumns(0, [-Infinity, 2])).toEqual(false) - }) - - it('no if number of columns is negative', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToRemoveColumns(0, [0, -1])).toEqual(false) - }) - - it('no if number of columns is not an integer', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToRemoveColumns(0, [0, 1.5])).toEqual(false) - expect(engine.isItPossibleToRemoveColumns(0, [0, NaN])).toEqual(false) - expect(engine.isItPossibleToRemoveColumns(0, [0, Infinity])).toEqual(false) - expect(engine.isItPossibleToRemoveColumns(0, [0, -Infinity])).toEqual(false) - }) - - it('no if sheet does not exist', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToRemoveColumns(1, [0, 1])).toEqual(false) - expect(engine.isItPossibleToRemoveColumns(1.5, [0, 1])).toEqual(false) - expect(engine.isItPossibleToRemoveColumns(-1, [0, 1])).toEqual(false) - expect(engine.isItPossibleToRemoveColumns(NaN, [0, 1])).toEqual(false) - expect(engine.isItPossibleToRemoveColumns(Infinity, [0, 1])).toEqual(false) - expect(engine.isItPossibleToRemoveColumns(-Infinity, [0, 1])).toEqual(false) - }) - - it('yes if theres an array in place where we remove', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=TRANSPOSE(A1:B2)'], - ['3', '4'], - ]) - - expect(engine.isItPossibleToRemoveColumns(0, [1, 1])).toEqual(true) - expect(engine.isItPossibleToRemoveColumns(0, [1, 2])).toEqual(true) - expect(engine.isItPossibleToRemoveColumns(0, [2, 1])).toEqual(true) - expect(engine.isItPossibleToRemoveColumns(0, [3, 1])).toEqual(true) - expect(engine.isItPossibleToRemoveColumns(0, [4, 1])).toEqual(true) - }) - - it('yes otherwise', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToRemoveColumns(0, [0, 1])).toEqual(true) - expect(engine.isItPossibleToRemoveColumns(0, [1, 1])).toEqual(true) - expect(engine.isItPossibleToRemoveColumns(0, [1, 2])).toEqual(true) - }) -}) - -describe('Address dependencies, Case 1: same sheet', () => { - it('case Aa: absolute dependency before removed column should not be affected', () => { - const engine = HyperFormula.buildFromArray([ - ['', '1', '', '=$B1'], - ]) - - engine.removeColumns(0, [2, 1]) - - expect(extractReference(engine, adr('C1'))).toEqual(CellAddress.absoluteCol(1, 0)) - }) - - it('case Ab: absolute dependency after removed column should be shifted', () => { - const engine = HyperFormula.buildFromArray([ - ['=$C1', '', '42'], - ]) - - engine.removeColumns(0, [1, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.absoluteCol(1, 0)) - }) - - it('case Ac: absolute dependency in removed column range should be replaced by #REF', () => { - const engine = HyperFormula.buildFromArray([ - ['=$B1', ''], - ]) - - engine.removeColumns(0, [1, 1]) - - expectReferenceToHaveRefError(engine, adr('A1')) - }) - - it('case Raa: relative dependency and formula before removed columns should not be affected', () => { - const engine = HyperFormula.buildFromArray([ - ['42', '=A1', '2'], - ]) - - engine.removeColumns(0, [2, 1]) - - expect(extractReference(engine, adr('B1'))).toEqual(CellAddress.relative(-1, 0)) - }) - - it('case Rab: relative address should be shifted when only formula is moving', () => { - const engine = HyperFormula.buildFromArray([ - ['42', '1', '2', '=A1'], - ]) - - engine.removeColumns(0, [1, 2]) - - expect(extractReference(engine, adr('B1'))).toEqual(CellAddress.relative(-1, 0)) - }) - - it('case Rba: relative address should be shifted when only dependency is moving', () => { - const engine = HyperFormula.buildFromArray([ - ['=D1', '1', '2', '42'], - ]) - - engine.removeColumns(0, [1, 2]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(1, 0)) - }) - - it('case Rbb: relative address should not be affected when dependency and formula is moving', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=D1', '42'], - ]) - - engine.removeColumns(0, [0, 2]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(1, 0)) - }) - - it('case Rca: relative dependency in deleted column range should be replaced by #REF', () => { - const engine = HyperFormula.buildFromArray([ - ['=C1', '1', '2', '3'], - ]) - - engine.removeColumns(0, [1, 2]) - - expectReferenceToHaveRefError(engine, adr('A1')) - }) - - it('case Rcb: relative dependency in deleted column range should be replaced by #REF', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '=B1'], - ]) - - engine.removeColumns(0, [0, 2]) - - expectReferenceToHaveRefError(engine, adr('B1')) - }) - - it('case Rca, range', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(B1:C1)', '1', '2'], - ]) - - engine.removeColumns(0, [1, 2]) - - expectFunctionToHaveRefError(engine, adr('A1')) - }) - - it('truncates range by one column from left if first column removed', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=SUM(A1:B1)'], - ]) - - engine.removeColumns(0, [0, 1]) - - expect(extractRange(engine, adr('B1'))).toEqual(new AbsoluteCellRange(adr('A1'), adr('A1'))) - }) - - it('truncates range by one column from right if last column removed', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=SUM(A1:B1)'], - ]) - - engine.removeColumns(0, [1, 1]) - - expect(extractRange(engine, adr('B1'))).toEqual(new AbsoluteCellRange(adr('A1'), adr('A1'))) - }) - - it('truncates range by columns from left if leftmost columns removed', () => { - const engine = HyperFormula.buildFromArray([ - ['', '1', '2', '3', '4'], - ['=SUM(B1:E1)'], - ]) - - engine.removeColumns(0, [1, 2]) - - expect(extractRange(engine, adr('A2'))).toEqual(new AbsoluteCellRange(adr('B1'), adr('C1'))) - }) - - it('truncates range by columns from left if leftmost columns removed - removing does not have to start with range', () => { - const engine = HyperFormula.buildFromArray([ - ['', '', '1', '2', '3', '4'], - ['=SUM(C1:F1)'], - ]) - - engine.removeColumns(0, [1, 3]) - - expect(extractRange(engine, adr('A2'))).toEqual(new AbsoluteCellRange(adr('B1'), adr('C1'))) - }) - - it('truncates range by columns from left if leftmost columns removed - removing does not have to start with range but may end on start', () => { - const engine = HyperFormula.buildFromArray([ - ['', '', '1', '2', '3', '4'], - ['=SUM(C1:F1)'], - ]) - - engine.removeColumns(0, [1, 2]) - - expect(extractRange(engine, adr('A2'))).toEqual(new AbsoluteCellRange(adr('B1'), adr('D1'))) - }) - - it('truncates range by columns from right if rightmost columns removed', () => { - const engine = HyperFormula.buildFromArray([ - ['', '1', '2', '3', '4'], - ['=SUM(B1:E1)'], - ]) - - engine.removeColumns(0, [3, 2]) - - expect(extractRange(engine, adr('A2'))).toEqual(new AbsoluteCellRange(adr('B1'), adr('C1'))) - }) - - it('truncates range by columns from right if rightmost columns removed - removing does not have to end with range', () => { - const engine = HyperFormula.buildFromArray([ - ['', '1', '2', '3', '4', ''], - ['=SUM(B1:E1)'], - ]) - - engine.removeColumns(0, [3, 3]) - - expect(extractRange(engine, adr('A2'))).toEqual(new AbsoluteCellRange(adr('B1'), adr('C1'))) - }) - - it('truncates range by columns from right if rightmost columns removed - removing does not have to end with range but may start on end', () => { - const engine = HyperFormula.buildFromArray([ - ['', '1', '2', '3', '4', ''], - ['=SUM(B1:E1)'], - ]) - - engine.removeColumns(0, [4, 2]) - - expect(extractRange(engine, adr('A2'))).toEqual(new AbsoluteCellRange(adr('B1'), adr('D1'))) - }) -}) - -describe('Address dependencies, Case 2: formula in sheet where we make crud with dependency to other sheet', () => { - it('case A: should not affect absolute dependencies', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '=Sheet2!$A1'], - ], - Sheet2: [ - ['2'], - ], - }) - - engine.removeColumns(0, [0, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.absoluteCol(0, 0, 1)) - }) - - it('case Ra: removing column before formula should shift dependency', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '=Sheet2!A1'], - ], - Sheet2: [ - ['2'], - ], - }) - - engine.removeColumns(0, [0, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 0, 1)) - }) - - it('case Rb: removing column after formula should not affect dependency', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=Sheet2!A1', '1'], - ], - Sheet2: [ - ['2'], - ], - }) - - engine.removeColumns(0, [1, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 0, 1)) - }) -}) - -describe('Address dependencies, Case 3: formula in different sheet', () => { - it('case ARa: relative/absolute dependency after removed column should be shifted', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=Sheet2!C1', '=Sheet2!C1', '=Sheet2!C1', '=Sheet2!$C1'], - ], - Sheet2: [ - ['1', '2', '3'], - ], - }) - - engine.removeColumns(1, [1, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(1, 0, 1)) - expect(extractReference(engine, adr('B1'))).toEqual(CellAddress.relative(0, 0, 1)) - expect(extractReference(engine, adr('C1'))).toEqual(CellAddress.relative(-1, 0, 1)) - expect(extractReference(engine, adr('D1'))).toEqual(CellAddress.absoluteCol(1, 0, 1)) - }) - - it('case ARb: relative/absolute dependency before removed column should not be affected', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=Sheet2!A1', '=Sheet2!$A1'], - ], - Sheet2: [ - ['0', '1'], - ], - }) - - engine.removeColumns(1, [1, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 0, 1)) - expect(extractReference(engine, adr('B1'))).toEqual(CellAddress.absoluteCol(0, 0, 1)) - }) - - it('case ARc: relative/absolute dependency in removed range should be replaced by #REF', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=Sheet2!$A1', '=Sheet2!A1'], - ], - Sheet2: [ - ['1', '2'], - ], - }) - - engine.removeColumns(1, [0, 1]) - - expectReferenceToHaveRefError(engine, adr('A1')) - expectReferenceToHaveRefError(engine, adr('B1')) - }) - - it('does not truncate any ranges if columns are removed from different sheet', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['', '2', '3'], - ['=SUM(B1:C1)'], - ], - Sheet2: [ - ['1'], - ], - }) - - engine.removeColumns(1, [1, 1]) - - expect(extractRange(engine, adr('A2'))).toEqual(new AbsoluteCellRange(adr('B1'), adr('C1'))) - }) -}) - -describe('Address dependencies, Case 4: remove columns in sheet different than formula or dependency sheet', () => { - it('should not affect dependency when removing columns in not relevant sheet', function() { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1'], - ], - Sheet2: [ - ['1', '=A1'], - ], - }) - - engine.removeColumns(0, [0, 1]) - - expect(extractReference(engine, adr('B1', 1))).toEqual(CellAddress.relative(-1, 0)) - }) - - it('should not affect dependency when removing columns in not relevant sheet, more sheets', function() { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1'], - ], - Sheet2: [ - ['foo'], - ], - Sheet3: [ - ['1', '=Sheet2!A1'], - ], - }) - - engine.removeColumns(0, [0, 1]) - - expect(extractReference(engine, adr('B1', 2))).toEqual(CellAddress.relative(-1, 0, 1)) - }) -}) - -describe('Removing columns - reevaluation', () => { - it('reevaluates', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(B1:D1)', '2', '4', '3'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(3) - - engine.removeColumns(0, [2, 1]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2.5) - }) - - it('dont reevaluate everything', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '', '3'], - ['=COUNTBLANK(A1:C1)'], - ['=SUM(A1:A1)'], - ]) - const a2 = engine.addressMapping.getCell(adr('A2')) - const a3 = engine.addressMapping.getCell(adr('A3')) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const a2setCellValueSpy = spyOn(a2 as any, 'setCellValue') - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const a3setCellValueSpy = spyOn(a3 as any, 'setCellValue') - - engine.removeColumns(0, [1, 1]) - - expect(a2setCellValueSpy).toHaveBeenCalled() - expect(a3setCellValueSpy).not.toHaveBeenCalled() - }) - - it('reevaluates cells which are dependent on structure changes', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '=COLUMNS(A1:C1)'], - ]) - const d1 = engine.addressMapping.getCell(adr('D1')) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const d1setCellValueSpy = spyOn(d1 as any, 'setCellValue') - - engine.removeColumns(0, [1, 1]) - - expect(d1setCellValueSpy).toHaveBeenCalled() - expect(extractRange(engine, adr('C1'))).toEqual(new AbsoluteCellRange(adr('A1'), adr('B1'))) - }) - - it('should reevaluate formula when range reduced to zero', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=SUM(A1:B1)'], - ]) - - const c1 = engine.addressMapping.getCell(adr('C1')) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const c1setCellValueSpy = spyOn(c1 as any, 'setCellValue') - - engine.removeColumns(0, [0, 2]) - - expect(c1setCellValueSpy).toHaveBeenCalled() - expectFunctionToHaveRefError(engine, adr('A1')) - }) - - it('returns changed values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=SUM(A1:B1)'], - ]) - - const changes = engine.removeColumns(0, [0, 1]) - - expect(changes.length).toBe(1) - expect(changes).toEqual([new ExportedCellChange(adr('B1'), 2)]) - }) -}) - -describe('Removing rows - arrays', () => { - it('ArrayFormulaVertex#formula should be updated', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '=TRANSPOSE(A1:C2)'], - ['4', '5', '6'], - ]) - - engine.removeColumns(0, [1, 1]) - - expect(extractMatrixRange(engine, adr('C1'))).toEqual(new AbsoluteCellRange(adr('A1'), adr('B2'))) - }) - - it('ArrayFormulaVertex#address should be updated', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '=TRANSPOSE(A1:C2)'], - ['4', '5', '6'], - ]) - - engine.removeColumns(0, [1, 1]) - - const matrixVertex = engine.addressMapping.getCell(adr('C1')) as ArrayFormulaVertex - expect(matrixVertex.getAddress(engine.lazilyTransformingAstService)).toEqual(adr('C1')) - }) - - it('ArrayFormulaVertex#formula should be updated when different sheets', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '2', '3'], - ['4', '5', '6'], - ], - Sheet2: [ - ['=TRANSPOSE(Sheet1!A1:C2)'], - ], - }) - - engine.removeColumns(0, [1, 1]) - - expect(extractMatrixRange(engine, adr('A1', 1))).toEqual(new AbsoluteCellRange(adr('A1'), adr('B2'))) - }) - - it('should be possible to remove column before array', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=-B3:D4', null, null, 'foo'], - ], {useArrayArithmetic: true}) - - engine.removeColumns(0, [0, 1]) - - const expected = HyperFormula.buildFromArray([ - ['=-A3:C4', null, null, 'foo'], - ], {useArrayArithmetic: true}) - - expectEngineToBeTheSameAs(engine, expected) - }) - - it('removing column across array should not change array', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3, '=-A1:C2', null, null, null, 'foo'], - [4, 5, 6] - ], {useArrayArithmetic: true}) - - engine.removeColumns(0, [4, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2, 3, '=-A1:C2', null, null, 'foo'], - [4, 5, 6] - ], {useArrayArithmetic: true})) - }) - - it('removing column should shrink dependent array', () => { - const engine = HyperFormula.buildFromArray([ - [1, null, 3, '=TRANSPOSE(A1:C2)'], - [2, null, 4], - ], {useArrayArithmetic: true}) - - engine.removeColumns(0, [1, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 3, '=TRANSPOSE(A1:B2)'], - [2, 4], - ], {useArrayArithmetic: true})) - }) - - it('should be REF if no space after removing column', () => { - const engine = HyperFormula.buildFromArray([ - ['=-C2:D2', null, 1], - [null, null, 1, 2] - ], {useArrayArithmetic: true}) - - engine.removeColumns(0, [1, 1]) - - expect(engine.getSheetValues(0)).toEqual([ - [noSpace(), 1], - [null, 1, 2], - ]) - - const expected = HyperFormula.buildFromArray([ - ['=-B2:C2', 1], - [null, 1, 2] - ], {useArrayArithmetic: true}) - - expectEngineToBeTheSameAs(engine, expected) - }) - - it('should be REF, not CYCLE, after removing columns', () => { - const engine = HyperFormula.buildFromArray([ - ['=-C1:D1', null, 1, 2] - ], {useArrayArithmetic: true}) - - engine.removeColumns(0, [1, 1]) - - expect(engine.getSheetValues(0)).toEqual([ - [noSpace(), 1, 2], - ]) - - const expected = HyperFormula.buildFromArray([ - ['=-B1:C1', 1, 2], - ], {useArrayArithmetic: true}) - expectEngineToBeTheSameAs(engine, expected) - }) - - it('should remove array when removing column with left corner', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=MMULT(A1:B2, A1:B2)'], - ['3', '4'], - ]) - - engine.removeColumns(0, [2, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2], - [3, 4] - ])) - }) - - it('should remove array when removing columns with whole matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=MMULT(A1:B2, A1:B2)'], - ['3', '4'], - ]) - - engine.removeColumns(0, [2, 2]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2], - [3, 4] - ])) - }) -}) - -describe('Removing columns - graph', function() { - it('should remove edges from other cells to removed nodes', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=B1'], - ]) - - engine.removeColumns(0, [2, 1]) - - const b1 = engine.addressMapping.getCell(adr('b1')) - expect(engine.graph.adjacentNodes(b1!)).toEqual(new Set()) - }) - - it('should remove vertices from graph', function() { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '4'], - ['1', '2', '3', '4'], - ]) - expect(engine.graph.getNodes().length).toBe(8) - engine.removeColumns(0, [0, 2]) - expect(engine.graph.getNodes().length).toBe(4) // left two vertices in first column, two in last - }) - - it('works if there are empty cells removed', function() { - const engine = HyperFormula.buildFromArray([ - ['1', null, '3'], - ]) - expect(engine.graph.getNodes().length).toBe(2) - engine.removeColumns(0, [1, 1]) - expect(engine.graph.getNodes().length).toBe(2) - }) -}) - -describe('Removing columns - dependencies', () => { - it('should not affect absolute dependencies to other sheet', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '2', '=Sheet2!$A1'], - /* */ - ], - Sheet2: [ - ['3'], - ['4'], - ], - }) - - expect(extractReference(engine, adr('C1'))).toEqual(CellAddress.absoluteCol(0, 0, 1)) - engine.removeColumns(0, [0, 2]) - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.absoluteCol(0, 0, 1)) - }) - -}) - -describe('Removing columns - ranges', function() { - it('shift ranges in range mapping, range start at right of removed columns', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['', '=SUM(B1:C1)', ''], - /**/ - ]) - - engine.removeColumns(0, [0, 1]) - - const range = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('B1')) - const a1 = engine.addressMapping.getCell(adr('A1')) - expect(engine.graph.existsEdge(a1!, range)).toBe(true) - }) - - it('shift ranges in range mapping, range start before removed columns', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['=SUM(A1:C1)', '', ''], - /* */ - ]) - - engine.removeColumns(0, [1, 2]) - - const range = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A1')) - const a1 = engine.addressMapping.getCell(adr('A1')) - expect(engine.graph.existsEdge(a1!, range)).toBe(true) - }) - - it('shift ranges in range mapping, whole range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '=SUM(A1:C1)'], - /* */ - ]) - const range = engine.rangeMapping.getRangeVertex(adr('A1'), adr('C1')) as RangeVertex - - engine.removeColumns(0, [0, 3]) - - const ranges = Array.from(engine.rangeMapping.rangesInSheet(0)) - expect(ranges.length).toBe(0) - expect(engine.graph.hasNode(range)).toBe(false) - }) - - it('does not truncate any ranges if columns are removed from different sheet', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '2', '=SUM(A1:B1)'], - ], - Sheet2: [ - ['1'], - ], - }) - - engine.removeColumns(1, [0, 1]) - - expect(extractRange(engine, adr('C1'))).toEqual(new AbsoluteCellRange(adr('A1'), adr('B1'))) - }) -}) - -describe('Removing columns - sheet dimensions', () => { - it('should do nothing when removing column outside effective sheet', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ]) - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const recalcSpy = spyOn(engine.evaluator as any, 'partialRun') - engine.removeColumns(0, [1, 1]) - engine.removeColumns(0, [10, 6]) - - expect(recalcSpy).not.toHaveBeenCalled() - expect(engine.getSheetDimensions(0)).toEqual({ - width: 1, - height: 1, - }) - }) - - it('should throw error when trying to remove non positive number of columns', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ]) - - expect(() => engine.removeColumns(0, [1, 0])).toThrowError() - }) -}) - -describe('Removing columns - column index', () => { - it('should update column index when adding column', () => { - const engine = HyperFormula.buildFromArray([ - ['', '1', '=VLOOKUP(2, A1:A10, 1, TRUE())'], - ], {useColumnIndex: true}) - - engine.removeColumns(0, [0, 1]) - - const index = (engine.columnSearch as ColumnIndex) - expectArrayWithSameContent([0], index.getValueIndex(0, 0, 1).index) - }) -}) - -describe('Removing columns - column range', () => { - it('removing column in the middle of column range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '=SUM(A:C)'] - ]) - - engine.removeColumns(0, [1, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '3', '=SUM(A:B)'] - ])) - }) - - it('removing column in at the start of column range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '=SUM(A:C)'] - ]) - - engine.removeColumns(0, [0, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['2', '3', '=SUM(A:B)'] - ])) - }) - - it('removing column in at the end of column range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '=SUM(A:C)'] - ]) - - engine.removeColumns(0, [2, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '2', '=SUM(A:B)'] - ])) - }) -}) - -describe('Removing columns - row range', () => { - it('should not affect row range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1', '1'], - ['2', '2', '2'], - [null, null, '=SUM(1:2)'] - ]) - - engine.removeColumns(0, [0, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '1'], - ['2', '2'], - [null, '=SUM(1:2)'] - ])) - }) -}) - -describe('Removing columns - merge ranges', () => { - it('should merge ranges', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents({sheet: 0, col: 1, row: 2}, 7) - engine.setCellContents({sheet: 0, col: 0, row: 3}, '=SUM(B2:C3)') - engine.setCellContents({sheet: 0, col: 0, row: 6}, '=SUM(A4:B6)') - engine.setCellContents({sheet: 0, col: 2, row: 6}, '=SUM(A4:A6)') - - verifyRangesInSheet(engine, 0, ['A4:A6', 'A4:B6', 'B2:C3']) - verifyValues(engine) - - engine.addColumns(0, [0, 2]) - - verifyRangesInSheet(engine, 0, ['C4:C6', 'C4:D6', 'D2:E3']) - verifyValues(engine) - - engine.removeColumns(0, [3, 1]) - - verifyRangesInSheet(engine, 0, ['C4:C6', 'D2:D3']) - verifyValues(engine) - }) - - it('Should properly deallocate all nodes', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents({sheet: 0, col: 2, row: 3}, '=SUM(B2:C2)') - engine.setCellContents({sheet: 0, col: 3, row: 5}, '=SUM(B2:C3)') - - engine.addColumns(0, [2, 2]) - engine.removeColumns(0, [4, 1]) - - verifyRangesInSheet(engine, 0, ['B2:D2', 'B2:D3']) - verifyValues(engine) - - engine.setCellContents({sheet: 0, col: 4, row: 5}, null) - - verifyRangesInSheet(engine, 0, []) - verifyValues(engine) - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('should merge ranges in proper order', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents({sheet: 0, col: 0, row: 0}, '=SUM(D5:F5)') - engine.setCellContents({sheet: 0, col: 1, row: 0}, '=SUM(D5:E5)') - engine.setCellContents({sheet: 0, col: 2, row: 0}, '=SUM(D5:D5)') - - engine.removeColumns(0, [4, 1]) - - verifyRangesInSheet(engine, 0, ['D5:E5', 'D5:D5']) - verifyValues(engine) - - engine.setCellContents(adr('A1'), null) - engine.setCellContents(adr('B1'), null) - engine.setCellContents(adr('C1'), null) - - verifyRangesInSheet(engine, 0, []) - verifyValues(engine) - }) - - it('should merge ranges when removing multiple columns', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents({sheet: 0, col: 3, row: 0}, '=SUM(A5:A5)') - engine.setCellContents({sheet: 0, col: 0, row: 0}, '=SUM(A5:C5)') - - verifyRangesInSheet(engine, 0, ['A5:A5', 'A5:C5']) - - engine.removeColumns(0, [1, 2]) - - verifyRangesInSheet(engine, 0, ['A5:A5']) - verifyValues(engine) - - engine.setCellContents(adr('A1'), null) - engine.setCellContents(adr('B1'), null) - - verifyRangesInSheet(engine, 0, []) - verifyValues(engine) - }) - - it('should undo merge ranges', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents({sheet: 0, col: 2, row: 1}, 7) - engine.setCellContents({sheet: 0, col: 3, row: 0}, '=SUM(B2:C3)') - engine.setCellContents({sheet: 0, col: 6, row: 0}, '=SUM(A4:B6)') - engine.setCellContents({sheet: 0, col: 6, row: 2}, '=SUM(A4:A6)') - - engine.addColumns(0, [0, 2]) - engine.removeColumns(0, [3, 1]) - - verifyRangesInSheet(engine, 0, ['C4:C6', 'D2:D3']) - verifyValues(engine) - - engine.undo() - - verifyRangesInSheet(engine, 0, ['C4:C6', 'C4:D6', 'D2:E3']) - - verifyValues(engine) - }) -}) - -describe('Removing columns - changes', () => { - it('returns the same changes regardless of address mapping policy', () => { - const data = [ - [1, 2, '=A2'], - [3, 4, '=B1'], - ] - - const alwaysDenseEngine = HyperFormula.buildFromArray(data, { chooseAddressMappingPolicy: new AlwaysDense() }) - const alwaysDenseChanges = alwaysDenseEngine.removeColumns(0, [0, 2]) - const alwaysSparseEngine = HyperFormula.buildFromArray(data, { chooseAddressMappingPolicy: new AlwaysSparse() }) - const alwaysSparseChanges = alwaysSparseEngine.removeColumns(0, [0, 2]) - - expect(alwaysDenseChanges).toEqual(alwaysSparseChanges) - }) -}) - -describe('Removing column where there are empty rows in the address mapping (DenseStrategy) - issue #1406', () => { - it('should not throw errors (stackblitz reproduction)', () => { - const hf = HyperFormula.buildEmpty({ - licenseKey: 'gpl-v3', - chooseAddressMappingPolicy: new AlwaysDense() - }) - - const sheetName = hf.addSheet('main') - const sheetId = hf.getSheetId(sheetName)! - - hf.setCellContents( - { row: 0, col: 0, sheet: sheetId }, - [ - ['test', null, undefined, 4], - [null, 3, '=ISBLANK(B1)'], - ] - ) - - hf.addRows(0, [2, 1]) - hf.addRows(0, [3, 1]) - hf.setCellContents( - { row: 3, col: 0, sheet: sheetId }, - 'will-it-break?' - ) - hf.removeColumns(0, [0, 1]) //one column removed - - expect(hf.getSheetSerialized(0)).toEqual([ - [null, null, 4], - [3, '=ISBLANK(A1)'], - ]) - }) - - it('should not throw errors (user reported case)', () => { - const data= [ - [], - [null, 1], - [null, 2], - [null, '=sum(B2:B3)'] - ] - - const options = { licenseKey: 'gpl-v3', chooseAddressMappingPolicy: new AlwaysDense() } - const hf=HyperFormula.buildEmpty(options) - - hf.addSheet('sheet1') - hf.setSheetContent(0, data) - hf.removeColumns(0, [0, 1]) - - expect(hf.getSheetSerialized(0)).toEqual([ - [], - [1], - [2], - ['=SUM(A2:A3)'] - ]) - }) -}) diff --git a/test/unit/cruds/removing-rows.spec.ts b/test/unit/cruds/removing-rows.spec.ts deleted file mode 100644 index cf02c8b7df..0000000000 --- a/test/unit/cruds/removing-rows.spec.ts +++ /dev/null @@ -1,1131 +0,0 @@ -import {ExportedCellChange, HyperFormula, InvalidArgumentsError} from '../../../src' -import {AbsoluteCellRange} from '../../../src/AbsoluteCellRange' -import {ArrayFormulaVertex} from '../../../src/DependencyGraph' -import {ColumnIndex} from '../../../src/Lookup/ColumnIndex' -import {CellAddress} from '../../../src/parser' -import { - adr, - expectArrayWithSameContent, - expectEngineToBeTheSameAs, - expectFunctionToHaveRefError, - expectReferenceToHaveRefError, - extractMatrixRange, - extractRange, - extractReference, graphReversedAdjacentNodes, - noSpace, - verifyRangesInSheet, - verifyValues, -} from '../testUtils' - -describe('Removing rows - checking if its possible', () => { - it('no if starting row is negative', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToRemoveRows(0, [-1, 1])).toEqual(false) - }) - - it('no if starting row is not an integer', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToRemoveRows(0, [1.5, 2])).toEqual(false) - expect(engine.isItPossibleToRemoveRows(0, [NaN, 2])).toEqual(false) - expect(engine.isItPossibleToRemoveRows(0, [Infinity, 2])).toEqual(false) - expect(engine.isItPossibleToRemoveRows(0, [-Infinity, 2])).toEqual(false) - }) - - it('no if number of rows is negative', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToRemoveRows(0, [0, -1])).toEqual(false) - }) - - it('no if number of rows is not an integer', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToRemoveRows(0, [0, 1.5])).toEqual(false) - expect(engine.isItPossibleToRemoveRows(0, [0, NaN])).toEqual(false) - expect(engine.isItPossibleToRemoveRows(0, [0, Infinity])).toEqual(false) - expect(engine.isItPossibleToRemoveRows(0, [0, -Infinity])).toEqual(false) - }) - - it('no if sheet does not exist', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToRemoveRows(1, [0, 1])).toEqual(false) - expect(engine.isItPossibleToRemoveRows(1.5, [0, 1])).toEqual(false) - expect(engine.isItPossibleToRemoveRows(-1, [0, 1])).toEqual(false) - expect(engine.isItPossibleToRemoveRows(NaN, [0, 1])).toEqual(false) - expect(engine.isItPossibleToRemoveRows(Infinity, [0, 1])).toEqual(false) - expect(engine.isItPossibleToRemoveRows(-Infinity, [0, 1])).toEqual(false) - }) - - it('yes if theres an array in place where we remove', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=TRANSPOSE(A1:B2)'], - ]) - - expect(engine.isItPossibleToRemoveRows(0, [1, 1])).toEqual(true) - expect(engine.isItPossibleToRemoveRows(0, [1, 2])).toEqual(true) - expect(engine.isItPossibleToRemoveRows(0, [2, 1])).toEqual(true) - expect(engine.isItPossibleToRemoveRows(0, [3, 1])).toEqual(true) - expect(engine.isItPossibleToRemoveRows(0, [4, 1])).toEqual(true) - }) - - it('yes otherwise', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToRemoveRows(0, [0, 1])).toEqual(true) - expect(engine.isItPossibleToRemoveRows(0, [1, 1])).toEqual(true) - expect(engine.isItPossibleToRemoveRows(0, [1, 2])).toEqual(true) - }) -}) - -describe('Address dependencies, Case 1: same sheet', () => { - it('case Aa: absolute dependency above removed row should not be affected', () => { - const engine = HyperFormula.buildFromArray([ - [null], - ['1'], - [null], // row to delete - ['=A$2'], - ]) - - engine.removeRows(0, [2, 1]) - - expect(extractReference(engine, adr('A3'))).toEqual(CellAddress.absoluteRow(0, 1)) - }) - - it('case Ab: absolute dependency below removed row should be shifted', () => { - const engine = HyperFormula.buildFromArray([ - ['=A$3'], - [null], // row to delete - ['42'], - ]) - - engine.removeRows(0, [1, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.absoluteRow(0, 1)) - }) - - it('case Ac: absolute dependency in removed row range should be replaced by #REF', () => { - const engine = HyperFormula.buildFromArray([ - ['=A$2'], - [null], // row to delete - ]) - - engine.removeRows(0, [1, 1]) - - expectReferenceToHaveRefError(engine, adr('A1')) - }) - - it('case Raa: relative dependency and formula above removed rows should not be affected', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ['=A1'], - ['2'], - ]) - - engine.removeRows(0, [2, 1]) - - expect(extractReference(engine, adr('A2'))).toEqual(CellAddress.relative(0, -1)) - }) - - it('case Rab: relative address should be shifted when only formula is moving', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ['1'], - ['2'], - ['=A1'], - ]) - - engine.removeRows(0, [1, 2]) - - expect(extractReference(engine, adr('A2'))).toEqual(CellAddress.relative(0, -1)) - }) - - it('case Rba: relative address should be shifted when only dependency is moving', () => { - const engine = HyperFormula.buildFromArray([ - ['=A4'], - ['1'], - ['2'], - ['42'], - ]) - - engine.removeRows(0, [1, 2]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 1)) - }) - - it('case Rbb: relative address should not be affected when dependency and formula is moving', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['=A4'], - ['42'], - ]) - - engine.removeRows(0, [0, 2]) - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 1)) - }) - - it('case Rca: relative dependency in deleted row range should be replaced by #REF', () => { - const engine = HyperFormula.buildFromArray([ - ['=A3'], - ['1'], - ['2'], - ['3'], - ]) - - engine.removeRows(0, [1, 2]) - expectReferenceToHaveRefError(engine, adr('A1')) - }) - - it('case Rcb: relative dependency in deleted row range should be replaced by #REF', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - ['=A2'], - ]) - - engine.removeRows(0, [0, 2]) - expectReferenceToHaveRefError(engine, adr('A2')) - }) - - it('case Rca, range', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(A2:A3)'], - ['1'], // - ['2'], // - ]) - engine.removeRows(0, [1, 2]) - expectFunctionToHaveRefError(engine, adr('A1')) - }) -}) - -describe('Address dependencies, Case 2: formula in sheet where we make crud with dependency to other sheet', () => { - it('case A: should not affect absolute dependencies', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1'], // row to delete - ['=Sheet2!A$1'], - ], - Sheet2: [ - ['2'], - ], - }) - - expect(extractReference(engine, adr('A2'))).toEqual(CellAddress.absoluteRow(0, 0, 1)) - engine.removeRows(0, [0, 1]) - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.absoluteRow(0, 0, 1)) - }) - - it('case Ra: removing row above formula should shift dependency', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1'], // row to delete - ['=Sheet2!A1'], - ], - Sheet2: [ - ['2'], - ], - }) - - expect(extractReference(engine, adr('A2'))).toEqual(CellAddress.relative(0, -1, 1)) - engine.removeRows(0, [0, 1]) - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 0, 1)) - }) - - it('case Rb: removing row below formula should not affect dependency', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=Sheet2!A1'], - ['1'], // row to delete - ], - Sheet2: [ - ['2'], - ], - }) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 0, 1)) - engine.removeRows(0, [1, 1]) - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 0, 1)) - }) -}) - -describe('Address dependencies, Case 3: formula in different sheet', () => { - it('case ARa: relative/absolute dependency below removed row should be shifted', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=Sheet2!A3'], - ['=Sheet2!A3'], - ['=Sheet2!A3'], - ['=Sheet2!A$3'], - ], - Sheet2: [ - ['1'], - ['2'], // row to delete - ['3'], - ], - }) - - engine.removeRows(1, [1, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 1, 1)) - expect(extractReference(engine, adr('A2'))).toEqual(CellAddress.relative(0, 0, 1)) - expect(extractReference(engine, adr('A3'))).toEqual(CellAddress.relative(0, -1, 1)) - expect(extractReference(engine, adr('A4'))).toEqual(CellAddress.absoluteRow(0, 1, 1)) - }) - - it('case ARb: relative/absolute dependency above removed row should not be affected', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=Sheet2!A1'], - ['=Sheet2!A$1'], - ], - Sheet2: [ - ['0'], - ['1'], // row to delete - ], - }) - - engine.removeRows(1, [1, 1]) - - expect(extractReference(engine, adr('A1'))).toEqual(CellAddress.relative(0, 0, 1)) - expect(extractReference(engine, adr('A2'))).toEqual(CellAddress.absoluteRow(0, 0, 1)) - }) - - it('case ARc: relative/absolute dependency in removed range should be replaced by #REF', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=Sheet2!A$1'], - ['=Sheet2!A1'], - ], - Sheet2: [ - ['1'], // row to delete - ['2'], - ], - }) - - engine.removeRows(1, [0, 1]) - - expectReferenceToHaveRefError(engine, adr('A1')) - expectReferenceToHaveRefError(engine, adr('A2')) - }) - - it('does not truncate any ranges if rows are removed from different sheet', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - [null, '=SUM(A2:A3)'], - ['2'], - ['3'], - ], - Sheet2: [ - ['1'], - ], - }) - - engine.removeRows(1, [1, 1]) - - expect(extractRange(engine, adr('B1'))).toEqual(new AbsoluteCellRange(adr('A2'), adr('A3'))) - }) -}) - -describe('Address dependencies, Case 4: remove rows in sheet different than formula or dependency sheet', () => { - it('should not affect dependency when removing rows in not relevant sheet', function() { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1'], // to remove - ], - Sheet2: [ - ['1'], - ['=A1'], - ], - }) - - engine.removeRows(0, [0, 1]) - - expect(extractReference(engine, adr('A2', 1))).toEqual(CellAddress.relative(0, -1)) - }) - - it('should not affect dependency when removing rows in not relevant sheet, more sheets', function() { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1'], // to remove - ], - Sheet2: [ - ['foo'], - ], - Sheet3: [ - ['1'], - ['=Sheet2!A1'], - ], - }) - - engine.removeRows(0, [0, 1]) - - expect(extractReference(engine, adr('A2', 2))).toEqual(CellAddress.relative(0, -1, 1)) - }) -}) - -describe('Removing rows - range dependencies, same sheet', () => { - it('truncates range by one row from top if topmost row removed', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=SUM(A2:A3)'], - ['1'], - ['2'], - ]) - - engine.removeRows(0, [1, 1]) - - expect(extractRange(engine, adr('B1'))).toEqual(new AbsoluteCellRange(adr('A2'), adr('A2'))) - }) - - it('truncates range by one row from bottom if last row removed', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=SUM(A2:A3)'], - ['1'], - ['2'], - ]) - - engine.removeRows(0, [2, 1]) - - expect(extractRange(engine, adr('B1'))).toEqual(new AbsoluteCellRange(adr('A2'), adr('A2'))) - }) - - it('truncates range by rows from top if topmost rows removed', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=SUM(A2:A5)'], - ['2'], - ['3'], - ['4'], - ['5'], - ]) - - engine.removeRows(0, [1, 2]) - - expect(extractRange(engine, adr('B1'))).toEqual(new AbsoluteCellRange(adr('A2'), adr('A3'))) - }) - - it('truncates range by rows from top if topmost rows removed - removing does not have to start with range', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=SUM(A3:A6)'], - [null], - ['3'], - ['4'], - ['5'], - ['6'], - ]) - - engine.removeRows(0, [1, 3]) - - expect(extractRange(engine, adr('B1'))).toEqual(new AbsoluteCellRange(adr('A2'), adr('A3'))) - }) - - it('truncates range by rows from top if topmost rows removed - removing does not have to start with range but may end on start', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=SUM(A3:A6)'], - [null], - ['3'], - ['4'], - ['5'], - ['6'], - ]) - - engine.removeRows(0, [1, 2]) - - expect(extractRange(engine, adr('B1'))).toEqual(new AbsoluteCellRange(adr('A2'), adr('A4'))) - }) - - it('truncates range by rows from bottom if bottomest rows removed', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=SUM(A2:A5)'], - ['2'], - ['3'], - ['4'], - ['5'], - ]) - - engine.removeRows(0, [3, 2]) - - expect(extractRange(engine, adr('B1'))).toEqual(new AbsoluteCellRange(adr('A2'), adr('A3'))) - }) - - it('truncates range by rows from bottom if bottomest rows removed - removing does not have to end with range', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=SUM(A2:A5)'], - ['2'], - ['3'], - ['4'], - ['5'], - [null], - ]) - - engine.removeRows(0, [3, 3]) - - expect(extractRange(engine, adr('B1'))).toEqual(new AbsoluteCellRange(adr('A2'), adr('A3'))) - }) - - it('truncates range by rows from bottom if bottomest rows removed - removing does not have to end with range but may start on end', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=SUM(A2:A5)'], - ['2'], - ['3'], - ['4'], - ['5'], - [null], - ]) - - engine.removeRows(0, [4, 2]) - - expect(extractRange(engine, adr('B1'))).toEqual(new AbsoluteCellRange(adr('A2'), adr('A4'))) - }) -}) - -describe('Removing rows - reevaluation', () => { - it('reevaluates cells', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=COUNTBLANK(A1:A3)'], - [null], // deleted - ['3'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(1) - engine.removeRows(0, [1, 1]) - expect(engine.getCellValue(adr('B1'))).toEqual(0) - }) - - it('dont reevaluate everything', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=COUNTBLANK(A1:A3)', '=SUM(A1:A1)'], - [null], // deleted - ['3'], - ]) - const b1 = engine.addressMapping.getCell(adr('B1')) - const c1 = engine.addressMapping.getCell(adr('C1')) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const b1setCellValueSpy = spyOn(b1 as any, 'setCellValue') - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const c1setCellValueSpy = spyOn(c1 as any, 'setCellValue') - - engine.removeRows(0, [1, 1]) - - expect(b1setCellValueSpy).toHaveBeenCalled() - expect(c1setCellValueSpy).not.toHaveBeenCalled() - }) - - it('reevaluates cells which are dependent on structure changes', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=COLUMNS(A1:B1)'], - ['1'], - ]) - const c1 = engine.addressMapping.getCell(adr('C1')) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const c1setCellValueSpy = spyOn(c1 as any, 'setCellValue') - - engine.removeRows(0, [1, 1]) - - expect(c1setCellValueSpy).toHaveBeenCalled() - }) - - it('should reevaluate formula when range reduced to zero', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['=SUM(A1:A2)'], - ]) - - const a3 = engine.addressMapping.getCell(adr('A3')) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const a3setCellValueSpy = spyOn(a3 as any, 'setCellValue') - - engine.removeRows(0, [0, 2]) - - expect(a3setCellValueSpy).toHaveBeenCalled() - expectFunctionToHaveRefError(engine, adr('A1')) - }) -}) - -describe('Removing rows - arrays', () => { - it('ArrayFormulaVertex#formula should be updated', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '4'], - ['2', '5'], - ['3', '6'], - ['=TRANSPOSE(A1:B3)'], - ]) - - engine.removeRows(0, [1, 1]) - - expect(extractMatrixRange(engine, adr('A3'))).toEqual(new AbsoluteCellRange(adr('A1'), adr('B2'))) - }) - - it('ArrayFormulaVertex#address should be updated', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '4'], - ['2', '5'], - ['3', '6'], - ['=TRANSPOSE(A1:B3)'], - ]) - - engine.removeRows(0, [1, 1]) - - const matrixVertex = engine.addressMapping.getCell(adr('A3')) as ArrayFormulaVertex - expect(matrixVertex.getAddress(engine.lazilyTransformingAstService)).toEqual(adr('A3')) - }) - - it('ArrayFormulaVertex#formula should be updated when different sheets', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '4'], - ['2', '5'], - ['3', '6'], - ], - Sheet2: [ - ['=TRANSPOSE(Sheet1!A1:B3)'], - ], - }) - - engine.removeRows(0, [1, 1]) - - expect(extractMatrixRange(engine, adr('A1', 1))).toEqual(new AbsoluteCellRange(adr('A1'), adr('B2'))) - }) - - it('should be possible to remove row above array', () => { - const engine = HyperFormula.buildFromArray([ - [], - ['=-C2:D4'], - [], - [], - ['foo'] - ], {useArrayArithmetic: true}) - - engine.removeRows(0, [0, 1]) - - const expected = HyperFormula.buildFromArray([ - ['=-C1:D3'], - [], - [], - ['foo'] - ], {useArrayArithmetic: true}) - - expectEngineToBeTheSameAs(engine, expected) - }) - - it('removing row across array should not change array', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2], [3, 4], [5, 6], - ['=-A1:B3'], - [], [], [], - ['foo'] - ], {useArrayArithmetic: true}) - - engine.removeRows(0, [4, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2], [3, 4], [5, 6], - ['=-A1:B3'], - [], [], - ['foo'] - ], {useArrayArithmetic: true})) - }) - - it('removing row should shrink dependent array', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2], - [], - [3, 4], - ['=TRANSPOSE(A1:B3)'] - ], {useArrayArithmetic: true}) - - engine.removeRows(0, [1, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2], - [3, 4], - ['=TRANSPOSE(A1:B2)'] - ], {useArrayArithmetic: true})) - }) - - it('should be REF if no space after removing row', () => { - const engine = HyperFormula.buildFromArray([ - ['=-B3:B4'], - [], - [1, 1], - [null, 2], - ], {useArrayArithmetic: true}) - - engine.removeRows(0, [1, 1]) - - expect(engine.getSheetValues(0)).toEqual([ - [noSpace()], - [1, 1], - [null, 2], - ]) - - const expected = HyperFormula.buildFromArray([ - ['=-B2:B3'], - [1, 1], - [null, 2] - ], {useArrayArithmetic: true}) - expectEngineToBeTheSameAs(engine, expected) - }) - - it('should be REF, not CYCLE, after removing rows', () => { - const engine = HyperFormula.buildFromArray([ - ['=-A3:A4'], - [], - [1], - [2] - ], {useArrayArithmetic: true}) - - engine.removeRows(0, [1, 1]) - - expect(engine.getSheetValues(0)).toEqual([ - [noSpace()], - [1], - [2] - ]) - - const expected = HyperFormula.buildFromArray([ - ['=-A2:A3'], - [1], - [2] - ], {useArrayArithmetic: true}) - expectEngineToBeTheSameAs(engine, expected) - }) - - it('should remove array when removing row with left corner', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=MMULT(A1:B2, A1:B2)'], - ]) - - engine.removeRows(0, [2, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2], - [3, 4] - ])) - }) - - it('should remove array when removing rows with whole matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=MMULT(A1:B2, A1:B2)'], - ]) - - engine.removeRows(0, [2, 2]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - [1, 2], - [3, 4] - ])) - }) -}) - -describe('Removing rows - graph', function() { - it('should remove edges from other cells to removed nodes', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['=A2'], // - ]) - - engine.removeRows(0, [2, 1]) - - const a2 = engine.addressMapping.getCell(adr('A2')) - expect(engine.graph.adjacentNodes(a2!)).toEqual(new Set()) - }) - - it('should remove vertices from graph', function() { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ]) - expect(engine.graph.getNodes().length).toBe(4) - engine.removeRows(0, [0, 2]) - expect(engine.graph.getNodes().length).toBe(0) - }) - - it('works if there are empty cells removed', function() { - const engine = HyperFormula.buildFromArray([ - ['1'], - [null], - ['3'], - ]) - expect(engine.graph.getNodes().length).toBe(2) - engine.removeRows(0, [1, 1]) - expect(engine.graph.getNodes().length).toBe(2) - }) -}) - -describe('Removing rows - range mapping', function() { - it('shift ranges in range mapping, range start below removed rows', () => { - const engine = HyperFormula.buildFromArray([ - ['1', null], - ['2', '=SUM(A2:A3)'], - ['3', null], - ]) - - engine.removeRows(0, [0, 1]) - const range = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A2')) - const a1 = engine.addressMapping.getCell(adr('A1')) - expect(engine.graph.existsEdge(a1!, range)).toBe(true) - }) - - it('shift ranges in range mapping, range start above removed rows', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=SUM(A1:A3)'], - ['2', null], - ['3', null], - ]) - - engine.removeRows(0, [1, 2]) - const range = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A1')) - const a1 = engine.addressMapping.getCell(adr('A1')) - expect(engine.graph.existsEdge(a1!, range)).toBe(true) - }) - - it('shift ranges in range mapping, whole range', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - ['=SUM(A1:A3)'], - ]) - - const range = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A3')) - engine.removeRows(0, [0, 3]) - const ranges = Array.from(engine.rangeMapping.rangesInSheet(0)) - expect(ranges.length).toBe(0) - expect(engine.graph.hasNode(range)).toBe(false) - }) - - it('should remove smaller range dependency', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - ['=SUM(A1:A2)'], - ['=SUM(A1:A3)'], - ]) - - const a1a3 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A3')) - expect(graphReversedAdjacentNodes(engine.graph, a1a3).length).toBe(2) - engine.removeRows(0, [0, 2]) - const a1a1 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A1')) - expect(a1a1).toBe(a1a3) - expect(graphReversedAdjacentNodes(engine.graph, a1a1).length).toBe(1) - }) -}) - -describe('Removing rows - sheet dimensions', () => { - it('should do nothing when removed row outside effective sheet', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ]) - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const recalcSpy = spyOn(engine.evaluator as any, 'partialRun') - engine.removeRows(0, [1, 1]) - engine.removeRows(0, [10, 6]) - - expect(recalcSpy).not.toHaveBeenCalled() - expect(engine.getSheetDimensions(0)).toEqual({ - width: 1, - height: 1, - }) - }) - - it('should throw error when trying to remove non positive number of rows', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ]) - - expect(() => engine.removeRows(0, [1, 0])).toThrow(new InvalidArgumentsError('starting row to be smaller than the ending row.')) - }) - - it('returns changed values', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['=SUM(A1:A2)'], - ]) - - const changes = engine.removeRows(0, [0, 1]) - - expect(changes.length).toBe(1) - expect(changes).toContainEqual(new ExportedCellChange(adr('A2'), 2)) - }) -}) - -describe('Removing rows - column index', () => { - it('should update column index when adding row', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=VLOOKUP(2, A1:A10, 1, TRUE())'], - [null], - ['2'], - ], {useColumnIndex: true}) - - engine.removeRows(0, [1, 1]) - - const index = (engine.columnSearch as ColumnIndex) - - expectArrayWithSameContent([0], index.getValueIndex(0, 0, 1).index) - expectArrayWithSameContent([1], index.getValueIndex(0, 0, 2).index) - }) -}) - -describe('Removing rows - row range', () => { - it('removing rows - start of row range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['1', '2'], - ['1', '2'], - ['=SUM(1:3)'] - ]) - - engine.removeRows(0, [0, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '2'], - ['1', '2'], - ['=SUM(1:2)'] - ])) - }) - - it('removing rows - middle of row range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['1', '2'], - ['1', '2'], - ['=SUM(1:3)'] - ]) - - engine.removeRows(0, [1, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '2'], - ['1', '2'], - ['=SUM(1:2)'] - ])) - }) - - it('removing rows - end of row range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['1', '2'], - ['1', '2'], - ['=SUM(1:3)'] - ]) - - engine.removeRows(0, [2, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '2'], - ['1', '2'], - ['=SUM(1:2)'] - ])) - }) -}) - -describe('Removing rows - column range', () => { - it('should not affect column range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['1', '2'], - ['1', '2', '=SUM(A:B)'], - ]) - - engine.removeRows(0, [0, 1]) - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([ - ['1', '2'], - ['1', '2', '=SUM(A:B)'], - ])) - }) -}) - -describe('Removing rows - merge ranges', () => { - it('should work', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents({sheet: 0, col: 4, row: 0}, '=SUM(A1:C1)') - engine.setCellContents({sheet: 0, col: 3, row: 1}, '=SUM(A1:C2)') - engine.setCellContents({sheet: 0, col: 4, row: 0}, '=SUM(A1:C2)') - - verifyRangesInSheet(engine, 0, ['A1:C1', 'A1:C2']) - - engine.addRows(0, [1, 2]) - engine.removeRows(0, [3, 1]) - - verifyRangesInSheet(engine, 0, ['A1:C1', 'A1:C2', 'A1:C3']) - - engine.setCellContents({sheet: 0, col: 4, row: 0}, '=SUM(B2:B3)') - engine.addRows(0, [1, 2]) - - verifyRangesInSheet(engine, 0, ['B4:B5']) - }) - - it('should not remove too much', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents({sheet: 0, col: 3, row: 0}, '=SUM(A1:C2)') - engine.setCellContents({sheet: 0, col: 4, row: 0}, '=SUM(A1:C1)') - - verifyRangesInSheet(engine, 0, ['A1:C1', 'A1:C2']) - - engine.addRows(0, [0, 2]) - engine.removeRows(0, [3, 1]) - - verifyRangesInSheet(engine, 0, ['A3:C3']) - verifyValues(engine) - - engine.setCellContents({sheet: 0, col: 3, row: 2}, '=SUM(A2:B3)') - engine.setCellContents({sheet: 0, col: 4, row: 2}, '=SUM(A2:B3)') - - verifyRangesInSheet(engine, 0, ['A2:B3']) - verifyValues(engine) - }) - - it('should merge ranges', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents({sheet: 0, col: 2, row: 1}, 7) - engine.setCellContents({sheet: 0, col: 3, row: 0}, '=SUM(B2:C3)') - engine.setCellContents({sheet: 0, col: 6, row: 0}, '=SUM(D1:F2)') - engine.setCellContents({sheet: 0, col: 6, row: 2}, '=SUM(D1:F1)') - - verifyRangesInSheet(engine, 0, ['D1:F1', 'D1:F2', 'B2:C3']) - verifyValues(engine) - - engine.addRows(0, [0, 2]) - - verifyRangesInSheet(engine, 0, ['D3:F3', 'D3:F4', 'B4:C5']) - verifyValues(engine) - - engine.removeRows(0, [3, 1]) - - verifyRangesInSheet(engine, 0, ['D3:F3', 'B4:C4']) - verifyValues(engine) - }) - - it('Should properly deallocate all nodes', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents({sheet: 0, col: 3, row: 2}, '=SUM(B2:C2)') - engine.setCellContents({sheet: 0, col: 5, row: 3}, '=SUM(B2:C3)') - - engine.addRows(0, [2, 2]) - engine.removeRows(0, [4, 1]) - - verifyRangesInSheet(engine, 0, ['B2:C2', 'B2:C3', 'B2:C4']) - verifyValues(engine) - - engine.setCellContents({sheet: 0, col: 5, row: 4}, null) - - verifyRangesInSheet(engine, 0, []) - verifyValues(engine) - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('should merge ranges in proper order', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents({sheet: 0, col: 0, row: 0}, '=SUM(A4:A6)') - engine.setCellContents({sheet: 0, col: 0, row: 1}, '=SUM(A4:A5)') - engine.setCellContents({sheet: 0, col: 0, row: 2}, '=SUM(A4:A4)') - - engine.removeRows(0, [4, 1]) - - verifyRangesInSheet(engine, 0, ['A4:A5', 'A4:A4']) - verifyValues(engine) - - engine.setCellContents(adr('A1'), null) - engine.setCellContents(adr('A2'), null) - engine.setCellContents(adr('A3'), null) - - verifyRangesInSheet(engine, 0, []) - verifyValues(engine) - }) - - it('should merge ranges with subranges in proper order', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents({sheet: 0, col: 0, row: 1}, '=SUM(E1:E1)') - engine.setCellContents({sheet: 0, col: 0, row: 0}, '=SUM(E1:E2)') - - engine.addRows(0, [1, 2]) - - verifyRangesInSheet(engine, 0, ['E1:E1', 'E1:E2', 'E1:E3', 'E1:E4']) - verifyValues(engine) - - engine.removeRows(0, [2, 1]) - - verifyRangesInSheet(engine, 0, ['E1:E1', 'E1:E2', 'E1:E3']) - verifyValues(engine) - - engine.setCellContents(adr('A1'), null) - engine.setCellContents(adr('A3'), null) - - verifyRangesInSheet(engine, 0, []) - verifyValues(engine) - }) - - it('should merge ranges when removing multiple rows', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents({sheet: 0, col: 0, row: 3}, '=SUM(E1:E1)') - engine.setCellContents({sheet: 0, col: 0, row: 0}, '=SUM(E1:E3)') - - verifyRangesInSheet(engine, 0, ['E1:E1', 'E1:E3']) - - engine.removeRows(0, [1, 2]) - - verifyRangesInSheet(engine, 0, ['E1:E1']) - verifyValues(engine) - - engine.setCellContents(adr('A1'), null) - engine.setCellContents(adr('A2'), null) - - verifyRangesInSheet(engine, 0, []) - verifyValues(engine) - }) - - it('should merge ranges when removing multiple rows 2', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents({sheet: 0, col: 0, row: 3}, '=SUM(E1:E1)') - engine.setCellContents({sheet: 0, col: 0, row: 2}, '=SUM(E1:E2)') - - engine.addRows(0, [1, 2]) - - verifyRangesInSheet(engine, 0, ['E1:E1', 'E1:E2', 'E1:E3', 'E1:E4']) - - engine.setCellContents({sheet: 0, col: 0, row: 5}, '=SUM(E4:E4)') - - verifyRangesInSheet(engine, 0, ['E1:E1', 'E1:E2', 'E1:E3', 'E1:E4', 'E4:E4']) - - engine.removeRows(0, [1, 3]) - - verifyRangesInSheet(engine, 0, ['E1:E1']) - verifyValues(engine) - }) - - it('should undo merge ranges', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents({sheet: 0, col: 2, row: 1}, 7) - engine.setCellContents({sheet: 0, col: 3, row: 0}, '=SUM(B2:C3)') - engine.setCellContents({sheet: 0, col: 6, row: 0}, '=SUM(D1:F2)') - engine.setCellContents({sheet: 0, col: 6, row: 2}, '=SUM(D1:F1)') - - engine.addRows(0, [0, 2]) - engine.removeRows(0, [3, 1]) - - verifyRangesInSheet(engine, 0, ['D3:F3', 'B4:C4']) - verifyValues(engine) - - engine.undo() - - verifyRangesInSheet(engine, 0, ['D3:F3', 'D3:F4', 'B4:C5']) - verifyValues(engine) - }) -}) diff --git a/test/unit/cruds/removing-sheet.spec.ts b/test/unit/cruds/removing-sheet.spec.ts deleted file mode 100644 index 08c39c4ee2..0000000000 --- a/test/unit/cruds/removing-sheet.spec.ts +++ /dev/null @@ -1,860 +0,0 @@ -import {ExportedCellChange, HyperFormula, NoSheetWithIdError, CellValueType} from '../../../src' -import {AbsoluteCellRange} from '../../../src/AbsoluteCellRange' -import {ErrorType} from '../../../src/Cell' -import {ArrayFormulaVertex} from '../../../src/DependencyGraph' -import { ErrorMessage } from '../../../src/error-message' -import {ColumnIndex} from '../../../src/Lookup/ColumnIndex' -import {CellAddress} from '../../../src/parser' -import { - adr, - detailedError, - detailedErrorWithOrigin, - expectArrayWithSameContent, - extractReference, -} from '../testUtils' - -describe('Removing sheet - checking if its possible', () => { - it('no if theres no such sheet', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToRemoveSheet(1)).toBe(false) - }) - - it('yes otherwise', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToRemoveSheet(0)).toBe(true) - }) -}) - -describe('remove sheet', () => { - it('should throw error when trying to remove not existing sheet', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(() => { - engine.removeSheet(1) - }).toThrow(new NoSheetWithIdError(1)) - }) - - it('should remove sheet by id', () => { - const engine = HyperFormula.buildFromArray([['foo']]) - - engine.removeSheet(0) - - expect(engine.sheetMapping.numberOfSheets()).toBe(0) - expect(Array.from(engine.addressMapping.entries())).toEqual([]) - }) - - it('should remove empty sheet', () => { - const engine = HyperFormula.buildFromArray([]) - - engine.removeSheet(0) - - expect(engine.sheetMapping.numberOfSheets()).toBe(0) - expect(Array.from(engine.addressMapping.entries())).toEqual([]) - }) - - it('should remove sheet with matrix', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1'], - ['{=TRANSPOSE(A1:A1)}'], - ], - }) - - engine.removeSheet(0) - - expect(engine.sheetMapping.numberOfSheets()).toBe(0) - expect(Array.from(engine.addressMapping.entries())).toEqual([]) - }) - - it('should remove sheet with formula matrix', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '2'], - ['{=TRANSPOSE(A1:B1)}'], - ['{=TRANSPOSE(A1:B1)}'], - ], - }) - - engine.removeSheet(0) - - expect(engine.sheetMapping.numberOfSheets()).toBe(0) - expect(Array.from(engine.addressMapping.entries())).toEqual([]) - }) - - it('should remove a sheet with a cell reference to a value in the same sheet', () => { - const engine = HyperFormula.buildFromArray([ - [1, '=A1'], - ]) - - engine.removeSheet(0) - - expect(engine.sheetMapping.numberOfSheets()).toBe(0) - expect(Array.from(engine.addressMapping.entries())).toEqual([]) - }) - - it('should not affect data in the sheets that were referenced by the sheet being removed', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=Sheet2!A1)'], - ['=SUM(Sheet2!A1:A2)'], - ], - Sheet2: [ - [1], - [2], - [3], - ], - }) - - engine.removeSheet(0) - - expect(Array.from(engine.sheetMapping.iterateSheetNames())).toEqual(['Sheet2']) - expect(engine.getCellValue(adr('A1', 1))).toBe(1) - expect(engine.getCellValue(adr('A2', 1))).toBe(2) - expect(engine.getCellValue(adr('A3', 1))).toBe(3) - }) - - it('converts sheet to placeholder if other sheet depends on it', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [[42]], - Sheet2: [['=Sheet1!A1']], - }) - - const sheet1Id = engine.getSheetId('Sheet1')! - - engine.removeSheet(sheet1Id) - - expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: false })).toBe(false) - expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: true })).toBe(true) - }) - - it('removes sheet completely if nothing depends on it', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [[42]], - Sheet2: [[100]], - }) - - const sheet1Id = engine.getSheetId('Sheet1')! - - engine.removeSheet(sheet1Id) - - expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: false })).toBe(false) - expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: true })).toBe(false) - }) - - it('removes the placeholder sheet if nothing depends on it any longer', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [[42]], - Sheet2: [['=Sheet1!A1']], - }) - - const sheet1Id = engine.getSheetId('Sheet1')! - const sheet2Id = engine.getSheetId('Sheet2')! - - engine.removeSheet(sheet1Id) - - expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: false })).toBe(false) - expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: true })).toBe(true) - - engine.setCellContents(adr('A1', sheet2Id), 100) - - expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: false })).toBe(false) - expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: true })).toBe(false) - }) - - it('decreases lastSheetId if removed sheet was the last one', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [[1]], - Sheet2: [[2]], - }) - - const sheet2Id = engine.getSheetId('Sheet2')! - - engine.removeSheet(sheet2Id) - - engine.addSheet('Sheet3') - const sheet3Id = engine.getSheetId('Sheet3')! - - expect(sheet3Id).toBe(sheet2Id) // new sheet reuses the ID - }) -}) - -describe('remove sheet - adjust edges', () => { - it('should not affect dependencies to sheet other than removed', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '=A1'], - ], - Sheet2: [ - ['1'], - ], - }) - - engine.removeSheet(1) - - const a1 = engine.addressMapping.getCell(adr('A1')) - const b1 = engine.addressMapping.getCell(adr('B1')) - - expect(engine.graph.existsEdge(a1!, b1!)).toBe(true) - }) - - it('should remove edge between sheets', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=Sheet2!A1'], - ], - Sheet2: [ - ['1'], - ], - }) - - const a1From0 = engine.addressMapping.getCell(adr('A1')) - const a1From1 = engine.addressMapping.getCell(adr('A1', 1)) - - expect(engine.graph.existsEdge(a1From1!, a1From0!)).toBe(true) - - engine.removeSheet(1) - - expect(engine.graph.existsEdge(a1From1!, a1From0!)).toBe(false) - }) -}) - -describe('remove sheet - adjust formula dependencies', () => { - it('should not affect formula with dependency to sheet other than removed', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '=A1'], - ], - Sheet2: [ - ['1'], - ], - }) - - engine.removeSheet(1) - - const reference = extractReference(engine, adr('B1')) - - expect(reference).toEqual(CellAddress.relative(-1, 0)) - expect(engine.getAllSheetsSerialized()).toEqual({Sheet1: [['1', '=A1']]}) - expect(engine.getAllSheetsValues()).toEqual({Sheet1: [[1, 1]]}) - }) - - it('should be #REF after removing sheet', () => { - const sheet1Name = 'Sheet1' - const sheet2Name = 'Sheet2' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [ - ['=Sheet2!A1'], - ['=Sheet2!A1:A2'], - ['=Sheet2!A:B'], - ['=Sheet2!1:2'], - ], - [sheet2Name]: [ - ['1'], - ], - }) - - const sheet1Id = engine.getSheetId(sheet1Name)! - const sheet2Id = engine.getSheetId(sheet2Name)! - - engine.removeSheet(sheet2Id) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A2', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A3', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A4', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return changed values', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=Sheet2!A1'], - ], - Sheet2: [ - ['1'], - ], - }) - - const changes = engine.removeSheet(1) - - expect(changes.length).toBe(1) - expect(changes).toContainEqual(new ExportedCellChange(adr('A1'), detailedErrorWithOrigin(ErrorType.REF, 'Sheet1!A1', ErrorMessage.SheetRef))) - }) - -}) - -describe('removeSheet() recalculates formulas (issue #1116)', () => { - it('returns REF error if other sheet depends on the removed one', () => { - const table1Name = 'table1' - const table2Name = 'table2' - const engine = HyperFormula.buildFromSheets({ - [table1Name]: [[`='${table2Name}'!A1`]], - [table2Name]: [[10]], - }) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(table1Name)))).toBe(10) - expect(engine.getCellValue(adr('A1', engine.getSheetId(table2Name)))).toBe(10) - - engine.removeSheet(engine.getSheetId(table2Name)!) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(table1Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A1', engine.getSheetId(table2Name)))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('returns REF error for chained dependencies across multiple sheets', () => { - const sheet1Name = 'Sheet1' - const sheet2Name = 'Sheet2' - const sheet3Name = 'Sheet3' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [[`='${sheet2Name}'!A1+2`]], - [sheet2Name]: [[`='${sheet3Name}'!A1*2`]], - [sheet3Name]: [[42]], - }) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet2Name)))).toBe(84) - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toBe(86) - - engine.removeSheet(engine.getSheetId(sheet3Name)!) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet2Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('returns REF error for nested dependencies within same sheet referencing removed sheet', () => { - const sheet1Name = 'Sheet1' - const removedSheetName = 'RemovedSheet' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [['=B1*2', `='${removedSheetName}'!A1`]], - [removedSheetName]: [[15]], - }) - - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet1Name)))).toBe(15) - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toBe(30) - - engine.removeSheet(engine.getSheetId(removedSheetName)!) - - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('returns REF error for multiple cells from different sheets referencing removed sheet', () => { - const sheet1Name = 'Sheet1' - const sheet2Name = 'Sheet2' - const targetSheetName = 'TargetSheet' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [[`='${targetSheetName}'!A1`, `='${targetSheetName}'!B1`]], - [sheet2Name]: [[`='${targetSheetName}'!A1+10`, `='${targetSheetName}'!B1+20`]], - [targetSheetName]: [[5, 7]], - }) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toBe(5) - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet1Name)))).toBe(7) - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet2Name)))).toBe(15) - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet2Name)))).toBe(27) - - engine.removeSheet(engine.getSheetId(targetSheetName)!) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet2Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet2Name)))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('returns REF error for formulas with mixed operations combining removed sheet references', () => { - const sheet1Name = 'Sheet1' - const removedSheetName = 'RemovedSheet' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [[100, `='${removedSheetName}'!A1 + A1`, `='${removedSheetName}'!B1 * 2`]], - [removedSheetName]: [[50, 25]], - }) - - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet1Name)))).toBe(150) - expect(engine.getCellValue(adr('C1', engine.getSheetId(sheet1Name)))).toBe(50) - - engine.removeSheet(engine.getSheetId(removedSheetName)!) - - expect(engine.getCellValue(adr('B1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('C1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('returns REF error for formulas with multi-cell ranges from removed sheet', () => { - const sheet1Name = 'Sheet1' - const dataSheetName = 'DataSheet' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [ - [`=SUM('${dataSheetName}'!A1:B5)`], - [`=MEDIAN('${dataSheetName}'!A1:B5)`], - ], - [dataSheetName]: [ - [1, 2], - [3, 4], - [5, 6], - [7, 8], - [9, 10], - ], - }) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toBe(55) - expect(engine.getCellValue(adr('A2', engine.getSheetId(sheet1Name)))).toBe(5.5) - - engine.removeSheet(engine.getSheetId(dataSheetName)!) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A2', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('returns REF error for named expressions referencing removed sheet', () => { - const sheet1Name = 'Sheet1' - const removedSheetName = 'RemovedSheet' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [['=MyValue'], ['=MyValue*2']], - [removedSheetName]: [[99]] - }, {}, [ - { name: 'MyValue', expression: `='${removedSheetName}'!$A$1` } - ]) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toBe(99) - expect(engine.getCellValue(adr('A2', engine.getSheetId(sheet1Name)))).toBe(198) - - engine.removeSheet(engine.getSheetId(removedSheetName)!) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A2', engine.getSheetId(sheet1Name)))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('handles add-remove-add cycle correctly', () => { - const engine = HyperFormula.buildEmpty() - const sheet1Name = 'Sheet1' - const sheet2Name = 'Sheet2' - - engine.addSheet(sheet1Name) - const sheet1Id = engine.getSheetId(sheet1Name)! - engine.setCellContents(adr('A1', sheet1Id), `='${sheet2Name}'!A1`) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet(sheet2Name) - const oldSheet2Id = engine.getSheetId(sheet2Name)! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBeNull() - - engine.setCellContents(adr('A1', oldSheet2Id), 42) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - expect(engine.getCellValue(adr('A1', oldSheet2Id))).toBe(42) - - engine.removeSheet(oldSheet2Id) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A1', oldSheet2Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet(sheet2Name) - let newSheet2Id = engine.getSheetId(sheet2Name)! - engine.setCellContents(adr('A1', newSheet2Id), 43) - - expect(newSheet2Id).toBe(oldSheet2Id) - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(43) - expect(engine.getCellValue(adr('A1', newSheet2Id))).toBe(43) - - engine.removeSheet(oldSheet2Id) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A1', oldSheet2Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet(sheet2Name) - newSheet2Id = engine.getSheetId(sheet2Name)! - engine.setCellContents(adr('A1', newSheet2Id), 44) - - expect(newSheet2Id).toBe(oldSheet2Id) - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(44) - expect(engine.getCellValue(adr('A1', newSheet2Id))).toBe(44) - - engine.removeSheet(oldSheet2Id) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A1', oldSheet2Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet(sheet2Name) - newSheet2Id = engine.getSheetId(sheet2Name)! - engine.setCellContents(adr('A1', newSheet2Id), 45) - - expect(newSheet2Id).toBe(oldSheet2Id) - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(45) - expect(engine.getCellValue(adr('A1', newSheet2Id))).toBe(45) - }) - - it('REF error propagates through dependency chain when source sheet is removed', () => { - const engine = HyperFormula.buildFromSheets({ - 'Main': [['=Intermediate!A1*2']], - 'Intermediate': [['=Source!A1+10']], - 'Source': [[5]], - }) - const mainId = engine.getSheetId('Main')! - const intermediateId = engine.getSheetId('Intermediate')! - const sourceId = engine.getSheetId('Source')! - - expect(engine.getCellValue(adr('A1', mainId))).toBe(30) - expect(engine.getCellValue(adr('A1', intermediateId))).toBe(15) - - // Remove source sheet - error should propagate through chain - engine.removeSheet(sourceId) - - expect(engine.getCellValue(adr('A1', intermediateId))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A1', mainId))).toEqualError(detailedError(ErrorType.REF)) - - // Re-add the sheet to resolve the errors - engine.addSheet('Source') - engine.setCellContents(adr('A1', engine.getSheetId('Source')), 5) - - expect(engine.getCellValue(adr('A1', intermediateId))).toBe(15) - expect(engine.getCellValue(adr('A1', mainId))).toBe(30) - }) - - it('removing sheet creates REF and adding it back resolves it', () => { - const engine = HyperFormula.buildFromSheets({ - 'Main': [['=Data!A1']], - 'Data': [[42]], - }) - const mainId = engine.getSheetId('Main')! - const dataId = engine.getSheetId('Data')! - - expect(engine.getCellValue(adr('A1', mainId))).toBe(42) - - engine.removeSheet(dataId) - - expect(engine.getCellValue(adr('A1', mainId))).toEqualError(detailedError(ErrorType.REF)) - - engine.addSheet('Data') - engine.setCellContents(adr('A1', engine.getSheetId('Data')), 99) - - expect(engine.getCellValue(adr('A1', mainId))).toBe(99) - }) - - describe('when using ranges with', () => { - it('function using `runFunction`', () => { - const sheet1Name = 'FirstSheet' - const sheet2Name = 'NewSheet' - const sheet1Data = [['=MEDIAN(NewSheet!A1:A1)', '=MEDIAN(NewSheet!A1:A2)', '=MEDIAN(NewSheet!A1:A3)', '=MEDIAN(NewSheet!A1:A4)']] - const sheet2Data = [[1], [2], [3], [4]] - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: sheet1Data, - [sheet2Name]: sheet2Data, - }) - - const sheet1Id = engine.getSheetId(sheet1Name)! - const sheet2Id = engine.getSheetId(sheet2Name)! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(1.5) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(2) - expect(engine.getCellValue(adr('D1', sheet1Id))).toBe(2.5) - - engine.removeSheet(sheet2Id) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('C1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('D1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('function not using `runFunction`', () => { - const sheet1Name = 'FirstSheet' - const sheet2Name = 'NewSheet' - const sheet1Data = [['=SUM(NewSheet!A1:A1)', '=SUM(NewSheet!A1:A2)', '=SUM(NewSheet!A1:A3)', '=SUM(NewSheet!A1:A4)']] - const sheet2Data = [[1], [2], [3], [4]] - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: sheet1Data, - [sheet2Name]: sheet2Data, - }) - - const sheet1Id = engine.getSheetId(sheet1Name)! - const sheet2Id = engine.getSheetId(sheet2Name)! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(3) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(6) - expect(engine.getCellValue(adr('D1', sheet1Id))).toBe(10) - - engine.removeSheet(sheet2Id) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('C1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('D1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('function using `runFunction` referencing range indirectly', () => { - const sheet1Name = 'FirstSheet' - const sheet2Name = 'NewSheet' - const sheet1Data = [ - ['=MEDIAN(A2)', '=MEDIAN(B2)', '=MEDIAN(C2)', '=MEDIAN(D2)'], - [`='${sheet2Name}'!A1:A1`, `='${sheet2Name}'!A1:B2`, `='${sheet2Name}'!A1:A3`, `='${sheet2Name}'!A1:A4`], - ] - const sheet2Data = [[1], [2], [3], [4]] - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: sheet1Data, - [sheet2Name]: sheet2Data, - }) - - const sheet1Id = engine.getSheetId(sheet1Name)! - const sheet2Id = engine.getSheetId(sheet2Name)! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(1.5) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(2) - expect(engine.getCellValue(adr('D1', sheet1Id))).toBe(2.5) - - engine.removeSheet(sheet2Id) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('C1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('D1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('function not using `runFunction` referencing range indirectly', () => { - const sheet1Name = 'FirstSheet' - const sheet2Name = 'NewSheet' - const sheet1Data = [ - ['=SUM(A2)', '=SUM(B2)', '=SUM(C2)', '=SUM(D2)'], - [`='${sheet2Name}'!A1:A1`, `='${sheet2Name}'!A1:B2`, `='${sheet2Name}'!A1:A3`, `='${sheet2Name}'!A1:A4`], - ] - const sheet2Data = [[1], [2], [3], [4]] - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: sheet1Data, - [sheet2Name]: sheet2Data, - }) - - const sheet1Id = engine.getSheetId(sheet1Name)! - const sheet2Id = engine.getSheetId(sheet2Name)! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(3) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(6) - expect(engine.getCellValue(adr('D1', sheet1Id))).toBe(10) - - engine.removeSheet(sheet2Id) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('C1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('D1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('function calling a named expression', () => { - const sheet1Name = 'FirstSheet' - const sheet2Name = 'NewSheet' - const sheet1Data = [[`='${sheet2Name}'!A1:A4`]] - const sheet2Data = [[1], [2], [3], [4]] - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: sheet1Data, - [sheet2Name]: sheet2Data, - }, {}, [ - { name: 'ExprA', expression: `=MEDIAN(${sheet2Name}!$A$1:$A$1)` }, - { name: 'ExprB', expression: `=MEDIAN(${sheet2Name}!$A$1:$A$2)` }, - { name: 'ExprC', expression: `=MEDIAN(${sheet2Name}!$A$1:$A$3)` }, - { name: 'ExprD', expression: `=MEDIAN(${sheet1Name}!$A$1)` } - ]) - - const sheet2Id = engine.getSheetId(sheet2Name)! - - expect(engine.getNamedExpressionValue('ExprA')).toBe(1) - expect(engine.getNamedExpressionValue('ExprB')).toBe(1.5) - expect(engine.getNamedExpressionValue('ExprC')).toBe(2) - expect(engine.getNamedExpressionValue('ExprD')).toBe(2.5) - - engine.removeSheet(sheet2Id) - - expect(engine.getNamedExpressionValue('ExprA')).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getNamedExpressionValue('ExprB')).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getNamedExpressionValue('ExprC')).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getNamedExpressionValue('ExprD')).toEqualError(detailedError(ErrorType.REF)) - }) - }) -}) - -describe('remove sheet - adjust address mapping', () => { - it('should remove sheet from address mapping if nothing depends on it', () => { - const sheet1Name = 'Sheet1' - - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [[42]] - }) - - const sheet1Id = engine.getSheetId(sheet1Name)! - engine.removeSheet(sheet1Id) - - expect(() => engine.addressMapping.getStrategyForSheetOrThrow(sheet1Id)).toThrow(new NoSheetWithIdError(sheet1Id)) - }) - - it('should not remove sheet from address mapping if another sheet depends on it', () => { - const sheet1Name = 'Sheet1' - const sheet2Name = 'Sheet2' - - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [[42]], - [sheet2Name]: [[`='${sheet1Name}'!A1`]], - }) - - const sheet1Id = engine.getSheetId(sheet1Name)! - - engine.removeSheet(sheet1Id) - - expect(() => engine.addressMapping.getStrategyForSheetOrThrow(sheet1Id)).not.toThrow() - }) - - it('should not remove sheet from address mapping if a named expression depends on it', () => { - const sheet1Name = 'Sheet1' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [[42]], - }, {}, [ - { name: 'namedExpressionName', expression: `='${sheet1Name}'!$A$1` }, - ]) - - const sheet1Id = engine.getSheetId(sheet1Name)! - - engine.removeSheet(sheet1Id) - - expect(() => engine.addressMapping.getStrategyForSheetOrThrow(sheet1Id)).not.toThrow() - }) - - it('removes the placeholder sheet from address mapping if nothing depends on it any longer', () => { - const sheet1Name = 'Sheet1' - const sheet2Name = 'Sheet2' - - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [[42]], - [sheet2Name]: [[`='${sheet1Name}'!A1`]], - }) - - const sheet1Id = engine.getSheetId(sheet1Name)! - const sheet2Id = engine.getSheetId(sheet2Name)! - - engine.removeSheet(sheet1Id) - - expect(() => engine.addressMapping.getStrategyForSheetOrThrow(sheet1Id)).not.toThrow() - - engine.setCellContents(adr('A1', sheet2Id), 100) - - expect(() => engine.addressMapping.getStrategyForSheetOrThrow(sheet1Id)).toThrow(new NoSheetWithIdError(sheet1Id)) - }) -}) - -describe('remove sheet - adjust range mapping', () => { - it('should remove ranges from range mapping when removing sheet', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=SUM(B1:B2)'], - ['=SUM(C1:C2)'], - ], - Sheet2: [ - ['=SUM(B1:B2)'], - ['=SUM(C1:C2)'], - ], - }) - - expect(Array.from(engine.rangeMapping.rangesInSheet(0)).length).toBe(2) - expect(Array.from(engine.rangeMapping.rangesInSheet(1)).length).toBe(2) - - engine.removeSheet(0) - - expect(Array.from(engine.rangeMapping.rangesInSheet(0)).length).toBe(0) - expect(Array.from(engine.rangeMapping.rangesInSheet(1)).length).toBe(2) - }) -}) - -describe('remove sheet - adjust matrix mapping', () => { - it('should remove matrices from matrix mapping when removing sheet', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - ], - Sheet2: [ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - ], - }) - - expect(engine.arrayMapping.getArray(AbsoluteCellRange.spanFrom(adr('A2'), 1, 2))).toBeInstanceOf(ArrayFormulaVertex) - - engine.removeSheet(0) - - expect(engine.arrayMapping.getArray(AbsoluteCellRange.spanFrom(adr('A2'), 1, 2))).toBeUndefined() - expect(engine.arrayMapping.getArray(AbsoluteCellRange.spanFrom(adr('A2', 1), 1, 2))).toBeInstanceOf(ArrayFormulaVertex) - }) -}) - -describe('remove sheet - adjust column index', () => { - it('should remove sheet from index', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ], {useColumnIndex: true}) - const index = engine.columnSearch as ColumnIndex - const removeSheetSpy = spyOn(index, 'removeSheet') - - engine.removeSheet(0) - - expect(removeSheetSpy).toHaveBeenCalledWith(0) - expectArrayWithSameContent([], index.getValueIndex(0, 0, 1).index) - }) -}) - -describe('remove sheet - placeholder sheet behavior', () => { - it('should return ERROR type when getting cell value from a placeholder sheet', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [[42]], - Sheet2: [['=Sheet1!A1']], - }) - - const sheet1Id = engine.getSheetId('Sheet1')! - - engine.removeSheet(sheet1Id) - - expect(engine.getCellValueType(adr('A1', sheet1Id))).toBe('ERROR') - }) - - it('should return null when getting serialized cell from a placeholder sheet', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [[42]], - Sheet2: [['=Sheet1!A1']], - }) - - const sheet1Id = engine.getSheetId('Sheet1')! - - engine.removeSheet(sheet1Id) - - const result = engine.getCellSerialized(adr('A1', sheet1Id)) - expect(result).toBeNull() - }) - - it('should remove range vertices when clearing formulas that use ranges', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3], - ['=SUM(A1:C1)'], - ]) - - expect(engine.rangeMapping.getNumberOfRangesInSheet(0)).toBe(1) - - engine.setCellContents(adr('A2'), null) - - expect(engine.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('should remove range vertices when removing sheet with ranges', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - [1, 2, 3], - ['=SUM(A1:C1)'], - ], - Sheet2: [[100]], - }) - - expect(engine.rangeMapping.getNumberOfRangesInSheet(0)).toBe(1) - - engine.removeSheet(0) - - expect(engine.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) -}) diff --git a/test/unit/cruds/rename-sheet.spec.ts b/test/unit/cruds/rename-sheet.spec.ts deleted file mode 100644 index 1114f63ff8..0000000000 --- a/test/unit/cruds/rename-sheet.spec.ts +++ /dev/null @@ -1,583 +0,0 @@ -import {HyperFormula, NoSheetWithIdError, SheetNameAlreadyTakenError} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {adr, detailedError} from '../testUtils' - -describe('isItPossibleToRenameSheet() returns', () => { - it('true if possible', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': []}) - - expect(engine.isItPossibleToRenameSheet(0, 'Foo')).toBe(true) - expect(engine.isItPossibleToRenameSheet(0, '~`!@#$%^&*()_-+_=/|?{}[]\"')).toBe(true) - }) - - it('true if same name', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': []}) - - expect(engine.isItPossibleToRenameSheet(0, 'Sheet1')).toBe(true) - }) - - it('false if sheet does not exists', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': []}) - - expect(engine.isItPossibleToRenameSheet(1, 'Foo')).toBe(false) - }) - - it('false if given name is taken', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [], 'Sheet2': []}) - - expect(engine.isItPossibleToRenameSheet(0, 'Sheet2')).toBe(false) - }) -}) - -describe('renameSheet()', () => { - it('renames sheet and updates sheet mapping', () => { - const engine = HyperFormula.buildEmpty() - engine.addSheet('foo') - - engine.renameSheet(0, 'bar') - - expect(engine.getSheetName(0)).toBe('bar') - expect(engine.doesSheetExist('foo')).toBe(false) - expect(engine.doesSheetExist('bar')).toBe(true) - }) - - it('throws error when sheet with given ID does not exist', () => { - const engine = HyperFormula.buildEmpty() - - expect(() => { - engine.renameSheet(0, 'bar') - }).toThrow(new NoSheetWithIdError(0)) - }) - - it('throws error when new sheet name is already taken', () => { - const engine = HyperFormula.buildEmpty() - engine.addSheet() - engine.addSheet('bar') - - expect(() => { - engine.renameSheet(0, 'bar') - }).toThrow(new SheetNameAlreadyTakenError('bar')) - }) - - it('allows renaming to the same name (no-op)', () => { - const engine = HyperFormula.buildEmpty() - engine.addSheet('foo') - - engine.renameSheet(0, 'foo') - - expect(engine.getSheetName(0)).toBe('foo') - expect(engine.doesSheetExist('foo')).toBe(true) - }) - - it('allows changing case of the same canonical name', () => { - const engine = HyperFormula.buildEmpty() - engine.addSheet('Foo') - - engine.renameSheet(0, 'FOO') - - expect(engine.getSheetName(0)).toBe('FOO') - expect(engine.doesSheetExist('FOO')).toBe(true) - }) - - describe('recalculates formulas (issue #1116)', () => { - it('recalculates single cell reference', () => { - const sheet1Name = 'Sheet1' - const oldName = 'OldName' - const newName = 'NewName' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [[`='${newName}'!A1`]], - [oldName]: [[42]], - }) - const sheet1Id = engine.getSheetId(sheet1Name)! - const oldNameId = engine.getSheetId(oldName)! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(oldNameId, newName) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - }) - - it('recalculates nested dependencies within same sheet', () => { - const sheet1Name = 'Sheet1' - const oldName = 'OldName' - const newName = 'NewName' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [['=B1*2', `='${newName}'!A1`]], - [oldName]: [[15]], - }) - const sheet1Id = engine.getSheetId(sheet1Name)! - const oldNameId = engine.getSheetId(oldName)! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(oldNameId, newName) - - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(15) - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(30) - }) - - it('recalculates formulas with mixed operations', () => { - const sheet1Name = 'Sheet1' - const oldName = 'OldName' - const newName = 'NewName' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [[100, `='${newName}'!A1 + A1`, `='${newName}'!B1 * 2`]], - [oldName]: [[50, 25]], - }) - const sheet1Id = engine.getSheetId(sheet1Name)! - const oldNameId = engine.getSheetId(oldName)! - - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('C1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(oldNameId, newName) - - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(150) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(50) - }) - - it('recalculates named expressions', () => { - const sheet1Name = 'Sheet1' - const oldName = 'OldName' - const newName = 'NewName' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [['=MyValue'], ['=MyValue*2']], - [oldName]: [[99]], - }, {}, [ - { name: 'MyValue', expression: `='${newName}'!$A$1` } - ]) - const sheet1Id = engine.getSheetId(sheet1Name)! - const oldNameId = engine.getSheetId(oldName)! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A2', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(oldNameId, newName) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(99) - expect(engine.getCellValue(adr('A2', sheet1Id))).toBe(198) - }) - - it('moves reserved range vertices onto renamed sheet', () => { - const formulaSheet = 'FormulaSheet' - const oldName = 'SourceSheet' - const newName = 'GhostSheet' - const engine = HyperFormula.buildFromSheets({ - [formulaSheet]: [[`=SUM('${newName}'!A1:B1)`]], - [oldName]: [[1, 2]], - }) - const formulaSheetId = engine.getSheetId(formulaSheet)! - const oldNameId = engine.getSheetId(oldName)! - - expect(engine.getCellValue(adr('A1', formulaSheetId))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(oldNameId, newName) - - const renamedSheetId = engine.getSheetId(newName)! - - expect(engine.getCellValue(adr('A1', formulaSheetId))).toBe(3) - - const movedRange = engine.rangeMapping.getRangeVertex(adr('A1', renamedSheetId), adr('B1', renamedSheetId)) - - expect(movedRange).toBeDefined() - expect(movedRange?.sheet).toBe(renamedSheetId) - }) - - it('merges duplicate range vertices after renaming into reserved name', () => { - const oldName = 'OldName' - const newName = 'GhostSheet' - const usesOld = 'UsesOld' - const usesNew = 'UsesNew' - const engine = HyperFormula.buildFromSheets({ - [usesOld]: [[`=SUM('${oldName}'!A1:A2)`]], - [usesNew]: [[`=SUM('${newName}'!A1:A2)`]], - [oldName]: [[5], [7]], - }) - const usesOldId = engine.getSheetId(usesOld)! - const usesNewId = engine.getSheetId(usesNew)! - const oldNameId = engine.getSheetId(oldName)! - - expect(engine.getCellValue(adr('A1', usesOldId))).toBe(12) - expect(engine.getCellValue(adr('A1', usesNewId))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(oldNameId, newName) - - expect(engine.getCellValue(adr('A1', usesOldId))).toBe(12) - expect(engine.getCellValue(adr('A1', usesNewId))).toBe(12) - expect(engine.getCellFormula(adr('A1', usesNewId))).toBe(`=SUM(${newName}!A1:A2)`) - expect(engine.getCellFormula(adr('A1', usesOldId))).toBe(`=SUM(${newName}!A1:A2)`) - - engine.setCellContents(adr('A1', oldNameId), 100) - - expect(engine.getCellValue(adr('A1', usesOldId))).toBe(107) - expect(engine.getCellValue(adr('A1', usesNewId))).toBe(107) - }) - - it('recalculates column and row ranges', () => { - const sheet1Name = 'Sheet1' - const oldName = 'OldName' - const newName = 'NewName' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [ - [`=SUM('${newName}'!A:A)`], - [`=SUM('${newName}'!1:2)`], - ], - [oldName]: [ - [1, 2], - [3, 4], - ], - }) - const sheet1Id = engine.getSheetId(sheet1Name)! - const oldNameId = engine.getSheetId(oldName)! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A2', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(oldNameId, newName) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(4) - expect(engine.getCellValue(adr('A2', sheet1Id))).toBe(10) - }) - - it('keeps existing dependencies and dependents when renaming a sheet', () => { - const mainSheetName = 'Sheet1' - const secondarySheetName = 'Sheet2' - const newNameForSecondarySheet = 'Sheet3' - const engine = HyperFormula.buildFromSheets({ - [mainSheetName]: [['main sheet', `=${secondarySheetName}!A1`, `=${newNameForSecondarySheet}!A1`]], - [secondarySheetName]: [['secondary sheet', `=${mainSheetName}!A1`]], - }) - const mainSheetId = engine.getSheetId(mainSheetName)! - const secondarySheetId = engine.getSheetId(secondarySheetName)! - - expect(engine.getCellValue(adr('B1', mainSheetId))).toBe('secondary sheet') - expect(engine.getCellValue(adr('C1', mainSheetId))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', secondarySheetId))).toBe('main sheet') - - engine.renameSheet(secondarySheetId, newNameForSecondarySheet) - - expect(engine.getSheetId(newNameForSecondarySheet)).toBe(secondarySheetId) - expect(engine.getCellValue(adr('B1', mainSheetId))).toBe('secondary sheet') - expect(engine.getCellValue(adr('C1', mainSheetId))).toBe('secondary sheet') - expect(engine.getCellValue(adr('B1', secondarySheetId))).toBe('main sheet') - }) - - it('removing renamed sheet returns REF error', () => { - const sheet1Name = 'Sheet1' - const oldName = 'OldName' - const newName = 'NewName' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [[`='${newName}'!A1`]], - [oldName]: [[42]], - }) - const sheet1Id = engine.getSheetId(sheet1Name)! - const oldNameId = engine.getSheetId(oldName)! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(oldNameId, newName) - - expect(engine.getSheetId(newName)).toBe(oldNameId) - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - - engine.removeSheet(oldNameId) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('adding sheet with the same name as new name of renamed sheet throws error', () => { - const oldName = 'OldName' - const newName = 'NewName' - const engine = HyperFormula.buildFromSheets({ - [oldName]: [[42]], - }) - const oldNameId = engine.getSheetId(oldName)! - - engine.renameSheet(oldNameId, newName) - - expect(() => { - engine.addSheet(newName) - }).toThrow(new SheetNameAlreadyTakenError(newName)) - }) - - it('adding sheet with the old name of renamed sheet creates new sheet', () => { - const sheet1Name = 'Sheet1' - const oldName = 'OldName' - const newName = 'NewName' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [[`='${oldName}'!A1`]], - [oldName]: [[42]], - }) - const sheet1Id = engine.getSheetId(sheet1Name)! - const oldNameId = engine.getSheetId(oldName)! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - - engine.renameSheet(oldNameId, newName) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - - engine.addSheet(oldName) - engine.setCellContents(adr('A1', engine.getSheetId(oldName)), 100) - - expect(engine.getSheetId(oldName)).not.toBe(engine.getSheetId(newName)) - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - expect(engine.getCellValue(adr('A1', engine.getSheetId(newName)))).toBe(42) - expect(engine.getCellValue(adr('A1', engine.getSheetId(oldName)))).toBe(100) - }) - - - it('renaming sheet that references non-existent sheet keeps REF error', () => { - const sheet1Name = 'Sheet1' - const newName = 'NewName' - const nonExistent = 'NonExistent' - const engine = HyperFormula.buildFromSheets({ - [sheet1Name]: [[`='${nonExistent}'!A1`]], - }) - const sheet1Id = engine.getSheetId(sheet1Name)! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(sheet1Id, newName) - - expect(engine.getCellValue(adr('A1', engine.getSheetId(newName)))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('chain renaming resolves multiple placeholder references sequentially', () => { - const engine = HyperFormula.buildFromSheets({ - 'Main': [['=SheetB!A1', '=SheetC!A1']], - 'SheetA': [[10]], - }) - const mainId = engine.getSheetId('Main')! - const sheetAId = engine.getSheetId('SheetA')! - - // Both are initially REF errors (placeholders) - expect(engine.getCellValue(adr('A1', mainId))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', mainId))).toEqualError(detailedError(ErrorType.REF)) - - // Rename SheetA → SheetB: resolves first placeholder - engine.renameSheet(sheetAId, 'SheetB') - - expect(engine.getCellValue(adr('A1', mainId))).toBe(10) - expect(engine.getCellValue(adr('B1', mainId))).toEqualError(detailedError(ErrorType.REF)) - // Formula A1 now references SheetB (follows the rename) - expect(engine.getCellFormula(adr('A1', mainId))).toBe('=SheetB!A1') - - // Add a new sheet named SheetC to resolve second placeholder - engine.addSheet('SheetC') - engine.setCellContents(adr('A1', engine.getSheetId('SheetC')), 20) - - expect(engine.getCellValue(adr('A1', mainId))).toBe(10) - expect(engine.getCellValue(adr('B1', mainId))).toBe(20) - }) - - it('renaming sheet with circular formula does not break engine', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=Sheet2!A1']], - 'Sheet2': [['=Sheet1!A1']], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const sheet2Id = engine.getSheetId('Sheet2')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('A1', sheet2Id))).toEqualError(detailedError(ErrorType.CYCLE)) - - engine.renameSheet(sheet1Id, 'RenamedSheet1') - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('A1', sheet2Id))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellFormula(adr('A1', sheet2Id))).toBe('=RenamedSheet1!A1') - }) - - it('multiple formulas referencing same placeholder all resolve after rename', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=Ghost!A1'], ['=Ghost!A1+1'], ['=SUM(Ghost!A1:A2)']], - 'Sheet2': [['=Ghost!A1*2'], ['=Ghost!B1']], - 'RealSheet': [[100, 200], [300]], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const sheet2Id = engine.getSheetId('Sheet2')! - const realSheetId = engine.getSheetId('RealSheet')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A2', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A3', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A1', sheet2Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A2', sheet2Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(realSheetId, 'Ghost') - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(100) - expect(engine.getCellValue(adr('A2', sheet1Id))).toBe(101) - expect(engine.getCellValue(adr('A3', sheet1Id))).toBe(400) - expect(engine.getCellValue(adr('A1', sheet2Id))).toBe(200) - expect(engine.getCellValue(adr('A2', sheet2Id))).toBe(200) - }) - - it('when new name is already referenced, engine merges both sheet names', () => { - const engine = HyperFormula.buildFromSheets({ - 'Main': [['=Source!A1', '=Target!A1']], - 'Source': [[42]], - }) - const mainId = engine.getSheetId('Main')! - const sourceId = engine.getSheetId('Source')! - - expect(engine.getCellValue(adr('A1', mainId))).toBe(42) - expect(engine.getCellValue(adr('B1', mainId))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(sourceId, 'Target') - - expect(engine.getCellFormula(adr('A1', mainId))).toBe('=Target!A1') - expect(engine.getCellFormula(adr('B1', mainId))).toBe('=Target!A1') - expect(engine.getCellValue(adr('A1', mainId))).toBe(42) - expect(engine.getCellValue(adr('B1', mainId))).toBe(42) - - engine.setCellContents(adr('A1', sourceId), 100) - - expect(engine.getCellValue(adr('A1', mainId))).toBe(100) - expect(engine.getCellValue(adr('B1', mainId))).toBe(100) - }) - - it('handles deeply nested REF error propagation', () => { - const engine = HyperFormula.buildFromSheets({ - 'L1': [['=L2!A1+1']], - 'L2': [['=L3!A1+1']], - 'L3': [['=L4!A1+1']], - 'L4': [['=Ghost!A1']], - 'Source': [[100]], - }) - const l1Id = engine.getSheetId('L1')! - const l2Id = engine.getSheetId('L2')! - const l3Id = engine.getSheetId('L3')! - const l4Id = engine.getSheetId('L4')! - const sourceId = engine.getSheetId('Source')! - - expect(engine.getCellValue(adr('A1', l1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A1', l2Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A1', l3Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('A1', l4Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(sourceId, 'Ghost') - - expect(engine.getCellValue(adr('A1', l4Id))).toBe(100) - expect(engine.getCellValue(adr('A1', l3Id))).toBe(101) - expect(engine.getCellValue(adr('A1', l2Id))).toBe(102) - expect(engine.getCellValue(adr('A1', l1Id))).toBe(103) - }) - - describe('when using ranges with', () => { - it('function using `runFunction`', () => { - const engine = HyperFormula.buildFromSheets({ - 'FirstSheet': [['=MEDIAN(NewName!A1:A1)', '=MEDIAN(NewName!A1:A2)', '=MEDIAN(NewName!A1:A3)', '=MEDIAN(NewName!A1:A4)']], - 'OldName': [[1], [2], [3], [4]], - }) - const sheet1Id = engine.getSheetId('FirstSheet')! - const sheet2Id = engine.getSheetId('OldName')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('C1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('D1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(sheet2Id, 'NewName') - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(1.5) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(2) - expect(engine.getCellValue(adr('D1', sheet1Id))).toBe(2.5) - }) - - it('function not using `runFunction`', () => { - const engine = HyperFormula.buildFromSheets({ - 'FirstSheet': [['=SUM(NewName!A1:A1)', '=SUM(NewName!A1:A2)', '=SUM(NewName!A1:A3)', '=SUM(NewName!A1:A4)']], - 'OldName': [[1], [2], [3], [4]], - }) - const sheet1Id = engine.getSheetId('FirstSheet')! - const sheet2Id = engine.getSheetId('OldName')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('C1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('D1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(sheet2Id, 'NewName') - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(3) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(6) - expect(engine.getCellValue(adr('D1', sheet1Id))).toBe(10) - }) - - it('function using `runFunction` referencing range indirectly', () => { - const engine = HyperFormula.buildFromSheets({ - 'FirstSheet': [ - ['=MEDIAN(A2)', '=MEDIAN(B2)', '=MEDIAN(C2)', '=MEDIAN(D2)'], - ['=\'NewName\'!A1:A1', '=\'NewName\'!A1:B2', '=\'NewName\'!A1:A3', '=\'NewName\'!A1:A4'], - ], - 'OldName': [[1], [2], [3], [4]], - }) - const sheet1Id = engine.getSheetId('FirstSheet')! - const sheet2Id = engine.getSheetId('OldName')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('C1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('D1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(sheet2Id, 'NewName') - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(1.5) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(2) - expect(engine.getCellValue(adr('D1', sheet1Id))).toBe(2.5) - }) - - it('function not using `runFunction` referencing range indirectly', () => { - const engine = HyperFormula.buildFromSheets({ - 'FirstSheet': [ - ['=SUM(A2)', '=SUM(B2)', '=SUM(C2)', '=SUM(D2)'], - ['=\'NewName\'!A1:A1', '=\'NewName\'!A1:B2', '=\'NewName\'!A1:A3', '=\'NewName\'!A1:A4'], - ], - 'OldName': [[1], [2], [3], [4]], - }) - const sheet1Id = engine.getSheetId('FirstSheet')! - const sheet2Id = engine.getSheetId('OldName')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('C1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getCellValue(adr('D1', sheet1Id))).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(sheet2Id, 'NewName') - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(3) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(6) - expect(engine.getCellValue(adr('D1', sheet1Id))).toBe(10) - }) - - it('function calling a named expression', () => { - const engine = HyperFormula.buildFromSheets({ - 'FirstSheet': [['=\'OldName\'!A1:A4']], - 'OldName': [[1], [2], [3], [4]], - }, {}, [ - { name: 'ExprA', expression: '=MEDIAN(NewName!$A$1:$A$1)' }, - { name: 'ExprB', expression: '=MEDIAN(NewName!$A$1:$A$2)' }, - { name: 'ExprC', expression: '=MEDIAN(NewName!$A$1:$A$3)' }, - ]) - - expect(engine.getNamedExpressionValue('ExprA')).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getNamedExpressionValue('ExprB')).toEqualError(detailedError(ErrorType.REF)) - expect(engine.getNamedExpressionValue('ExprC')).toEqualError(detailedError(ErrorType.REF)) - - engine.renameSheet(engine.getSheetId('OldName')!, 'NewName') - - expect(engine.getNamedExpressionValue('ExprA')).toBe(1) - expect(engine.getNamedExpressionValue('ExprB')).toBe(1.5) - expect(engine.getNamedExpressionValue('ExprC')).toBe(2) - }) - }) - }) -}) diff --git a/test/unit/cruds/replace-sheet-content.spec.ts b/test/unit/cruds/replace-sheet-content.spec.ts deleted file mode 100644 index f969b760ec..0000000000 --- a/test/unit/cruds/replace-sheet-content.spec.ts +++ /dev/null @@ -1,118 +0,0 @@ -import {ExportedCellChange, HyperFormula} from '../../../src' -import {adr, expectArrayWithSameContent} from '../testUtils' - -describe('Replace sheet content - checking if its possible', () => { - it('no if theres no such sheet', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToReplaceSheetContent(1, [])).toEqual(false) - }) - - it('yes otherwise', () => { - const engine = HyperFormula.buildFromArray([[]]) - - expect(engine.isItPossibleToReplaceSheetContent(0, [])).toEqual(true) - }) -}) - -describe('Replace sheet content', () => { - it('should throw error trying to replace not existing sheet', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', 'foo'], - ]) - - expect(() => { - engine.setSheetContent(1, [['3', '4']]) - - }).toThrowError("There's no sheet with id = 1") - }) - - it('should replace sheet content with new values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', 'foo'], - ]) - - engine.setSheetContent(0, [['3', '4']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - expect(engine.getCellValue(adr('B1'))).toEqual(4) - expect(engine.getCellValue(adr('A2'))).toBe(null) - expect(engine.getCellValue(adr('B2'))).toBe(null) - }) - - /* for now return only new values */ - it('should return changes', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', 'foo'], - ]) - - const changes = engine.setSheetContent(0, [['3', '4']]) - - expectArrayWithSameContent(changes, [ - new ExportedCellChange(adr('A1'), 3), - new ExportedCellChange(adr('B1'), 4), - ]) - }) - - /* should we return removed values? */ - xit('should return new values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', 'foo'], - ]) - - const changes = engine.setSheetContent(0, [['3', '4']]) - - expect(changes.length).toEqual(4) - - expectArrayWithSameContent(changes, [ - new ExportedCellChange(adr('A1'), 3), - new ExportedCellChange(adr('B1'), 4), - new ExportedCellChange(adr('A2'), null), - new ExportedCellChange(adr('B2'), null), - ]) - }) - - it('should replace content of a sheet with formula matrix', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '2'], - ['{=TRANSPOSE(A1:B1)}'], - ], - Sheet2: [ - ['=Sheet1!A2'], - ['=Sheet1!A3'], - ], - }) - - engine.setSheetContent(0, [ - ['3', '4'], - ['foo', '5'], - ]) - - expect(engine.getCellValue(adr('A1', 1))).toEqual('foo') - expect(engine.getCellValue(adr('A2', 1))).toBe(null) - }) - - it('should replace content of a sheet with formula matrix and recalculate range formula', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['1', '2'], - ['{=TRANSPOSE(A1:B1)}'], - ], - Sheet2: [ - ['=SUM(Sheet1!A1:A2)'], - ], - }) - - engine.setSheetContent(0, [ - ['3', '4'], - [null, '5'], - ]) - - expect(engine.getCellValue(adr('A1', 1))).toEqual(3) - }) -}) diff --git a/test/unit/cruds/set-column-order.spec.ts b/test/unit/cruds/set-column-order.spec.ts deleted file mode 100644 index 1050ef7213..0000000000 --- a/test/unit/cruds/set-column-order.spec.ts +++ /dev/null @@ -1,363 +0,0 @@ -import {HyperFormula, AlwaysSparse} from '../../../src' -import {adr} from '../testUtils' - -describe('swapping columns - checking if it is possible', () => { - it('should validate numbers for negative columns', () => { - const engine = HyperFormula.buildFromArray([[]]) - expect(engine.isItPossibleToSwapColumnIndexes(0, [[-1, 0]])).toEqual(false) - expect(() => - engine.swapColumnIndexes(0, [[-1, 0]]) - ).toThrowError('Invalid arguments, expected column numbers to be nonnegative integers and less than sheet width.') - }) - - it('should validate sources for non-integer values', () => { - const engine = HyperFormula.buildFromArray([[]]) - expect(engine.isItPossibleToSwapColumnIndexes(0, [[1, 1], [0.5, 0]])).toEqual(false) - expect(() => - engine.swapColumnIndexes(0, [[1, 1], [0.5, 0]]) - ).toThrowError('Invalid arguments, expected column numbers to be nonnegative integers and less than sheet width.') - }) - - it('should validate sources for values exceeding sheet width', () => { - const engine = HyperFormula.buildFromArray([[0, 0, 0]]) - expect(engine.isItPossibleToSwapColumnIndexes(0, [[1, 1], [3, 0]])).toEqual(false) - expect(() => - engine.swapColumnIndexes(0, [[3, 0]]) - ).toThrowError('Invalid arguments, expected column numbers to be nonnegative integers and less than sheet width.') - }) - - it('should validate sources to be unique', () => { - const engine = HyperFormula.buildFromArray([[0, 0, 0]]) - expect(engine.isItPossibleToSwapColumnIndexes(0, [[0, 0], [1, 1], [1, 2]])).toEqual(false) - expect(() => - engine.swapColumnIndexes(0, [[0, 0], [1, 1], [1, 2]]) - ).toThrowError('Invalid arguments, expected source column numbers to be unique.') - }) - - it('should validate sources to be permutation of targets', () => { - const engine = HyperFormula.buildFromArray([[0, 0, 0]]) - expect(engine.isItPossibleToSwapColumnIndexes(0, [[0, 0], [1, 1], [2, 1]])).toEqual(false) - expect(() => - engine.swapColumnIndexes(0, [[0, 0], [1, 1], [2, 1]]) - ).toThrowError('Invalid arguments, expected target column numbers to be permutation of source column numbers.') - }) - - it('should check for matrices', () => { - const engine = HyperFormula.buildFromArray([[0, 0, '=TRANSPOSE(A1:B1)']]) - expect(engine.isItPossibleToSwapColumnIndexes(0, [[0, 2], [1, 1], [2, 0]])).toEqual(false) - expect(() => - engine.swapColumnIndexes(0, [[0, 2], [1, 1], [2, 0]]) - ).toThrowError('Cannot perform this operation, source location has an array inside.') - }) - - it('should check for matrices only in moved columns', () => { - const engine = HyperFormula.buildFromArray([[0, 0, '=TRANSPOSE(A1:B1)']]) - expect(engine.isItPossibleToSwapColumnIndexes(0, [[0, 1], [1, 0], [2, 2]])).toEqual(true) - expect(() => - engine.swapColumnIndexes(0, [[0, 1], [1, 0], [2, 2]]) - ).not.toThrowError() - }) -}) - -describe('swapping columns should correctly work', () => { - it('should work on static engine', () => { - const engine = HyperFormula.buildFromArray([[1, 'abcd'], [3, 3], [5, true]]) - expect(engine.isItPossibleToSwapColumnIndexes(0, [[0, 1], [1, 0]])).toEqual(true) - engine.swapColumnIndexes(0, [[0, 1], [1, 0]]) - expect(engine.getSheetSerialized(0)).toEqual([['abcd', 1], [3, 3], [true, 5]]) - }) - - it('should return number of changed cells', () => { - const engine = HyperFormula.buildFromArray([[1, 2], [3, 4], [5, 6]]) - expect(engine.isItPossibleToSwapColumnIndexes(0, [[0, 1], [1, 0]])).toEqual(true) - const ret = engine.swapColumnIndexes(0, [[0, 1], [1, 0]]) - expect(ret.length).toEqual(6) - }) - - it('should work on static engine with uneven column', () => { - const engine = HyperFormula.buildFromArray([[1, 2], [3, 4], [5]], {chooseAddressMappingPolicy: new AlwaysSparse()}) - expect(engine.isItPossibleToSwapColumnIndexes(0, [[0, 1], [1, 0]])).toEqual(true) - engine.swapColumnIndexes(0, [[0, 1], [1, 0]]) - expect(engine.getSheetSerialized(0)).toEqual([[2, 1], [4, 3], [null, 5]]) - }) - - it('should work with more complicated permutations', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - expect(engine.isItPossibleToSwapColumnIndexes(0, [[0, 1], [1, 2], [2, 0]])).toEqual(true) - engine.swapColumnIndexes(0, [[0, 1], [1, 2], [2, 0]]) - expect(engine.getSheetSerialized(0)).toEqual([[3, 1, 2], [6, 4, 5], [9, 7, 8]]) - }) - - it('should not move values unnecessarily', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6]]) - expect(engine.isItPossibleToSwapColumnIndexes(0, [[0, 0], [1, 1]])).toEqual(true) - const ret = engine.swapColumnIndexes(0, [[0, 0], [1, 1]]) - expect(ret.length).toEqual(0) - }) - - it('should work with external references', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3, '=A1', '=SUM(A2:A3)'], [4, 5, 6], [7, 8, 9]]) - expect(engine.isItPossibleToSwapColumnIndexes(0, [[0, 1], [1, 2], [2, 0]])).toEqual(true) - engine.swapColumnIndexes(0, [[0, 1], [1, 2], [2, 0]]) - expect(engine.getSheetSerialized(0)).toEqual([[3, 1, 2, '=A1', '=SUM(A2:A3)'], [6, 4, 5], [9, 7, 8]]) - expect(engine.getSheetValues(0)).toEqual([[3, 1, 2, 3, 15], [6, 4, 5], [9, 7, 8]]) - }) - - it('should work with internal references', () => { - const engine = HyperFormula.buildFromArray([['=A2', '=SUM(B2:B3)', 3], [1, '=SUM(B10:B15)', '=A10'], ['=SUM(D1:D10)', 8, 9]]) - expect(engine.isItPossibleToSwapColumnIndexes(0, [[0, 1], [1, 2], [2, 0]])).toEqual(true) - engine.swapColumnIndexes(0, [[0, 1], [1, 2], [2, 0]]) - expect(engine.getSheetSerialized(0)).toEqual([[3, '=B2', '=SUM(C2:C3)'], ['=#REF!', 1, '=SUM(C10:C15)'], [9, '=SUM(E1:E10)', 8]]) - }) -}) - -describe('swapping columns working with undo', () => { - it('should work on static engine', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - engine.swapColumnIndexes(0, [[0, 1], [1, 2], [2, 0]]) - engine.undo() - expect(engine.getSheetSerialized(0)).toEqual([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - }) - - it('should work with external references', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3, '=A1', '=SUM(A2:A3)'], [4, 5, 6], [7, 8, 9]]) - engine.swapColumnIndexes(0, [[0, 1], [1, 2], [2, 0]]) - engine.undo() - expect(engine.getSheetSerialized(0)).toEqual([[1, 2, 3, '=A1', '=SUM(A2:A3)'], [4, 5, 6], [7, 8, 9]]) - }) - - it('should work with internal references', () => { - const engine = HyperFormula.buildFromArray([['=A2', '=SUM(B2:B3)', 3], [1, '=SUM(B10:B15)', '=A10'], ['=SUM(D1:D10)', 8, 9]]) - engine.swapColumnIndexes(0, [[0, 1], [1, 2], [2, 0]]) - engine.undo() - expect(engine.getSheetSerialized(0)).toEqual([['=A2', '=SUM(B2:B3)', 3], [1, '=SUM(B10:B15)', '=A10'], ['=SUM(D1:D10)', 8, 9]]) - }) -}) - -describe('swapping columns working with redo', () => { - it('should work on static engine', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - engine.swapColumnIndexes(0, [[0, 1], [1, 2], [2, 0]]) - engine.undo() - expect(engine.isItPossibleToSwapColumnIndexes(0, [[0, 1], [1, 2], [2, 0]])).toEqual(true) - engine.redo() - expect(engine.getSheetSerialized(0)).toEqual([[3, 1, 2], [6, 4, 5], [9, 7, 8]]) - }) - - it('should work with external references', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3, '=A1', '=SUM(A2:A3)'], [4, 5, 6], [7, 8, 9]]) - engine.swapColumnIndexes(0, [[0, 1], [1, 2], [2, 0]]) - engine.undo() - expect(engine.isItPossibleToSwapColumnIndexes(0, [[0, 1], [1, 2], [2, 0]])).toEqual(true) - engine.redo() - expect(engine.getSheetSerialized(0)).toEqual([[3, 1, 2, '=A1', '=SUM(A2:A3)'], [6, 4, 5], [9, 7, 8]]) - expect(engine.getSheetValues(0)).toEqual([[3, 1, 2, 3, 15], [6, 4, 5], [9, 7, 8]]) - }) - - it('should work with internal references', () => { - const engine = HyperFormula.buildFromArray([['=A2', '=SUM(B2:B3)', 3], [1, '=SUM(B10:B15)', '=A10'], ['=SUM(D1:D10)', 8, 9]]) - engine.swapColumnIndexes(0, [[0, 1], [1, 2], [2, 0]]) - engine.undo() - expect(engine.isItPossibleToSwapColumnIndexes(0, [[0, 1], [1, 2], [2, 0]])).toEqual(true) - engine.redo() - expect(engine.getSheetSerialized(0)).toEqual([[3, '=B2', '=SUM(C2:C3)'], ['=#REF!', 1, '=SUM(C10:C15)'], [9, '=SUM(E1:E10)', 8]]) - }) - - it('clears redo stack', () => { - const engine = HyperFormula.buildFromArray([[1]]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.swapColumnIndexes(0, [[0, 0]]) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('setting column order - checking if it is possible', () => { - it('should check for length', () => { - const engine = HyperFormula.buildFromArray([[]]) - expect(engine.isItPossibleToSetColumnOrder(0, [0])).toEqual(false) - expect(() => - engine.setColumnOrder(0, [0]) - ).toThrowError('Invalid arguments, expected number of columns provided to be sheet width.') - }) - - it('should validate sources for non-integer values', () => { - const engine = HyperFormula.buildFromArray([[]]) - expect(engine.isItPossibleToSetColumnOrder(0, [0, 0.5])).toEqual(false) - expect(() => - engine.setColumnOrder(0, [0, 0.5]) - ).toThrowError('Invalid arguments, expected number of columns provided to be sheet width.') - }) - - it('should validate for repeated values', () => { - const engine = HyperFormula.buildFromArray([[0, 0, 0]]) - expect(engine.isItPossibleToSetColumnOrder(0, [0, 1, 1])).toEqual(false) - expect(() => - engine.setColumnOrder(0, [0, 1, 1]) - ).toThrowError('Invalid arguments, expected target column numbers to be permutation of source column numbers.') - }) - - it('should validate sources to be permutation of targets', () => { - const engine = HyperFormula.buildFromArray([[0, 0, 0]]) - expect(engine.isItPossibleToSetColumnOrder(0, [1, 2, 3])).toEqual(false) - expect(() => - engine.setColumnOrder(0, [1, 2, 3]) - ).toThrowError('Invalid arguments, expected target column numbers to be permutation of source column numbers.') - }) - - it('should check for matrices', () => { - const engine = HyperFormula.buildFromArray([[0, 0, '=TRANSPOSE(A1:B1)']]) - expect(engine.isItPossibleToSetColumnOrder(0, [2, 1, 0])).toEqual(false) - expect(() => - engine.setColumnOrder(0, [2, 1, 0]) - ).toThrowError('Cannot perform this operation, source location has an array inside.') - }) - - it('should check for matrices only in moved columns', () => { - const engine = HyperFormula.buildFromArray([[0, 0, '=TRANSPOSE(A1:B1)']]) - expect(engine.isItPossibleToSetColumnOrder(0, [1, 0, 2])).toEqual(true) - expect(() => - engine.setColumnOrder(0, [1, 0, 2]) - ).not.toThrowError() - }) -}) - -describe('setColumnOrder', () => { - it('should work on static engine', () => { - const engine = HyperFormula.buildFromArray([[1, 'abcd'], [3, 3], [5, true]]) - expect(engine.isItPossibleToSetColumnOrder(0, [1, 0])).toEqual(true) - engine.setColumnOrder(0, [1, 0]) - expect(engine.getSheetSerialized(0)).toEqual([['abcd', 1], [3, 3], [true, 5]]) - }) - - it('should return number of changed cells', () => { - const engine = HyperFormula.buildFromArray([[1, 2], [3, 4], [5, 6]]) - expect(engine.isItPossibleToSetColumnOrder(0, [1, 0])).toEqual(true) - const ret = engine.setColumnOrder(0, [1, 0]) - expect(ret.length).toEqual(6) - }) - - it('should work on static engine with uneven column', () => { - const engine = HyperFormula.buildFromArray([[1, 2], [3, 4], [5]], {chooseAddressMappingPolicy: new AlwaysSparse()}) - expect(engine.isItPossibleToSetColumnOrder(0, [1, 0])).toEqual(true) - engine.setColumnOrder(0, [1, 0]) - expect(engine.getSheetSerialized(0)).toEqual([[2, 1], [4, 3], [null, 5]]) - }) - - it('should work with more complicated permutations', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - expect(engine.isItPossibleToSetColumnOrder(0, [1, 2, 0])).toEqual(true) - engine.setColumnOrder(0, [1, 2, 0]) - expect(engine.getSheetSerialized(0)).toEqual([[3, 1, 2], [6, 4, 5], [9, 7, 8]]) - }) - - it('should work with strings', () => { - const hfInstance = HyperFormula.buildFromArray([ - ['A', 'B', 'C', 'D'] - ]) - - hfInstance.setColumnOrder(0, [0, 3, 2, 1]) - expect(hfInstance.getSheetSerialized(0)).toEqual([['A', 'D', 'C', 'B']]) - }) - - it('should not move values unnecessarily', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6]]) - expect(engine.isItPossibleToSetColumnOrder(0, [0, 1, 2])).toEqual(true) - const ret = engine.setColumnOrder(0, [0, 1, 2]) - expect(ret.length).toEqual(0) - }) - - it('should work with external references', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3, '=A1', '=SUM(A2:A3)'], [4, 5, 6], [7, 8, 9]]) - expect(engine.isItPossibleToSetColumnOrder(0, [1, 2, 0, 3, 4])).toEqual(true) - engine.setColumnOrder(0, [1, 2, 0, 3, 4]) - expect(engine.getSheetSerialized(0)).toEqual([[3, 1, 2, '=A1', '=SUM(A2:A3)'], [6, 4, 5], [9, 7, 8]]) - expect(engine.getSheetValues(0)).toEqual([[3, 1, 2, 3, 15], [6, 4, 5], [9, 7, 8]]) - }) - - it('should work with internal references', () => { - const engine = HyperFormula.buildFromArray([['=A2', '=SUM(B2:B3)', 3], [1, '=SUM(B10:B15)', '=A10'], ['=SUM(D1:D10)', 8, 9]]) - expect(engine.isItPossibleToSetColumnOrder(0, [1, 2, 0, 3])).toEqual(true) - engine.setColumnOrder(0, [1, 2, 0, 3]) - expect(engine.getSheetSerialized(0)).toEqual([[3, '=B2', '=SUM(C2:C3)'], ['=#REF!', 1, '=SUM(C10:C15)'], [9, '=SUM(E1:E10)', 8]]) - }) - - it('leaves the engine in a valid state so other operations are possible afterwards', () => { - const engine = HyperFormula.buildFromArray([[null, '=A1', 42]]) - engine.setColumnOrder(0, [1, 0, 2]) - engine.setCellContents(adr('A1'), '=B1') - expect(engine.getSheetSerialized(0)).toEqual([['=B1', null, 42]]) - }) - - it('leaves the engine in a valid state so other operations are possible afterwards (with a range)', () => { - const engine = HyperFormula.buildFromArray([[null, null, null, '=SUM(A1:C1)', 42]]) - engine.setColumnOrder(0, [1, 2, 3, 0, 4]) - engine.setCellContents(adr('A1'), '=SUM(B1:D1)') - expect(engine.getSheetSerialized(0)).toEqual([['=SUM(B1:D1)', null, null, null, 42]]) - }) -}) - -describe('reorder working with undo', () => { - it('should work on static engine', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - engine.setColumnOrder(0, [1, 2, 0]) - engine.undo() - expect(engine.getSheetSerialized(0)).toEqual([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - }) - - it('should work with external references', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3, '=A1', '=SUM(A2:A3)'], [4, 5, 6], [7, 8, 9]]) - engine.setColumnOrder(0, [1, 2, 0, 3, 4]) - engine.undo() - expect(engine.getSheetSerialized(0)).toEqual([[1, 2, 3, '=A1', '=SUM(A2:A3)'], [4, 5, 6], [7, 8, 9]]) - }) - - it('should work with internal references', () => { - const engine = HyperFormula.buildFromArray([['=A2', '=SUM(B2:B3)', 3], [1, '=SUM(B10:B15)', '=A10'], ['=SUM(D1:D10)', 8, 9]]) - engine.setColumnOrder(0, [1, 2, 0, 3]) - engine.undo() - expect(engine.getSheetSerialized(0)).toEqual([['=A2', '=SUM(B2:B3)', 3], [1, '=SUM(B10:B15)', '=A10'], ['=SUM(D1:D10)', 8, 9]]) - }) -}) - -describe('reorder working with redo', () => { - it('should work on static engine', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - engine.setColumnOrder(0, [1, 2, 0]) - engine.undo() - expect(engine.isItPossibleToSetColumnOrder(0, [1, 2, 0])).toEqual(true) - engine.redo() - expect(engine.getSheetSerialized(0)).toEqual([[3, 1, 2], [6, 4, 5], [9, 7, 8]]) - }) - - it('should work with external references', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3, '=A1', '=SUM(A2:A3)'], [4, 5, 6], [7, 8, 9]]) - engine.setColumnOrder(0, [1, 2, 0, 3, 4]) - engine.undo() - expect(engine.isItPossibleToSetColumnOrder(0, [1, 2, 0, 3, 4])).toEqual(true) - engine.redo() - expect(engine.getSheetSerialized(0)).toEqual([[3, 1, 2, '=A1', '=SUM(A2:A3)'], [6, 4, 5], [9, 7, 8]]) - expect(engine.getSheetValues(0)).toEqual([[3, 1, 2, 3, 15], [6, 4, 5], [9, 7, 8]]) - }) - - it('should work with internal references', () => { - const engine = HyperFormula.buildFromArray([['=A2', '=SUM(B2:B3)', 3], [1, '=SUM(B10:B15)', '=A10'], ['=SUM(D1:D10)', 8, 9]]) - engine.setColumnOrder(0, [1, 2, 0, 3]) - engine.undo() - expect(engine.isItPossibleToSetColumnOrder(0, [1, 2, 0, 3, 4])).toEqual(true) - engine.redo() - expect(engine.getSheetSerialized(0)).toEqual([[3, '=B2', '=SUM(C2:C3)'], ['=#REF!', 1, '=SUM(C10:C15)'], [9, '=SUM(E1:E10)', 8]]) - }) - - it('clears redo stack', () => { - const engine = HyperFormula.buildFromArray([[1]]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.setColumnOrder(0, [0]) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) diff --git a/test/unit/cruds/set-matrix-empty.spec.ts b/test/unit/cruds/set-matrix-empty.spec.ts deleted file mode 100644 index b0b8e20fb4..0000000000 --- a/test/unit/cruds/set-matrix-empty.spec.ts +++ /dev/null @@ -1,121 +0,0 @@ -import {HyperFormula} from '../../../src' -import {AbsoluteCellRange} from '../../../src/AbsoluteCellRange' -import {adr} from '../testUtils' - -describe('Set matrix empty', () => { - it('should set matrix empty', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - ]) - const dependencyGraph = engine.dependencyGraph - - const matrixVertex = dependencyGraph.arrayMapping.getArray(AbsoluteCellRange.spanFrom(adr('A2'), 1, 2))! - - dependencyGraph.setArrayEmpty(matrixVertex) - - expect(engine.getCellValue(adr('A2'))).toBe(null) - expect(engine.getCellValue(adr('A3'))).toBe(null) - expect(dependencyGraph.arrayMapping.arrayMapping.size).toEqual(0) - }) - - it('should adjust edges between matrix cells and formula', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=A1+A2'], - ['=TRANSPOSE(A1:B1)'], - ]) - const dependencyGraph = engine.dependencyGraph - - const matrixVertex = dependencyGraph.arrayMapping.getArray(AbsoluteCellRange.spanFrom(adr('A2'), 1, 2))! - - dependencyGraph.setArrayEmpty(matrixVertex) - - expect(engine.getCellValue(adr('A2'))).toBe(null) - expect(engine.getCellValue(adr('A3'))).toBe(null) - expect(dependencyGraph.arrayMapping.arrayMapping.size).toEqual(0) - - const formula = dependencyGraph.fetchCell(adr('C1')) - const a1 = dependencyGraph.fetchCell(adr('A1')) - const a2 = dependencyGraph.fetchCell(adr('A2')) - const a3 = dependencyGraph.getCell(adr('A3')) - expect(dependencyGraph.existsEdge(matrixVertex, formula)).toBe(false) - expect(dependencyGraph.existsEdge(a1, formula)).toBe(true) - expect(dependencyGraph.existsEdge(a2, formula)).toBe(true) - expect(a3).toBe(undefined) - }) - - it('should adjust edges between matrix cells and formula matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=TRANSPOSE(A1:B1)'], - ['=TRANSPOSE(A1:B1)'], - ]) - const dependencyGraph = engine.dependencyGraph - - const matrixVertex = dependencyGraph.arrayMapping.getArray(AbsoluteCellRange.spanFrom(adr('A2'), 1, 2))! - - dependencyGraph.setArrayEmpty(matrixVertex) - - expect(engine.getCellValue(adr('A2'))).toBe(null) - expect(engine.getCellValue(adr('A3'))).toBe(null) - expect(dependencyGraph.arrayMapping.arrayMapping.size).toEqual(1) - - const formulaMatrix = dependencyGraph.fetchCell(adr('C1')) - const a1 = dependencyGraph.fetchCell(adr('A1')) - expect(dependencyGraph.existsEdge(matrixVertex, formulaMatrix)).toBe(false) - expect(dependencyGraph.existsEdge(a1, formulaMatrix)).toBe(false) - }) - - it('should adjust edges between matrix cells and range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=SUM(A2:A3)'], - ['=TRANSPOSE(A1:B1)'], - ]) - const dependencyGraph = engine.dependencyGraph - - const matrixVertex = dependencyGraph.arrayMapping.getArray(AbsoluteCellRange.spanFrom(adr('A2'), 1, 2))! - - const rangeVertex = dependencyGraph.rangeMapping.getRangeVertex(adr('A2'), adr('A3'))! - expect(dependencyGraph.existsEdge(matrixVertex, rangeVertex)).toBe(true) - - dependencyGraph.setArrayEmpty(matrixVertex) - - expect(dependencyGraph.arrayMapping.arrayMapping.size).toEqual(0) - - const formula = dependencyGraph.fetchCell(adr('C1')) - const a2 = dependencyGraph.fetchCell(adr('A2')) - const a3 = dependencyGraph.fetchCell(adr('A3')) - expect(a2).not.toBe(a3) - expect(dependencyGraph.existsEdge(rangeVertex, formula)).toBe(true) - expect(dependencyGraph.existsEdge(matrixVertex, rangeVertex)).toBe(false) - expect(dependencyGraph.existsEdge(a2, rangeVertex)).toBe(true) - expect(dependencyGraph.existsEdge(a3, rangeVertex)).toBe(true) - }) - - it('should adjust edges between matrix cells and range crossing matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=SUM(A1:A2)'], - ['=TRANSPOSE(A1:B1)'], - ]) - const dependencyGraph = engine.dependencyGraph - - const matrixVertex = dependencyGraph.arrayMapping.getArray(AbsoluteCellRange.spanFrom(adr('A2'), 1, 2))! - - const rangeVertex = dependencyGraph.rangeMapping.getRangeVertex(adr('A1'), adr('A2'))! - expect(dependencyGraph.existsEdge(matrixVertex, rangeVertex)).toBe(true) - - dependencyGraph.setArrayEmpty(matrixVertex) - - expect(dependencyGraph.arrayMapping.arrayMapping.size).toEqual(0) - - const formula = dependencyGraph.fetchCell(adr('C1')) - const a1 = dependencyGraph.fetchCell(adr('A1')) - const a2 = dependencyGraph.fetchCell(adr('A2')) - const a3 = dependencyGraph.getCell(adr('A3')) - expect(dependencyGraph.existsEdge(rangeVertex, formula)).toBe(true) - expect(dependencyGraph.existsEdge(matrixVertex, rangeVertex)).toBe(false) - expect(dependencyGraph.existsEdge(a1, rangeVertex)).toBe(true) - expect(dependencyGraph.existsEdge(a2, rangeVertex)).toBe(true) - expect(a3).toBe(undefined) - expect(engine.getCellValue(adr('A1'))).toBe(1) - }) -}) diff --git a/test/unit/cruds/set-row-order.spec.ts b/test/unit/cruds/set-row-order.spec.ts deleted file mode 100644 index fcf3137d17..0000000000 --- a/test/unit/cruds/set-row-order.spec.ts +++ /dev/null @@ -1,390 +0,0 @@ -import {HyperFormula, AlwaysSparse} from '../../../src' -import {adr} from '../testUtils' - -describe('swapping rows - checking if it is possible', () => { - it('should validate numbers for negative rows', () => { - const engine = HyperFormula.buildFromArray([[]]) - expect(engine.isItPossibleToSwapRowIndexes(0, [[-1, 0]])).toEqual(false) - expect(() => - engine.swapRowIndexes(0, [[-1, 0]]) - ).toThrowError('Invalid arguments, expected row numbers to be nonnegative integers and less than sheet height.') - }) - - it('should validate sources for non-integer values', () => { - const engine = HyperFormula.buildFromArray([[]]) - expect(engine.isItPossibleToSwapRowIndexes(0, [[1, 1], [0.5, 0]])).toEqual(false) - expect(() => - engine.swapRowIndexes(0, [[1, 1], [0.5, 0]]) - ).toThrowError('Invalid arguments, expected row numbers to be nonnegative integers and less than sheet height.') - }) - - it('should validate sources for values exceeding sheet height', () => { - const engine = HyperFormula.buildFromArray([[0], [0], [0]]) - expect(engine.isItPossibleToSwapRowIndexes(0, [[1, 1], [3, 0]])).toEqual(false) - expect(() => - engine.swapRowIndexes(0, [[3, 0]]) - ).toThrowError('Invalid arguments, expected row numbers to be nonnegative integers and less than sheet height.') - }) - - it('should validate sources to be unique', () => { - const engine = HyperFormula.buildFromArray([[0], [0], [0]]) - expect(engine.isItPossibleToSwapRowIndexes(0, [[0, 0], [1, 1], [1, 2]])).toEqual(false) - expect(() => - engine.swapRowIndexes(0, [[0, 0], [1, 1], [1, 2]]) - ).toThrowError('Invalid arguments, expected source row numbers to be unique.') - }) - - it('should validate sources to be permutation of targets', () => { - const engine = HyperFormula.buildFromArray([[0], [0], [0]]) - expect(engine.isItPossibleToSwapRowIndexes(0, [[0, 0], [1, 1], [2, 1]])).toEqual(false) - expect(() => - engine.swapRowIndexes(0, [[0, 0], [1, 1], [2, 1]]) - ).toThrowError('Invalid arguments, expected target row numbers to be permutation of source row numbers.') - }) - - it('should check for matrices', () => { - const engine = HyperFormula.buildFromArray([[0], [0], ['=TRANSPOSE(A1:A2)']]) - expect(engine.isItPossibleToSwapRowIndexes(0, [[0, 2], [1, 1], [2, 0]])).toEqual(false) - expect(() => - engine.swapRowIndexes(0, [[0, 2], [1, 1], [2, 0]]) - ).toThrowError('Cannot perform this operation, source location has an array inside.') - }) - - it('should check for matrices only in moved rows', () => { - const engine = HyperFormula.buildFromArray([[0], [0], ['=TRANSPOSE(A1:A2)']]) - expect(engine.isItPossibleToSwapRowIndexes(0, [[0, 1], [1, 0], [2, 2]])).toEqual(true) - expect(() => - engine.swapRowIndexes(0, [[0, 1], [1, 0], [2, 2]]) - ).not.toThrowError() - }) -}) - -describe('swapping rows should correctly work', () => { - it('should work on static engine', () => { - const engine = HyperFormula.buildFromArray([[1, 'abcd', 3], [3, 5, true]]) - expect(engine.isItPossibleToSwapRowIndexes(0, [[0, 1], [1, 0]])).toEqual(true) - engine.swapRowIndexes(0, [[0, 1], [1, 0]]) - expect(engine.getSheetSerialized(0)).toEqual([[3, 5, true], [1, 'abcd', 3]]) - }) - - it('should return number of changed cells', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6]]) - expect(engine.isItPossibleToSwapRowIndexes(0, [[0, 1], [1, 0]])).toEqual(true) - const ret = engine.swapRowIndexes(0, [[0, 1], [1, 0]]) - expect(ret.length).toEqual(6) - }) - - it('should work on static engine with uneven rows', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6, 7, 8]], {chooseAddressMappingPolicy: new AlwaysSparse()}) - expect(engine.isItPossibleToSwapRowIndexes(0, [[0, 1], [1, 0]])).toEqual(true) - engine.swapRowIndexes(0, [[0, 1], [1, 0]]) - expect(engine.getSheetSerialized(0)).toEqual([[4, 5, 6, 7, 8], [1, 2, 3]]) - }) - - it('should work with more complicated permutations', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - expect(engine.isItPossibleToSwapRowIndexes(0, [[0, 1], [1, 2], [2, 0]])).toEqual(true) - engine.swapRowIndexes(0, [[0, 1], [1, 2], [2, 0]]) - expect(engine.getSheetSerialized(0)).toEqual([[7, 8, 9], [1, 2, 3], [4, 5, 6]]) - }) - - it('should not move values unnecessarily', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6]]) - expect(engine.isItPossibleToSwapRowIndexes(0, [[0, 0], [1, 1]])).toEqual(true) - const ret = engine.swapRowIndexes(0, [[0, 0], [1, 1]]) - expect(ret.length).toEqual(0) - }) - - it('should work with external references', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9], ['=A1', '=SUM(A2:A3)']]) - engine.swapRowIndexes(0, [[0, 1], [1, 2], [2, 0]]) - expect(engine.getSheetSerialized(0)).toEqual([[7, 8, 9], [1, 2, 3], [4, 5, 6], ['=A1', '=SUM(A2:A3)']]) - expect(engine.getSheetValues(0)).toEqual([[7, 8, 9], [1, 2, 3], [4, 5, 6], [7, 5]]) - }) - - it('should work with internal references', () => { - const engine = HyperFormula.buildFromArray([['=A2', '=SUM(B2:B3)', 3], ['=A10', '=SUM(B10:B15)', 6], ['=SUM(C1:C10)', 8, 9]]) - expect(engine.isItPossibleToSwapRowIndexes(0, [[0, 1], [1, 2], [2, 0]])).toEqual(true) - engine.swapRowIndexes(0, [[0, 1], [1, 2], [2, 0]]) - expect(engine.getSheetSerialized(0)).toEqual([['=SUM(#REF!)', 8, 9], ['=A3', '=SUM(B3:B4)', 3], ['=A11', '=SUM(B11:B16)', 6]]) - }) -}) - -describe('swapping rows working with undo', () => { - it('should work on static engine', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - engine.swapRowIndexes(0, [[0, 1], [1, 2], [2, 0]]) - engine.undo() - expect(engine.getSheetSerialized(0)).toEqual([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - }) - - it('should work with external references', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9], ['=A1', '=SUM(A2:A3)']]) - engine.swapRowIndexes(0, [[0, 1], [1, 2], [2, 0]]) - engine.undo() - expect(engine.getSheetSerialized(0)).toEqual([[1, 2, 3], [4, 5, 6], [7, 8, 9], ['=A1', '=SUM(A2:A3)']]) - }) - - it('should work with internal references', () => { - const engine = HyperFormula.buildFromArray([['=A2', '=SUM(B2:B3)', 3], ['=A10', '=SUM(B10:B15)', 6], ['=SUM(C1:C10)', 8, 9]]) - engine.swapRowIndexes(0, [[0, 1], [1, 2], [2, 0]]) - engine.undo() - expect(engine.getSheetSerialized(0)).toEqual([['=A2', '=SUM(B2:B3)', 3], ['=A10', '=SUM(B10:B15)', 6], ['=SUM(C1:C10)', 8, 9]]) - }) -}) - -describe('swapping rows working with redo', () => { - it('should work on static engine', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - engine.swapRowIndexes(0, [[0, 1], [1, 2], [2, 0]]) - engine.undo() - expect(engine.isItPossibleToSwapRowIndexes(0, [[0, 1], [1, 2], [2, 0]])).toEqual(true) - engine.redo() - expect(engine.getSheetSerialized(0)).toEqual([[7, 8, 9], [1, 2, 3], [4, 5, 6]]) - }) - - it('should work with external references', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9], ['=A1', '=SUM(A2:A3)']]) - engine.swapRowIndexes(0, [[0, 1], [1, 2], [2, 0]]) - engine.undo() - expect(engine.isItPossibleToSwapRowIndexes(0, [[0, 1], [1, 2], [2, 0]])).toEqual(true) - engine.redo() - expect(engine.getSheetSerialized(0)).toEqual([[7, 8, 9], [1, 2, 3], [4, 5, 6], ['=A1', '=SUM(A2:A3)']]) - expect(engine.getSheetValues(0)).toEqual([[7, 8, 9], [1, 2, 3], [4, 5, 6], [7, 5]]) - }) - - it('should work with internal references', () => { - const engine = HyperFormula.buildFromArray([['=A2', '=SUM(B2:B3)', 3], ['=A10', '=SUM(B10:B15)', 6], ['=SUM(C1:C10)', 8, 9]]) - engine.swapRowIndexes(0, [[0, 1], [1, 2], [2, 0]]) - engine.undo() - expect(engine.isItPossibleToSwapRowIndexes(0, [[0, 1], [1, 2], [2, 0]])).toEqual(true) - engine.redo() - expect(engine.getSheetSerialized(0)).toEqual([['=SUM(#REF!)', 8, 9], ['=A3', '=SUM(B3:B4)', 3], ['=A11', '=SUM(B11:B16)', 6]]) - }) - - it('clears redo stack', () => { - const engine = HyperFormula.buildFromArray([[1]]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.swapRowIndexes(0, [[0, 0]]) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -function fillValues(order: number[], fill: number): number[] { - while (order.length < fill) { - const x = order.length - order[x] = x - } - return order -} - -describe('setting row order - checking if it is possible', () => { - it('should check for length', () => { - const engine = HyperFormula.buildFromArray([[]]) - expect(engine.isItPossibleToSetRowOrder(0, [0])).toEqual(false) - expect(() => - engine.setRowOrder(0, [0]) - ).toThrowError('Invalid arguments, expected number of rows provided to be sheet height.') - }) - - it('should validate sources for non-integer values', () => { - const engine = HyperFormula.buildFromArray([[0], [0]]) - expect(engine.isItPossibleToSetRowOrder(0, [0, 0.5])).toEqual(false) - expect(() => - engine.setRowOrder(0, [0, 0.5]) - ).toThrowError('Invalid arguments, expected target row numbers to be permutation of source row numbers.') - }) - - it('should validate for repeated values', () => { - const engine = HyperFormula.buildFromArray([[0], [0], [0]]) - expect(engine.isItPossibleToSetRowOrder(0, [0, 1, 1])).toEqual(false) - expect(() => - engine.setRowOrder(0, [0, 1, 1]) - ).toThrowError('Invalid arguments, expected target row numbers to be permutation of source row numbers.') - }) - - it('should validate sources to be permutation of targets', () => { - const engine = HyperFormula.buildFromArray([[0], [0], [0]]) - expect(engine.isItPossibleToSetRowOrder(0, [1, 2, 3])).toEqual(false) - expect(() => - engine.setRowOrder(0, [1, 2, 3]) - ).toThrowError('Invalid arguments, expected target row numbers to be permutation of source row numbers.') - }) - - it('should check for matrices', () => { - const engine = HyperFormula.buildFromArray([[0], [0], ['=TRANSPOSE(A1:A2)']]) - expect(engine.isItPossibleToSetRowOrder(0, [2, 1, 0])).toEqual(false) - expect(() => - engine.setRowOrder(0, [2, 1, 0]) - ).toThrowError('Cannot perform this operation, source location has an array inside.') - }) - - it('should check for matrices only in moved rows', () => { - const engine = HyperFormula.buildFromArray([[0], [0], ['=TRANSPOSE(A1:A2)']]) - expect(engine.isItPossibleToSetRowOrder(0, [1, 0, 2])).toEqual(true) - expect(() => - engine.setRowOrder(0, [1, 0, 2]) - ).not.toThrowError() - }) -}) - -describe('reorder base case', () => { - it('should work on static engine', () => { - const engine = HyperFormula.buildFromArray([[1, 'abcd', 3], [3, 5, true]]) - expect(engine.isItPossibleToSetRowOrder(0, [1, 0])).toEqual(true) - engine.setRowOrder(0, [1, 0]) - expect(engine.getSheetSerialized(0)).toEqual([[3, 5, true], [1, 'abcd', 3]]) - }) - - it('should return number of changed cells', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6]]) - expect(engine.isItPossibleToSetRowOrder(0, [1, 0])).toEqual(true) - const ret = engine.setRowOrder(0, [1, 0]) - expect(ret.length).toEqual(6) - }) - - it('should work on static engine with uneven rows', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6, 7, 8]], {chooseAddressMappingPolicy: new AlwaysSparse()}) - expect(engine.isItPossibleToSetRowOrder(0, [1, 0])).toEqual(true) - engine.setRowOrder(0, [1, 0]) - expect(engine.getSheetSerialized(0)).toEqual([[4, 5, 6, 7, 8], [1, 2, 3]]) - }) - - it('should work with more complicated permutations', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - expect(engine.isItPossibleToSetRowOrder(0, [1, 2, 0])).toEqual(true) - engine.setRowOrder(0, [1, 2, 0]) - expect(engine.getSheetSerialized(0)).toEqual([[7, 8, 9], [1, 2, 3], [4, 5, 6]]) - }) - - it('should work with strings', () => { - const hfInstance = HyperFormula.buildFromArray([ - ['A'], - ['B'], - ['C'], - ['D'] - ]) - - hfInstance.setRowOrder(0, [0, 3, 2, 1]) - expect(hfInstance.getSheetSerialized(0)).toEqual([['A'], ['D'], ['C'], ['B']]) - }) - - it('should update the addressing in cells being sorted', () => { - const engine = HyperFormula.buildFromArray([[1, 2, '=A1+B1'], [4, 5, '=A2+B2'], [7, 8, '=A3+B3']]) - expect(engine.getSheetValues(0)).toEqual([[1, 2, 3], [4, 5, 9], [7, 8, 15]]) - expect(engine.isItPossibleToSetRowOrder(0, [1, 2, 0])).toEqual(true) - engine.setRowOrder(0, [1, 2, 0]) - expect(engine.getSheetSerialized(0)).toEqual([[7, 8, '=A1+B1'], [1, 2, '=A2+B2'], [4, 5, '=A3+B3']]) - expect(engine.getSheetValues(0)).toEqual([[7, 8, 15], [1, 2, 3], [4, 5, 9]]) - }) - - it('should not change the constants in formulas when updating addresses', () => { - const engine = HyperFormula.buildFromArray([[1, 2, '=1+A1'], [4, 5, '=2+A2'], [7, 8, '=3+A3']]) - expect(engine.getSheetValues(0)).toEqual([[1, 2, 2], [4, 5, 6], [7, 8, 10]]) - expect(engine.isItPossibleToSetRowOrder(0, [1, 2, 0])).toEqual(true) - engine.setRowOrder(0, [1, 2, 0]) - expect(engine.getSheetSerialized(0)).toEqual([[7, 8, '=3+A1'], [1, 2, '=1+A2'], [4, 5, '=2+A3']]) - expect(engine.getSheetValues(0)).toEqual([[7, 8, 10], [1, 2, 2], [4, 5, 6]]) - }) - - it('should not move values unnecessarily', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6]]) - expect(engine.isItPossibleToSetRowOrder(0, [0, 1])).toEqual(true) - const ret = engine.setRowOrder(0, [0, 1]) - expect(ret.length).toEqual(0) - }) - - it('should work with external references', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9], ['=A1', '=SUM(A2:A3)']]) - engine.setRowOrder(0, [1, 2, 0, 3]) - expect(engine.getSheetSerialized(0)).toEqual([[7, 8, 9], [1, 2, 3], [4, 5, 6], ['=A1', '=SUM(A2:A3)']]) - expect(engine.getSheetValues(0)).toEqual([[7, 8, 9], [1, 2, 3], [4, 5, 6], [7, 5]]) - }) - - it('should work with internal references', () => { - const engine = HyperFormula.buildFromArray([['=A2', '=SUM(B2:B3)', 3], ['=A10', '=SUM(B10:B15)', 6], ['=SUM(C1:C10)', 8, 9]]) - expect(engine.isItPossibleToSetRowOrder(0, fillValues([1, 2, 0], 15))).toEqual(true) - engine.setRowOrder(0, fillValues([1, 2, 0], 15)) - expect(engine.getSheetSerialized(0)).toEqual([['=SUM(#REF!)', 8, 9], ['=A3', '=SUM(B3:B4)', 3], ['=A11', '=SUM(B11:B16)', 6]]) - }) - - it('leaves the engine in a valid state so other operations are possible afterwards', () => { - const engine = HyperFormula.buildFromArray([[null], ['=A1']]) - engine.setRowOrder(0, [1, 0]) - engine.setCellContents(adr('A1'), '=A2') - expect(engine.getSheetSerialized(0)).toEqual([['=A2']]) - }) - - it('leaves the engine in a valid state so other operations are possible afterwards (with a range)', () => { - const engine = HyperFormula.buildFromArray([[null, null, null], ['=SUM(A1:C1)']]) - engine.setRowOrder(0, [1, 0]) - engine.setCellContents(adr('A1'), '=SUM(A2:C2)') - expect(engine.getSheetSerialized(0)).toEqual([['=SUM(A2:C2)']]) - }) -}) - -describe('reorder working with undo', () => { - it('should work on static engine', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - engine.setRowOrder(0, [1, 2, 0]) - engine.undo() - expect(engine.getSheetSerialized(0)).toEqual([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - }) - - it('should work with external references', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9], ['=A1', '=SUM(A2:A3)']]) - engine.setRowOrder(0, [1, 2, 0, 3]) - engine.undo() - expect(engine.getSheetSerialized(0)).toEqual([[1, 2, 3], [4, 5, 6], [7, 8, 9], ['=A1', '=SUM(A2:A3)']]) - }) - - it('should work with internal references', () => { - const engine = HyperFormula.buildFromArray([['=A2', '=SUM(B2:B3)', 3], ['=A10', '=SUM(B10:B15)', 6], ['=SUM(C1:C10)', 8, 9]]) - engine.setRowOrder(0, fillValues([1, 2, 0], 15)) - engine.undo() - expect(engine.getSheetSerialized(0)).toEqual([['=A2', '=SUM(B2:B3)', 3], ['=A10', '=SUM(B10:B15)', 6], ['=SUM(C1:C10)', 8, 9]]) - }) -}) - -describe('reorder working with redo', () => { - it('should work on static engine', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - engine.setRowOrder(0, [1, 2, 0]) - engine.undo() - expect(engine.isItPossibleToSetRowOrder(0, [1, 2, 0])).toEqual(true) - engine.redo() - expect(engine.getSheetSerialized(0)).toEqual([[7, 8, 9], [1, 2, 3], [4, 5, 6]]) - }) - - it('should work with external references', () => { - const engine = HyperFormula.buildFromArray([[1, 2, 3], [4, 5, 6], [7, 8, 9], ['=A1', '=SUM(A2:A3)']]) - engine.setRowOrder(0, [1, 2, 0, 3]) - engine.undo() - expect(engine.isItPossibleToSetRowOrder(0, [1, 2, 0, 3])).toEqual(true) - engine.redo() - expect(engine.getSheetSerialized(0)).toEqual([[7, 8, 9], [1, 2, 3], [4, 5, 6], ['=A1', '=SUM(A2:A3)']]) - expect(engine.getSheetValues(0)).toEqual([[7, 8, 9], [1, 2, 3], [4, 5, 6], [7, 5]]) - }) - - it('should work with internal references', () => { - const engine = HyperFormula.buildFromArray([['=A2', '=SUM(B2:B3)', 3], ['=A10', '=SUM(B10:B15)', 6], ['=SUM(C1:C10)', 8, 9]]) - engine.setRowOrder(0, fillValues([1, 2, 0], 15)) - engine.undo() - expect(engine.isItPossibleToSetRowOrder(0, fillValues([1, 2, 0], 16))).toEqual(true) - engine.redo() - expect(engine.getSheetSerialized(0)).toEqual([['=SUM(#REF!)', 8, 9], ['=A3', '=SUM(B3:B4)', 3], ['=A11', '=SUM(B11:B16)', 6]]) - }) - - it('clears redo stack', () => { - const engine = HyperFormula.buildFromArray([[1]]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.setRowOrder(0, [0]) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) diff --git a/test/unit/custom-functions.spec.ts b/test/unit/custom-functions.spec.ts deleted file mode 100644 index 4dbe70cbe8..0000000000 --- a/test/unit/custom-functions.spec.ts +++ /dev/null @@ -1,682 +0,0 @@ -import { - CellError, - ErrorType, - FunctionArgumentType, - FunctionPluginValidationError, - HyperFormula, - SimpleRangeValue, - ArraySize, -} from '../../src' -import {ErrorMessage} from '../../src/error-message' -import {AliasAlreadyExisting, ProtectedFunctionError, ProtectedFunctionTranslationError} from '../../src/errors' -import {plPL} from '../../src/i18n/languages' -import {InterpreterState} from '../../src/interpreter/InterpreterState' -import {InternalScalarValue} from '../../src/interpreter/InterpreterValue' -import {FunctionPlugin, FunctionPluginTypecheck} from '../../src/interpreter/plugin/FunctionPlugin' -import {ConditionalAggregationPlugin, NumericAggregationPlugin} from '../../src/interpreter/plugin' -import {VersionPlugin} from '../../src/interpreter/plugin/VersionPlugin' -import {ProcedureAst} from '../../src/parser' -import {adr, detailedError, expectArrayWithSameContent, resetSpy} from './testUtils' - -class FooPlugin extends FunctionPlugin implements FunctionPluginTypecheck { - public static implementedFunctions = { - 'FOO': { - method: 'foo', - }, - 'BAR': { - method: 'bar', - }, - 'ARRAYFOO': { - method: 'arrayfoo', - sizeOfResultArrayMethod: 'arraysizeFoo', - parameters: [{ argumentType: FunctionArgumentType.NUMBER }], - }, - } - - public static translations = { - 'enGB': { - 'FOO': 'FOO', - 'BAR': 'BAR', - 'ARRAYFOO': 'ARRAYFOO', - }, - 'plPL': { - 'FOO': 'FU', - 'BAR': 'BAR', - 'ARRAYFOO': 'ARRAYFOO', - } - } - - public foo(_ast: ProcedureAst, _state: InterpreterState): InternalScalarValue { - return 'foo' - } - - public bar(_ast: ProcedureAst, _state: InterpreterState): InternalScalarValue { - return 'bar' - } - - public arrayfoo(_ast: ProcedureAst, _state: InterpreterState): SimpleRangeValue { - return SimpleRangeValue.onlyValues([[1, 1], [1, 1]]) - } - - public arraysizeFoo(_ast: ProcedureAst, _state: InterpreterState): ArraySize { - return new ArraySize(2, 2) - } -} - -class SumWithExtra extends FunctionPlugin implements FunctionPluginTypecheck { - public static implementedFunctions = { - 'SUM': { - method: 'sum', - } - } - - public static aliases = { - 'SUMALIAS': 'SUM', - } - - public sum(ast: ProcedureAst, state: InterpreterState): InternalScalarValue { - const left = this.evaluateAst(ast.args[0], state) as number - const right = this.evaluateAst(ast.args[1], state) as number - return 42 + left + right - } -} - -class InvalidPlugin extends FunctionPlugin implements FunctionPluginTypecheck { - public static implementedFunctions = { - 'FOO': { - method: 'foo', - } - } - - public bar(_ast: ProcedureAst, _state: InterpreterState): InternalScalarValue { - return 'bar' - } -} - -class EmptyAliasPlugin extends FunctionPlugin implements FunctionPluginTypecheck { - public static implementedFunctions = { - 'FOO': { - method: 'foo', - } - } - - public static aliases = { - 'FOOALIAS': 'BAR', - } -} - -class OverloadedAliasPlugin extends FunctionPlugin implements FunctionPluginTypecheck { - public static implementedFunctions = { - 'FOO': { - method: 'foo', - }, - 'BAR': { - method: 'foo', - } - } - - public static aliases = { - 'FOO': 'BAR', - } -} - -class ReservedNamePlugin extends FunctionPlugin implements FunctionPluginTypecheck { - public static implementedFunctions = { - 'VERSION': { - method: 'version', - } - } - - public version(_ast: ProcedureAst, _state: InterpreterState): InternalScalarValue { - return 'foo' - } -} - -class SquarePlugin extends FunctionPlugin implements FunctionPluginTypecheck { - public static implementedFunctions = { - // Key of the mapping describes which function will be used to compute it - 'SQUARE': { - method: 'square', - }, - } - - public square(ast: ProcedureAst, state: InterpreterState): InternalScalarValue { - // Take ast of first argument from list of arguments - const arg = ast.args[0] - - // If there was no argument, return NA error - if (!arg) { - return new CellError(ErrorType.NA) - } - - // Compute value of argument - const argValue = this.evaluateAst(arg, state) - - if (argValue instanceof CellError) { - // If the value is some error, return that error - return argValue - } else if (typeof argValue === 'number') { - // If it's a number, compute the result - return (argValue * argValue) - } else { - // If it's some other type which doesn't make sense in terms of square (string, boolean), return VALUE error - return new CellError(ErrorType.VALUE) - } - } -} - -class GreetingsPlugin extends FunctionPlugin { - public static implementedFunctions = { - GREET: { - method: 'greet', - parameters: [ - { argumentType: FunctionArgumentType.STRING } - ], - }, - EXAMPLE_ARRAY_FUNCTION: { - method: 'exampleArrayFunction', - parameters: [ - { argumentType: FunctionArgumentType.NUMBER } - ], - } - } - - public static translations = { - enGB: { - GREET: 'GREET', - EXAMPLE_ARRAY_FUNCTION: 'EXAMPLE_ARRAY_FUNCTION', - }, - enUS: { - GREET: 'GREET', - EXAMPLE_ARRAY_FUNCTION: 'EXAMPLE_ARRAY_FUNCTION', - } - } - - greet(ast: ProcedureAst, state: InterpreterState) { - return this.runFunction( - ast.args, - state, - this.metadata('GREET'), - (username) => { - if (!username) { - return new CellError(ErrorType.VALUE) - } - - return `👋 Hello, ${username}!` - }, - ) - } - - exampleArrayFunction(ast: ProcedureAst, state: InterpreterState) { - return this.runFunction( - ast.args, - state, - this.metadata('EXAMPLE_ARRAY_FUNCTION'), - (val: number) => { - return SimpleRangeValue.onlyValues([[val, val], [val, val]]) - }, - ) - } -} - -class ContextPlugin extends FunctionPlugin { - public static implementedFunctions = { - 'GETCONTEXT': { - method: 'getContext', - } - } - - public getContext(ast: ProcedureAst, state: InterpreterState): unknown { - return this.config.context - } -} - -describe('Register static custom plugin', () => { - it('should register plugin with translations', () => { - HyperFormula.registerLanguage('plPL', plPL) - HyperFormula.registerFunctionPlugin(FooPlugin, FooPlugin.translations) - - const pl = HyperFormula.getLanguage('plPL') - - expect(pl.getFunctionTranslation('FOO')).toEqual('FU') - }) - - it('should register single function with translations', () => { - HyperFormula.registerFunction('FOO', FooPlugin, FooPlugin.translations) - - const engine = HyperFormula.buildFromArray([['=FOO()']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('foo') - }) - - it('registerFunction should not affect the existing HyperFormula instances', () => { - const engine = HyperFormula.buildFromArray([['=FOO()']]) - HyperFormula.registerFunction('FOO', FooPlugin, FooPlugin.translations) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOO'))) - }) - - it('registerFunctionPlugin should not affect the existing HyperFormula instances', () => { - const engine = HyperFormula.buildFromArray([['=FOO()']]) - HyperFormula.registerFunctionPlugin(FooPlugin, FooPlugin.translations) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOO'))) - }) - - it('should return registered formula translations', () => { - HyperFormula.unregisterAllFunctions() - HyperFormula.registerLanguage('plPL', plPL) - HyperFormula.registerFunctionPlugin(ConditionalAggregationPlugin) - HyperFormula.registerFunctionPlugin(FooPlugin, FooPlugin.translations) - const formulaNames = HyperFormula.getRegisteredFunctionNames('plPL') - - expectArrayWithSameContent(['FU', 'BAR', 'ARRAYFOO', 'SUMA.JEŻELI', 'LICZ.JEŻELI', 'ŚREDNIA.JEŻELI', 'SUMY.JEŻELI', 'LICZ.WARUNKI', 'VERSION', 'PRZESUNIĘCIE', 'MAKS.WARUNKÓW', 'MIN.WARUNKÓW'], formulaNames) - }) - - it('should register all formulas from plugin', () => { - HyperFormula.registerFunctionPlugin(FooPlugin, FooPlugin.translations) - - const engine = HyperFormula.buildFromArray([ - ['=foo()', '=bar()'] - ]) - - expect(HyperFormula.getRegisteredFunctionNames('enGB')).toContain('FOO') - expect(HyperFormula.getRegisteredFunctionNames('enGB')).toContain('BAR') - expect(engine.getCellValue(adr('A1'))).toEqual('foo') - expect(engine.getCellValue(adr('B1'))).toEqual('bar') - }) - - it('should register single formula from plugin', () => { - HyperFormula.registerFunction('BAR', FooPlugin, FooPlugin.translations) - const engine = HyperFormula.buildFromArray([ - ['=foo()', '=bar()'] - ]) - - expect(HyperFormula.getRegisteredFunctionNames('enGB')).not.toContain('FOO') - expect(HyperFormula.getRegisteredFunctionNames('enGB')).toContain('BAR') - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOO'))) - expect(engine.getCellValue(adr('B1'))).toEqual('bar') - }) - - it('should register single array functions', () => { - HyperFormula.registerFunction('ARRAYFOO', FooPlugin, FooPlugin.translations) - const engine = HyperFormula.buildFromArray([ - ['=ARRAYFOO(0)'] - ]) - - expect(engine.getSheetValues(0)).toEqual([[1, 1], [1, 1]]) - }) - - it('should override one formula with custom implementation', () => { - HyperFormula.registerFunction('SUM', SumWithExtra) - const engine = HyperFormula.buildFromArray([ - ['=SUM(1, 2)', '=MAX(1, 2)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(45) - expect(engine.getCellValue(adr('B1'))).toEqual(2) - }) - - it('should allow to register only alias', () => { - HyperFormula.registerFunction('SUMALIAS', SumWithExtra, {'enGB': {'SUMALIAS': 'SUMALIAS'}}) - const engine = HyperFormula.buildFromArray([ - ['=SUMALIAS(1, 2)', '=MAX(1, 2)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(45) - expect(engine.getCellValue(adr('B1'))).toEqual(2) - }) - - it('should throw plugin validation error', () => { - expect(() => { - HyperFormula.registerFunctionPlugin(InvalidPlugin) - }).toThrow(FunctionPluginValidationError.functionMethodNotFound('foo', 'InvalidPlugin')) - - expect(() => { - HyperFormula.registerFunction('FOO', InvalidPlugin) - }).toThrow(FunctionPluginValidationError.functionMethodNotFound('foo', 'InvalidPlugin')) - - expect(() => { - HyperFormula.registerFunction('BAR', InvalidPlugin) - }).toThrow(FunctionPluginValidationError.functionNotDeclaredInPlugin('BAR', 'InvalidPlugin')) - }) - - it('should return registered plugins', () => { - HyperFormula.unregisterAllFunctions() - HyperFormula.registerFunctionPlugin(ConditionalAggregationPlugin) - HyperFormula.registerFunctionPlugin(NumericAggregationPlugin) - HyperFormula.registerFunctionPlugin(SumWithExtra) - - expectArrayWithSameContent(HyperFormula.getAllFunctionPlugins(), [ConditionalAggregationPlugin, NumericAggregationPlugin, SumWithExtra]) - }) - - it('should unregister whole plugin', () => { - HyperFormula.unregisterAllFunctions() - HyperFormula.registerFunctionPlugin(NumericAggregationPlugin) - HyperFormula.registerFunctionPlugin(ConditionalAggregationPlugin) - - HyperFormula.unregisterFunctionPlugin(NumericAggregationPlugin) - - expectArrayWithSameContent(HyperFormula.getAllFunctionPlugins(), [ConditionalAggregationPlugin]) - }) - - it('should return plugin for given functionId', () => { - expect(HyperFormula.getFunctionPlugin('SUMIF')).toBe(ConditionalAggregationPlugin) - }) - - it('should clear function registry', () => { - expect(HyperFormula.getRegisteredFunctionNames('enGB').length).toBeGreaterThan(0) - - HyperFormula.unregisterAllFunctions() - - expect(HyperFormula.getRegisteredFunctionNames('enGB').length).toEqual(2) // protected functions counts - }) -}) - -describe('Instance level formula registry', () => { - beforeEach(() => { - HyperFormula.getLanguage('enGB').extendFunctions({FOO: 'FOO'}) - HyperFormula.getLanguage('enGB').extendFunctions({BAR: 'BAR'}) - }) - - it('should return registered formula ids', () => { - const engine = HyperFormula.buildFromArray([], {functionPlugins: [FooPlugin, SumWithExtra]}) - - expectArrayWithSameContent(engine.getRegisteredFunctionNames(), ['SUM', 'FOO', 'BAR', 'VERSION']) - }) - - it('should create engine only with plugins passed to configuration', () => { - const engine = HyperFormula.buildFromArray([ - ['=foo()', '=bar()', '=SUM(1, 2)'] - ], {functionPlugins: [FooPlugin]}) - - expectArrayWithSameContent(['FOO', 'BAR', 'VERSION'], engine.getRegisteredFunctionNames()) - expect(engine.getCellValue(adr('A1'))).toEqual('foo') - expect(engine.getCellValue(adr('B1'))).toEqual('bar') - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('SUM'))) - }) - - it('modifying static plugins should not affect existing engine instance registry', () => { - HyperFormula.registerFunctionPlugin(FooPlugin) - const engine = HyperFormula.buildFromArray([ - ['=foo()', '=bar()'] - ]) - HyperFormula.unregisterFunction('FOO') - - engine.setCellContents(adr('C1'), '=A1') - - expect(engine.getCellValue(adr('A1'))).toEqual('foo') - expect(engine.getCellValue(adr('B1'))).toEqual('bar') - expect(engine.getCellValue(adr('C1'))).toEqual('foo') - }) - - it('should return registered plugins', () => { - const engine = HyperFormula.buildFromArray([], {functionPlugins: [ConditionalAggregationPlugin, NumericAggregationPlugin, SumWithExtra]}) - - expectArrayWithSameContent(engine.getAllFunctionPlugins(), [ConditionalAggregationPlugin, NumericAggregationPlugin, SumWithExtra]) - }) - - it('should instantiate engine with additional plugin', () => { - const engine = HyperFormula.buildFromArray([], { - functionPlugins: [...HyperFormula.getAllFunctionPlugins(), FooPlugin] - }) - - const registeredPlugins = new Set(engine.getAllFunctionPlugins()) - - expect(registeredPlugins.size).toEqual(HyperFormula.getAllFunctionPlugins().length + 1) - expect(registeredPlugins.has(FooPlugin)).toBe(true) - }) - - it('should rebuild engine and override plugins', () => { - const engine = HyperFormula.buildFromArray([]) - - let registeredPlugins = new Set(engine.getAllFunctionPlugins()) - expect(registeredPlugins.has(ConditionalAggregationPlugin)).toBe(true) - expect(registeredPlugins.has(FooPlugin)).toBe(false) - - engine.updateConfig({functionPlugins: [FooPlugin]}) - registeredPlugins = new Set(engine.getAllFunctionPlugins()) - expect(registeredPlugins.has(FooPlugin)).toBe(true) - expect(registeredPlugins.size).toBe(1) - }) - - it('should return plugin for given functionId', () => { - const engine = HyperFormula.buildFromArray([]) - - expect(engine.getFunctionPlugin('SUMIF')).toBe(ConditionalAggregationPlugin) - }) -}) - -describe('Reserved functions', () => { - it('should not be possible to remove reserved function', () => { - expect(() => { - HyperFormula.unregisterFunction('VERSION') - }).toThrow(ProtectedFunctionError.cannotUnregisterFunctionWithId('VERSION')) - }) - - it('should not be possible to unregister reserved plugin', () => { - expect(() => { - HyperFormula.unregisterFunctionPlugin(VersionPlugin) - }).toThrow(ProtectedFunctionError.cannotUnregisterProtectedPlugin()) - }) - - it('should not be possible to override reserved function', () => { - expect(() => { - HyperFormula.registerFunction('VERSION', ReservedNamePlugin) - }).toThrow(ProtectedFunctionError.cannotRegisterFunctionWithId('VERSION')) - - expect(() => { - HyperFormula.registerFunctionPlugin(ReservedNamePlugin) - }).toThrow(ProtectedFunctionError.cannotRegisterFunctionWithId('VERSION')) - }) - - it('should return undefined when trying to retrieve protected function plugin', () => { - expect(HyperFormula.getFunctionPlugin('VERSION')).toBe(undefined) - }) - - it('should not be possible to override protected function translation when registering plugin', () => { - expect(() => { - HyperFormula.registerFunction('FOO', FooPlugin, {'enGB': {'VERSION': 'FOOBAR'}}) - }).toThrow(new ProtectedFunctionTranslationError('VERSION')) - - expect(() => { - HyperFormula.registerFunctionPlugin(FooPlugin, {'enGB': {'VERSION': 'FOOBAR'}}) - }).toThrow(new ProtectedFunctionTranslationError('VERSION')) - }) -}) - -describe('aliases', () => { - it('should validate that alias target exists', () => { - expect(() => { - HyperFormula.registerFunctionPlugin(EmptyAliasPlugin) - }).toThrow(FunctionPluginValidationError.functionMethodNotFound('foo', 'EmptyAliasPlugin')) - }) - - it('should validate that alias key is available', () => { - expect(() => { - HyperFormula.registerFunctionPlugin(OverloadedAliasPlugin) - }).toThrow(new AliasAlreadyExisting('FOO', 'OverloadedAliasPlugin')) - }) -}) - -describe('Argument validation implemented by hand (without call to runFunction)', () => { - it('works', () => { - HyperFormula.registerFunctionPlugin(SquarePlugin) - HyperFormula.getLanguage('enGB').extendFunctions({SQUARE: 'SQUARE'}) - - const engine = HyperFormula.buildFromArray([ - ['=SQUARE(2)'], - ['=SQUARE()'], - ['=SQUARE(TRUE())'], - ['=SQUARE(1/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) - -describe('Custom function implemented with runFunction)', () => { - it('works for a non-empty string', () => { - HyperFormula.registerFunctionPlugin(GreetingsPlugin, GreetingsPlugin.translations) - - const engine = HyperFormula.buildFromArray([['Anthony', '=GREET(A1)']]) - - expect(engine.getCellValue(adr('B1'))).toEqual('👋 Hello, Anthony!') - }) - - it('returns #VALUE! error for empty string', () => { - HyperFormula.registerFunctionPlugin(GreetingsPlugin, GreetingsPlugin.translations) - - const engine = HyperFormula.buildFromArray([['', '=GREET(A1)']]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.VALUE)) - }) - - it('propagates #DIV_BY_ZERO! error', () => { - HyperFormula.registerFunctionPlugin(GreetingsPlugin, GreetingsPlugin.translations) - - const engine = HyperFormula.buildFromArray([['=1/0', '=GREET(A1)']]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('propagates #CYCLE! error', () => { - HyperFormula.registerFunctionPlugin(GreetingsPlugin, GreetingsPlugin.translations) - - const engine = HyperFormula.buildFromArray([['=B1', '=GREET(A1)']]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.CYCLE)) - }) - - it('function returning array throws error when user tries to vectorize it', () => { - HyperFormula.registerFunctionPlugin(GreetingsPlugin, GreetingsPlugin.translations) - - expect(() => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3], - ['=EXAMPLE_ARRAY_FUNCTION(A1:C1)'], - ], {useArrayArithmetic: true}) - - engine.getCellValue(adr('A2')) - }).toThrow(new Error('Function returning array cannot be vectorized.')) - }) -}) - -describe('Context accessible within custom function', () => { - it('works', () => { - HyperFormula.registerFunctionPlugin(ContextPlugin) - HyperFormula.getLanguage('enGB').extendFunctions({GETCONTEXT: 'GETCONTEXT'}) - - const context = 'abc' - const engine = HyperFormula.buildFromArray([ - ['=GETCONTEXT()'], - ], {context}) - - expect(engine.getCellValue(adr('A1'))).toEqual(context) - }) -}) - -/** - * Mock plugin for testing handling of the depracated parameters. - */ -class DeprecatedPlugin extends FunctionPlugin { - public static implementedFunctions = { - FUNCTION_USING_ARRAY_FUNCTION_PARAM: { - method: 'implementation', - arrayFunction: true, - sizeOfResultArrayMethod: 'arraySizeMethod', - parameters: [ - { argumentType: FunctionArgumentType.NUMBER } - ], - }, - FUNCTION_USING_ARRAY_SIZE_METHOD_PARAM: { - method: 'implementation', - enableArrayArithmeticForArguments: true, - arraySizeMethod: 'arraySizeMethod', - parameters: [ - { argumentType: FunctionArgumentType.NUMBER } - ], - } - } - - public static translations = { - enGB: { - FUNCTION_USING_ARRAY_FUNCTION_PARAM: 'FUNCTION_USING_ARRAY_FUNCTION_PARAM', - FUNCTION_USING_ARRAY_SIZE_METHOD_PARAM: 'FUNCTION_USING_ARRAY_SIZE_METHOD_PARAM', - }, - enUS: { - FUNCTION_USING_ARRAY_FUNCTION_PARAM: 'FUNCTION_USING_ARRAY_FUNCTION_PARAM', - FUNCTION_USING_ARRAY_SIZE_METHOD_PARAM: 'FUNCTION_USING_ARRAY_SIZE_METHOD_PARAM', - }, - } - - /** - * Test function using deprecated arrayFunction parameter. - */ - implementation(ast: ProcedureAst, state: InterpreterState) { - return this.runFunction( - ast.args, - state, - this.metadata('FUNCTION_USING_ARRAY_FUNCTION_PARAM'), - (val: number) => { - return SimpleRangeValue.onlyValues([[val, val], [val, val]]) - }, - ) - } - - /** - * Returns array size for testing deprecated arrayFunction parameter. - */ - arraySizeMethod(): ArraySize { - return new ArraySize(2, 2) - } -} - -describe('Custum function using "arrayFunction" parameter', () => { - it('returns the correct result', () => { - HyperFormula.registerFunctionPlugin(DeprecatedPlugin, DeprecatedPlugin.translations) - const engine = HyperFormula.buildFromArray([['=FUNCTION_USING_ARRAY_FUNCTION_PARAM(1)']]) - - expect(engine.getSheetValues(0)).toEqual([[1, 1], [1, 1]]) - }) - - it('displays a deprecation warning in console', () => { - const consoleWarnSpy = spyOn(console, 'warn') - - try { - HyperFormula.registerFunctionPlugin(DeprecatedPlugin, DeprecatedPlugin.translations) - - expect(consoleWarnSpy).toHaveBeenCalledWith( - "FUNCTION_USING_ARRAY_FUNCTION_PARAM: 'arrayFunction' parameter is deprecated since 3.1.0; Use 'enableArrayArithmeticForArguments' instead." - ) - } finally { - resetSpy(consoleWarnSpy) - } - }) -}) - -describe('Custum function using "arraySizeMethod" parameter', () => { - it('returns the correct result', () => { - HyperFormula.registerFunctionPlugin(DeprecatedPlugin, DeprecatedPlugin.translations) - const engine = HyperFormula.buildFromArray([['=FUNCTION_USING_ARRAY_SIZE_METHOD_PARAM(1)']]) - - expect(engine.getSheetValues(0)).toEqual([[1, 1], [1, 1]]) - }) - - it('displays a deprecation warning in console', () => { - const consoleWarnSpy = spyOn(console, 'warn') - - try { - HyperFormula.registerFunctionPlugin(DeprecatedPlugin, DeprecatedPlugin.translations) - - expect(consoleWarnSpy).toHaveBeenCalledWith( - "FUNCTION_USING_ARRAY_SIZE_METHOD_PARAM: 'arraySizeMethod' parameter is deprecated since 3.1.0; Use 'sizeOfResultArrayMethod' instead." - ) - } finally { - resetSpy(consoleWarnSpy) - } - }) -}) diff --git a/test/unit/date.spec.ts b/test/unit/date.spec.ts deleted file mode 100644 index 44d1f25b78..0000000000 --- a/test/unit/date.spec.ts +++ /dev/null @@ -1,303 +0,0 @@ -import moment from 'moment' -import {Config} from '../../src/Config' -import {DateTimeHelper, SimpleDate} from '../../src/DateTimeHelper' -import {DateNumber, DateTimeNumber, getRawValue, TimeNumber} from '../../src/interpreter/InterpreterValue' -import {Maybe} from '../../src/Maybe' - -describe('Date helpers', () => { - it('#dateToNumber should return number representation of a date', () => { - const dateHelper = new DateTimeHelper(new Config()) - expect(dateHelper.dateToNumber({year: 1900, month: 1, day: 1})).toBe(2) - expect(dateHelper.dateToNumber({year: 1899, month: 12, day: 30})).toBe(0) - expect(dateHelper.dateToNumber({year: 1900, month: 12, day: 31})).toBe(366) - expect(dateHelper.dateToNumber({year: 2018, month: 12, day: 31})).toBe(43465) - }) - - it('#dateToNumber should return number representation of a date, excel compatibility', () => { - const dateHelper = new DateTimeHelper(new Config({leapYear1900: true, nullDate: { year:1899, month: 12, day: 31 }})) - expect(dateHelper.dateToNumber({year: 1899, month: 12, day: 31})).toBe(0) - expect(dateHelper.dateToNumber({year: 1900, month: 1, day: 1})).toBe(1) - expect(dateHelper.dateToNumber({year: 1900, month: 2, day: 28})).toBe(59) - expect(dateHelper.dateToNumber({year: 1900, month: 2, day: 29})).toBe(60) - expect(dateHelper.dateToNumber({year: 1900, month: 3, day: 1})).toBe(61) - expect(dateHelper.dateToNumber({year: 1900, month: 12, day: 31})).toBe(366) - expect(dateHelper.dateToNumber({year: 2018, month: 12, day: 31})).toBe(43465) - }) - - it('#dateNumberToMonthNumber should return proper month number', () => { - const dateHelper = new DateTimeHelper(new Config()) - expect(dateHelper.numberToSimpleDate(0).month).toEqual(12) - expect(dateHelper.numberToSimpleDate(2).month).toEqual(1) - expect(dateHelper.numberToSimpleDate(43465).month).toEqual(12) - }) - - it('#stringToDateNumber - tests expected to return not null, dates', () => { - const dateHelper = new DateTimeHelper(new Config()) - expect(dateHelper.dateStringToDateNumber('16/08/1985')).toEqual(new DateNumber(31275, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber('15/01/2020')).toEqual(new DateNumber(43845, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber('29/02/2000')).toEqual(new DateNumber(36585, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber('31/12/2999')).toEqual(new DateNumber(401768, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber('31 12 2999')).toEqual(new DateNumber(401768, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber(' 31 12 2999 ')).toEqual(new DateNumber(401768, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber('31 12 2999')).toEqual(new DateNumber(401768, 'DD/MM/YYYY')) - }) - - it('#stringToDateNumber - no time format', () => { - const dateHelper = new DateTimeHelper(new Config({timeFormats: []})) - expect(dateHelper.dateStringToDateNumber('16/08/1985')).toEqual(new DateNumber(31275, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber('15/01/2020')).toEqual(new DateNumber(43845, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber('29/02/2000')).toEqual(new DateNumber(36585, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber('31/12/2999')).toEqual(new DateNumber(401768, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber('31 12 2999')).toEqual(new DateNumber(401768, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber(' 31 12 2999 ')).toEqual(new DateNumber(401768, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber('31 12 2999')).toEqual(new DateNumber(401768, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber('16/08/1985 3:40')).toEqual(undefined) - }) - - it('#stringToDateNumber - tests expected to return not null, times', () => { - const dateHelper = new DateTimeHelper(new Config()) - expect(dateHelper.dateStringToDateNumber('00:00')).toEqual(new TimeNumber(0, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('03:00')).toEqual(new TimeNumber(0.125, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('24:00')).toEqual(new TimeNumber(1, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('48:00')).toEqual(new TimeNumber(2, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('00:01')).toEqual(new TimeNumber(0.0006944444444444445, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('00:00:00')).toEqual(new TimeNumber(0, 'hh:mm:ss.sss')) - expect(dateHelper.dateStringToDateNumber('00:00:00.001')).toEqual(new TimeNumber(1.1574074074074076e-8, 'hh:mm:ss.sss')) - expect(dateHelper.dateStringToDateNumber('00:00:00.0001')).toEqual(new TimeNumber(0, 'hh:mm:ss.sss')) - expect(dateHelper.dateStringToDateNumber('00:00:01')).toEqual(new TimeNumber(0.000011574074074074073, 'hh:mm:ss.sss')) - expect(dateHelper.dateStringToDateNumber('00:179:60')).toEqual(new TimeNumber(0.125, 'hh:mm:ss.sss')) - }) - - it('#stringToDateNumber - no date format', () => { - const dateHelper = new DateTimeHelper(new Config({dateFormats: []})) - expect(dateHelper.dateStringToDateNumber('00:00')).toEqual(new TimeNumber(0, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('03:00')).toEqual(new TimeNumber(0.125, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('24:00')).toEqual(new TimeNumber(1, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('48:00')).toEqual(new TimeNumber(2, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('00:01')).toEqual(new TimeNumber(0.0006944444444444445, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('00:00:00')).toEqual(new TimeNumber(0, 'hh:mm:ss.sss')) - expect(dateHelper.dateStringToDateNumber('00:00:00.001')).toEqual(new TimeNumber(1.1574074074074076e-8, 'hh:mm:ss.sss')) - expect(dateHelper.dateStringToDateNumber('00:00:00.0001')).toEqual(new TimeNumber(0, 'hh:mm:ss.sss')) - expect(dateHelper.dateStringToDateNumber('00:00:01')).toEqual(new TimeNumber(0.000011574074074074073, 'hh:mm:ss.sss')) - expect(dateHelper.dateStringToDateNumber('00:179:60')).toEqual(new TimeNumber(0.125, 'hh:mm:ss.sss')) - expect(dateHelper.dateStringToDateNumber('16/08/1985 3:40')).toEqual(undefined) - }) - - it('#stringToDateNumber - fraction of seconds', () => { - const dateHelper = new DateTimeHelper(new Config({timeFormats: ['hh:mm:ss.ss']})) - expect(getRawValue(dateHelper.dateStringToDateNumber('00:00:00.1'))).toBeCloseTo(0.0000011574074074074074) - expect(getRawValue(dateHelper.dateStringToDateNumber('00:00:00.01'))).toBeCloseTo(1.1574074074074073e-7) - expect(getRawValue(dateHelper.dateStringToDateNumber('00:00:00.001'))).toBeCloseTo(0) - }) - - it('#stringToDateNumber am/pm', () => { - const dateHelper = new DateTimeHelper(new Config()) - expect(dateHelper.dateStringToDateNumber('03:00 am')).toEqual(new TimeNumber(0.125, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('03:00 a')).toEqual(new TimeNumber(0.125, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('03:00 pm')).toEqual(new TimeNumber(0.625, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('12:00 pm')).toEqual(new TimeNumber(0.5, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('12:00 p')).toEqual(new TimeNumber(0.5, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('00:00 pm')).toEqual(new TimeNumber(0.5, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('12:59 pm')).toEqual(new TimeNumber(0.5409722222222222, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('12:00 am')).toEqual(new TimeNumber(0.0, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('00:00 am')).toEqual(new TimeNumber(0.0, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('12:59 am')).toEqual(new TimeNumber(0.04097222222222222, 'hh:mm')) - expect(dateHelper.dateStringToDateNumber('13:00 am')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('13:00 pm')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('pm')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('02/02/2020 pm')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('02/02/2020 12:00pm')).toEqual(new DateTimeNumber(43863.5, 'DD/MM/YYYY hh:mm')) - expect(dateHelper.dateStringToDateNumber('02/02/2020 12:9999pm')).toEqual(new DateTimeNumber(43870.44375, 'DD/MM/YYYY hh:mm')) - }) - - it('#stringToDateNumber - tests expected to return not null, dates + times', () => { - const dateHelper = new DateTimeHelper(new Config()) - expect(dateHelper.dateStringToDateNumber('16/08/1985 03:40')).toEqual(new DateTimeNumber(31275.152777777777, 'DD/MM/YYYY hh:mm')) - expect(dateHelper.dateStringToDateNumber(' 31 12 2999 00:00:00 ')).toEqual(new DateTimeNumber(401768, 'DD/MM/YYYY hh:mm:ss.sss')) - }) - - it('#stringToDateNumber - excel compatibility', () => { - const dateHelper = new DateTimeHelper(new Config()) - expect(dateHelper.dateStringToDateNumber('29/02/1900')).toBe(undefined) - const dateHelper2 = new DateTimeHelper(new Config({leapYear1900: true})) - expect(dateHelper2.dateStringToDateNumber('29/02/1900')).toEqual(new DateNumber(61, 'DD/MM/YYYY')) - }) - - it('stringToDateNumber - 00 year parsing', () => { - const dateHelper = new DateTimeHelper(new Config()) - expect(dateHelper.dateStringToDateNumber('16/08/85')).toEqual(new DateNumber(31275, 'DD/MM/YY')) - expect(dateHelper.dateStringToDateNumber('15/01/20')).toEqual(new DateNumber(43845, 'DD/MM/YY')) - expect(dateHelper.dateStringToDateNumber('29/02/00')).toEqual(new DateNumber(36585, 'DD/MM/YY')) - expect(dateHelper.dateStringToDateNumber('31/12/99')).toEqual(new DateNumber(36525, 'DD/MM/YY')) - const dateHelper1 = new DateTimeHelper(new Config({nullYear: 0})) - expect(dateHelper1.dateStringToDateNumber('15/01/20')).toEqual(new DateNumber(7320, 'DD/MM/YY')) - const dateHelper2 = new DateTimeHelper(new Config({nullYear: 100})) - expect(dateHelper2.dateStringToDateNumber('31/12/99')).toEqual(new DateNumber(73050, 'DD/MM/YY')) - }) - - it('stringToDateNumber - other date formats', () => { - const dateHelper = new DateTimeHelper(new Config({dateFormats: ['MM/DD/YYYY']})) - expect(dateHelper.dateStringToDateNumber('12/31/99')).toBe(undefined) - const dateHelper1 = new DateTimeHelper(new Config({dateFormats: ['YY/MM/DD']})) - expect(dateHelper1.dateStringToDateNumber('99/12/31')).toEqual(new DateNumber(36525, 'YY/MM/DD')) - const dateHelper2 = new DateTimeHelper(new Config({dateFormats: ['MM/DD/YY', 'YY/MM/DD']})) - expect(dateHelper2.dateStringToDateNumber('99/12/31')).toEqual(new DateNumber(36525, 'YY/MM/DD')) - const dateHelper3 = new DateTimeHelper(new Config({dateFormats: ['YYYY/DD/MM']})) - expect(dateHelper3.dateStringToDateNumber('1999/12/31')).toBe(undefined) - const dateHelper4 = new DateTimeHelper(new Config({dateFormats: ['YYYY/MM/DD']})) - expect(dateHelper4.dateStringToDateNumber('1999/12/31')).toEqual(new DateNumber(36525, 'YYYY/MM/DD')) - const dateHelper5 = new DateTimeHelper(new Config({dateFormats: ['MM/YYYY/DD']})) - expect(dateHelper5.dateStringToDateNumber('12/1999/31')).toEqual(new DateNumber(36525, 'MM/YYYY/DD')) - const dateHelper6 = new DateTimeHelper(new Config({dateFormats: ['DD/YYYY/MM']})) - expect(dateHelper6.dateStringToDateNumber('31/1999/12')).toEqual(new DateNumber(36525, 'DD/YYYY/MM')) - const dateHelper7 = new DateTimeHelper(new Config({dateFormats: ['DD/MM/YYYY']})) - expect(dateHelper7.dateStringToDateNumber('31/12/1999')).toEqual(new DateNumber(36525, 'DD/MM/YYYY')) - const dateHelper8 = new DateTimeHelper(new Config({dateFormats: ['YY/DD/MM']})) - expect(dateHelper8.dateStringToDateNumber('99/31/12')).toEqual(new DateNumber(36525, 'YY/DD/MM')) - const dateHelper9 = new DateTimeHelper(new Config({dateFormats: ['MM/YY/DD']})) - expect(dateHelper9.dateStringToDateNumber('12/99/31')).toEqual(new DateNumber(36525, 'MM/YY/DD')) - const dateHelper10 = new DateTimeHelper(new Config({dateFormats: ['DD/MM/YY']})) - expect(dateHelper10.dateStringToDateNumber('31/12/99')).toEqual(new DateNumber(36525, 'DD/MM/YY')) - const dateHelper11 = new DateTimeHelper(new Config({dateFormats: ['DD/YY/MM']})) - expect(dateHelper11.dateStringToDateNumber('31/99/12')).toEqual(new DateNumber(36525, 'DD/YY/MM')) - }) - - it('stringToDateNumber - other time formats', () => { - const dateHelper = new DateTimeHelper(new Config({timeFormats: ['mm:hh']})) - expect(dateHelper.dateStringToDateNumber('60:02')).toEqual(new TimeNumber(0.125, 'mm:hh')) - }) - - it('#stringToDateNumber - tests expected to return undefined', () => { - const dateHelper = new DateTimeHelper(new Config()) - expect(dateHelper.dateStringToDateNumber('1/1/10000')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('5/29/1453')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('www')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('0')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('0/0/1999')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('13/13/2020')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('w8')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('www1')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('10/2020')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('12//31/2999')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('12/31/2999 0')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('12//31/2999 0:0')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('12/31/2999 0:0:0:0')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('12:00 12/31/2999')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber(' ')).toBe(undefined) - expect(dateHelper.dateStringToDateNumber('')).toBe(undefined) - }) -}) - -describe('Date helpers, other zero date', () => { - it('#dateToNumber should return number representation of a date, different zero date', () => { - const dateHelper = new DateTimeHelper(new Config({nullDate: {year: 1950, month: 6, day: 15}})) - expect(dateHelper.dateToNumber({year: 1900, month: 1, day: 1})).toBe(-18427) - expect(dateHelper.dateToNumber({year: 1899, month: 12, day: 30})).toBe(-18429) - expect(dateHelper.dateToNumber({year: 1900, month: 12, day: 31})).toBe(-18063) - expect(dateHelper.dateToNumber({year: 2018, month: 12, day: 31})).toBe(25036) - }) - - it('#dateNumberToMonthNumber should return proper month number, different zero date', () => { - const config = new Config({nullDate: {year: 1950, month: 6, day: 15}}) - const dateHelper = new DateTimeHelper(config) - expect(dateHelper.numberToSimpleDate(0).month).toEqual(6) - expect(dateHelper.numberToSimpleDate(2).month).toEqual(6) - expect(dateHelper.numberToSimpleDate(43465).month).toEqual(6) - }) - - it('#stringToDateNumber - tests expected to return not null, different zero date', () => { - const dateHelper = new DateTimeHelper(new Config({nullDate: {year: 1950, month: 6, day: 15}})) - expect(dateHelper.dateStringToDateNumber('16/08/1985')).toEqual(new DateNumber(12846, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber('15/01/2020')).toEqual(new DateNumber(25416, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber('29/02/2000')).toEqual(new DateNumber(18156, 'DD/MM/YYYY')) - expect(dateHelper.dateStringToDateNumber('31/12/2999')).toEqual(new DateNumber(383339, 'DD/MM/YYYY')) - }) -}) - -describe('By default function parseDateTimeFromConfigFormats', () => { - it('returns {} when dateFormats=[] and timeFormats=[]', () => { - const dateHelper = new DateTimeHelper(new Config({ dateFormats: [], timeFormats: [] })) - const parsedDate = dateHelper.parseDateTimeFromConfigFormats('01/01/2019') - expect(parsedDate).toEqual({}) - }) - - it('returns {} when trying to parse date but dateFormats=[]', () => { - const dateHelper = new DateTimeHelper(new Config({ dateFormats: [] })) - const parsedDate = dateHelper.parseDateTimeFromConfigFormats('01/01/2019') - expect(parsedDate).toEqual({}) - }) - - it('returns {} when trying to parse time but timeFormats=[]', () => { - const dateHelper = new DateTimeHelper(new Config({ timeFormats: [] })) - const parsedDate = dateHelper.parseDateTimeFromConfigFormats('01:01') - expect(parsedDate).toEqual({}) - }) - - it('returns {} when time format contains no day term', () => { - const dateHelper = new DateTimeHelper(new Config({ dateFormats: ['MM/YY'] })) - const parsedDate = dateHelper.parseDateTimeFromConfigFormats('12/12') - expect(parsedDate).toEqual({}) - }) - - it('returns {} when time format contains no month term', () => { - const dateHelper = new DateTimeHelper(new Config({ dateFormats: ['DD/YY'] })) - const parsedDate = dateHelper.parseDateTimeFromConfigFormats('12/12') - expect(parsedDate).toEqual({}) - }) - - it('returns {} when time format contains no year term', () => { - const dateHelper = new DateTimeHelper(new Config({ dateFormats: ['DD/MM'] })) - const parsedDate = dateHelper.parseDateTimeFromConfigFormats('12/12') - expect(parsedDate).toEqual({}) - }) - - it('returns {} when time format contains both long year and short year term', () => { - const dateHelper = new DateTimeHelper(new Config({ dateFormats: ['DD/MM/YY/YYYY'] })) - const parsedDate = dateHelper.parseDateTimeFromConfigFormats('12/12/12/12') - expect(parsedDate).toEqual({}) - }) - - it('parses a time value with AM/PM postfix', () => { - const dateHelper = new DateTimeHelper(new Config({ timeFormats: ['hh:mm am/pm'] })) - const { dateTime: dateTimeWithPrefix } = dateHelper.parseDateTimeFromConfigFormats('01:01 pm') - const { dateTime: dateTimeWithOutPrefix } = dateHelper.parseDateTimeFromConfigFormats('13:01') - expect(dateTimeWithPrefix).toEqual(dateTimeWithOutPrefix) - }) - - it('parses a time value with A/P postfix', () => { - const dateHelper = new DateTimeHelper(new Config({ timeFormats: ['hh:mm a/p'] })) - const { dateTime: dateTimeWithPrefix } = dateHelper.parseDateTimeFromConfigFormats('01:01 p') - const { dateTime: dateTimeWithOutPrefix } = dateHelper.parseDateTimeFromConfigFormats('13:01') - expect(dateTimeWithPrefix).toEqual(dateTimeWithOutPrefix) - }) - - it('returns the matching dateFormat', () => { - const dateHelper = new DateTimeHelper(new Config({ dateFormats: ['DD/MM/YY', 'DD/MM/YYYY'], timeFormats: ['hh:mm:ss', 'hh:mm'] })) - const { dateFormat } = dateHelper.parseDateTimeFromConfigFormats('01/01/2019') - expect(dateFormat).toEqual('DD/MM/YYYY') - }) - - it('returns the matching timeFormat', () => { - const dateHelper = new DateTimeHelper(new Config({ dateFormats: ['DD/MM/YY', 'DD/MM/YYYY'], timeFormats: ['hh:mm:ss', 'hh:mm'] })) - const { timeFormat } = dateHelper.parseDateTimeFromConfigFormats('01:01') - expect(timeFormat).toEqual('hh:mm') - }) -}) - -describe('Custom date parsing', () => { - function customParseDate(dateString: string, dateFormat?: string): Maybe { - const momentDate = moment(dateString, dateFormat, true) - if (momentDate.isValid()) { - return {year: momentDate.year(), month: momentDate.month() + 1, day: momentDate.date()} - } - return undefined - } - - it('moment-based custom parsing', () => { - const config = new Config({parseDateTime: customParseDate, dateFormats: ['Do MMM YY', 'DDD YYYY']}) - const dateHelper = new DateTimeHelper(config) - expect(dateHelper.dateStringToDateNumber('31st Jan 00')).toEqual(new DateNumber(36556, 'Do MMM YY')) - expect(dateHelper.dateStringToDateNumber('365 1900')).toEqual(new DateNumber(366, 'DDD YYYY')) - }) -}) diff --git a/test/unit/dependency-graph-sheet-reference-registrar.spec.ts b/test/unit/dependency-graph-sheet-reference-registrar.spec.ts deleted file mode 100644 index 1fe440d6a5..0000000000 --- a/test/unit/dependency-graph-sheet-reference-registrar.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { AlwaysDense } from '../../src' -import {AddressMapping, DenseStrategy, SheetMapping, SheetReferenceRegistrar} from '../../src/DependencyGraph' -import {buildTranslationPackage} from '../../src/i18n' -import {enGB} from '../../src/i18n/languages' - -describe('SheetReferenceRegistrar', () => { - const createDependencies = () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - const addressMapping = new AddressMapping(new AlwaysDense()) - const registrar = new SheetReferenceRegistrar(sheetMapping, addressMapping) - return { sheetMapping, addressMapping, registrar } - } - - it('when called with non-existing sheet, adds a placeholder', () => { - const { sheetMapping, addressMapping, registrar } = createDependencies() - - const sheetId = registrar.ensureSheetRegistered('NewSheet') - - expect(sheetMapping.numberOfSheets({ includePlaceholders: true })).toBe(1) - expect(sheetMapping.hasSheetWithId(sheetId, { includePlaceholders: true })).toBe(true) - expect(() => addressMapping.getStrategyForSheetOrThrow(sheetId)).not.toThrow() - }) - - it('when called with existing placeholder sheet, doesnt modify address mapping nor sheet mapping', () => { - const { sheetMapping, addressMapping, registrar } = createDependencies() - const firstSheetId = registrar.ensureSheetRegistered('PlaceholderSheet') - - const secondSheetId = registrar.ensureSheetRegistered('PlaceholderSheet') - - expect(secondSheetId).toBe(firstSheetId) - expect(sheetMapping.numberOfSheets({ includePlaceholders: true })).toBe(1) - expect(() => addressMapping.getStrategyForSheetOrThrow(firstSheetId)).not.toThrow() - }) - - it('when called with existing real sheet, doesnt modify address mapping nor sheet mapping', () => { - const { sheetMapping, addressMapping, registrar } = createDependencies() - const realSheetId = sheetMapping.addSheet('RealSheet') - addressMapping.addSheetWithStrategy(realSheetId, new DenseStrategy(0, 0)) - - const returnedSheetId = registrar.ensureSheetRegistered('RealSheet') - - expect(returnedSheetId).toBe(realSheetId) - expect(sheetMapping.numberOfSheets({ includePlaceholders: true })).toBe(1) - expect(sheetMapping.numberOfSheets({ includePlaceholders: false })).toBe(1) - expect(() => addressMapping.getStrategyForSheetOrThrow(realSheetId)).not.toThrow() - }) - - it('when called with name that differs from some placeholder sheet only by case, doesnt modify address mapping nor sheet mapping', () => { - const { sheetMapping, addressMapping, registrar } = createDependencies() - const firstSheetId = registrar.ensureSheetRegistered('PlaceholderSheet') - - const secondSheetId = registrar.ensureSheetRegistered('PLACEHOLDERSHEET') - - expect(secondSheetId).toBe(firstSheetId) - expect(secondSheetId).toBe(firstSheetId) - expect(sheetMapping.numberOfSheets({ includePlaceholders: true })).toBe(1) - expect(() => addressMapping.getStrategyForSheetOrThrow(firstSheetId)).not.toThrow() - }) - - it('when called with name that differs from some real sheet only by case, doesnt modify address mapping nor sheet mapping', () => { - const { sheetMapping, addressMapping, registrar } = createDependencies() - const realSheetId = sheetMapping.addSheet('RealSheet') - addressMapping.addSheetWithStrategy(realSheetId, new DenseStrategy(0, 0)) - - const returnedSheetId = registrar.ensureSheetRegistered('REALSHEET') - - expect(returnedSheetId).toBe(realSheetId) - expect(sheetMapping.numberOfSheets({ includePlaceholders: true })).toBe(1) - expect(sheetMapping.numberOfSheets({ includePlaceholders: false })).toBe(1) - expect(() => addressMapping.getStrategyForSheetOrThrow(realSheetId)).not.toThrow() - }) -}) diff --git a/test/unit/dependencyTransformers/clean-out-of-scope-dependencies-transformer.spec.ts b/test/unit/dependencyTransformers/clean-out-of-scope-dependencies-transformer.spec.ts deleted file mode 100644 index 2fc4393ee5..0000000000 --- a/test/unit/dependencyTransformers/clean-out-of-scope-dependencies-transformer.spec.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { CleanOutOfScopeDependenciesTransformer } from '../../../src/dependencyTransformers/CleanOutOfScopeDependenciesTransformer' - -describe('CleanOutOfScopeDependenciesTransformer', () => { - - it('isIrreversible always returns true', () => { - const transformer = new CleanOutOfScopeDependenciesTransformer(5) - expect(transformer.isIrreversible()).toEqual(true) - }) - -}) diff --git a/test/unit/dependencyTransformers/combined-transformer.spec.ts b/test/unit/dependencyTransformers/combined-transformer.spec.ts deleted file mode 100644 index d0afcdded9..0000000000 --- a/test/unit/dependencyTransformers/combined-transformer.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { adr } from '../testUtils' -import { Config } from '../../../src/Config' -import { CombinedTransformer } from '../../../src/dependencyTransformers/CombinedTransformer' -import { HyperFormula } from '../../../src' -import { buildEmptyParserWithCaching } from '../parser/common' -import { AbsoluteCellRange } from '../../../src/AbsoluteCellRange' - -describe('CombinedTransformer', () => { - - it('performEagerTransformations() coverage', () => { - const transformer = new CombinedTransformer(0) - const parser = buildEmptyParserWithCaching(new Config()) - - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'] - ]) - - transformer.performEagerTransformations(engine.dependencyGraph, parser) - - expect(engine.getRangeValues(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2))).toEqual([ - [1, 2], - [3, 4], - ]) - - expect(transformer.isIrreversible()).toEqual(true) - }) - -}) diff --git a/test/unit/dependencyTransformers/remove-columns-transformer.spec.ts b/test/unit/dependencyTransformers/remove-columns-transformer.spec.ts deleted file mode 100644 index aceea687e3..0000000000 --- a/test/unit/dependencyTransformers/remove-columns-transformer.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { simpleCellAddress } from '../../../src/Cell' -import { Config } from '../../../src/Config' -import { CellRangeAst } from '../../../src/parser' -import { buildEmptyParserWithCaching } from '../parser/common' -import { adr } from '../testUtils' -import { ParenthesisAst, ProcedureAst } from '../../../src/parser/Ast' -import { ColumnsSpan } from '../../../src/Span' -import { RemoveColumnsTransformer } from '../../../src/dependencyTransformers/RemoveColumnsTransformer' - -describe('RemoveColumnsTransformer', () => { - - it('transformColRange error - removing cols from left of range throws error', () => { - const transformer = new RemoveColumnsTransformer(new ColumnsSpan(0, 5, 5)) - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=(SUM(C2:F7))', adr('A1', 1)).ast - - { - const astParen = ast as ParenthesisAst - const astSum = astParen.expression as ProcedureAst - const astSumArg = astSum.args[0] as CellRangeAst - expect(astSumArg.start.sheet).toBeUndefined() - expect(astSumArg.start.col).toEqual(2) - expect(astSumArg.start.row).toEqual(1) - expect(astSumArg.end.sheet).toBeUndefined() - expect(astSumArg.end.col).toEqual(5) - expect(astSumArg.end.row).toEqual(6) - } - - expect(() => { - transformer.transformSingleAst(ast, simpleCellAddress(0, 5, 5)) - }).toThrowError('Cannot happen') - - }) - -}) diff --git a/test/unit/dependencyTransformers/remove-rows-transformer.spec.ts b/test/unit/dependencyTransformers/remove-rows-transformer.spec.ts deleted file mode 100644 index 94a56f703b..0000000000 --- a/test/unit/dependencyTransformers/remove-rows-transformer.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { ErrorType, simpleCellAddress } from '../../../src/Cell' -import { Config } from '../../../src/Config' -import { CellRangeAst } from '../../../src/parser' -import { buildEmptyParserWithCaching } from '../parser/common' -import { adr } from '../testUtils' -import { ParenthesisAst, ProcedureAst, ErrorAst } from '../../../src/parser/Ast' -import { RowsSpan } from '../../../src/Span' -import { RemoveRowsTransformer } from '../../../src/dependencyTransformers/RemoveRowsTransformer' - -describe('RemoveRowsTransformer', () => { - - it('transformRowRange error - removing rows from top of range', () => { - const transformer = new RemoveRowsTransformer(new RowsSpan(0, 0, 2)) - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=(SUM(C2:F7))', adr('A1', 1)).ast - - { - const astParen = ast as ParenthesisAst - const astSum = astParen.expression as ProcedureAst - const astSumArg = astSum.args[0] as CellRangeAst - expect(astSumArg.start.sheet).toBeUndefined() - expect(astSumArg.start.col).toEqual(2) - expect(astSumArg.start.row).toEqual(1) - expect(astSumArg.end.sheet).toBeUndefined() - expect(astSumArg.end.col).toEqual(5) - expect(astSumArg.end.row).toEqual(6) - } - - const [transformedAst, cellAddress] = transformer.transformSingleAst(ast, simpleCellAddress(0, 0, 0)) - - { - const astParen = transformedAst as ParenthesisAst - const astSum = astParen.expression as ProcedureAst - const astSumError = astSum.args[0] as ErrorAst - expect(astSumError.type).toEqual(ErrorType.ERROR) - expect(astSumError.error.type).toEqual(ErrorType.REF) - expect(cellAddress.sheet).toEqual(0) - expect(cellAddress.col).toEqual(0) - expect(cellAddress.row).toEqual(-3) - } - - }) - -}) diff --git a/test/unit/dependencyTransformers/transformer.spec.ts b/test/unit/dependencyTransformers/transformer.spec.ts deleted file mode 100644 index 4c93e406ac..0000000000 --- a/test/unit/dependencyTransformers/transformer.spec.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { ErrorType, simpleCellAddress, SimpleCellAddress } from '../../../src/Cell' -import { Config } from '../../../src/Config' -import { CellAddress, CellRangeAst } from '../../../src/parser' -import { ColumnAddress } from '../../../src/parser/ColumnAddress' -import { RowAddress } from '../../../src/parser/RowAddress' -import { Transformer } from '../../../src/dependencyTransformers/Transformer' -import { buildEmptyParserWithCaching } from '../parser/common' -import { adr } from '../testUtils' -import { ParenthesisAst, ProcedureAst, ArrayAst } from '../../../src/parser/Ast' - -describe('Transformer', () => { - - /** - * A mock Transformer subclass that can be used to test the methods implemented in the partially abstract Transform class - */ - class MockTransformer extends Transformer { - constructor() { - super() - } - public get sheet(): number { - return 0 - } - public isIrreversible(): boolean { - return false - } - protected transformCellAddress(dependencyAddress: T, formulaAddress: SimpleCellAddress): T | ErrorType.REF | false { - return ErrorType.REF - } - protected transformCellRange(start: CellAddress, end: CellAddress, formulaAddress: SimpleCellAddress): [CellAddress, CellAddress] | ErrorType.REF | false { - const newStart = new CellAddress(15, 10, start.type, 19) - const newEnd = new CellAddress(20, 30, start.type, 19) - return [newStart, newEnd] - } - protected transformRowRange(start: RowAddress, end: RowAddress, formulaAddress: SimpleCellAddress): [RowAddress, RowAddress] | ErrorType.REF | false { - return ErrorType.REF - } - protected transformColumnRange(start: ColumnAddress, end: ColumnAddress, formulaAddress: SimpleCellAddress): [ColumnAddress, ColumnAddress] | ErrorType.REF | false { - return ErrorType.REF - } - protected fixNodeAddress(address: SimpleCellAddress): SimpleCellAddress { - return simpleCellAddress(19, 12, 50) - } - } - - it('transformSingleAst - type PARENTHESIS', () => { - const transformer = new MockTransformer() - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=(SUM(C2:F7))', adr('A1')).ast - - { - const astParen = ast as ParenthesisAst - const astSum = astParen.expression as ProcedureAst - const astSumArg = astSum.args[0] as CellRangeAst - expect(astSumArg.start.sheet).toBeUndefined() - expect(astSumArg.start.col).toEqual(2) - expect(astSumArg.start.row).toEqual(1) - expect(astSumArg.end.sheet).toBeUndefined() - expect(astSumArg.end.col).toEqual(5) - expect(astSumArg.end.row).toEqual(6) - } - - const [transformedAst, cellAddress] = transformer.transformSingleAst(ast, simpleCellAddress(0, 2, 2)) - - { - const astParen = transformedAst as ParenthesisAst - const astSum = astParen.expression as ProcedureAst - const astSumArg = astSum.args[0] as CellRangeAst - expect(astSumArg.start.sheet).toEqual(19) - expect(astSumArg.start.col).toEqual(15) - expect(astSumArg.start.row).toEqual(10) - expect(astSumArg.end.sheet).toEqual(19) - expect(astSumArg.end.col).toEqual(20) - expect(astSumArg.end.row).toEqual(30) - expect(cellAddress.sheet).toEqual(19) - expect(cellAddress.col).toEqual(12) - expect(cellAddress.row).toEqual(50) - } - - }) - - it('transformSingleAst - type ARRAY', () => { - const transformer = new MockTransformer() - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('={SUM(C2:F7)}', adr('A1')).ast - - { - const astArray = ast as ArrayAst - const astSum = astArray.args[0][0] as ProcedureAst - const astSumArg = astSum.args[0] as CellRangeAst - expect(astSumArg.start.sheet).toBeUndefined() - expect(astSumArg.start.col).toEqual(2) - expect(astSumArg.start.row).toEqual(1) - expect(astSumArg.end.sheet).toBeUndefined() - expect(astSumArg.end.col).toEqual(5) - expect(astSumArg.end.row).toEqual(6) - } - - const [transformedAst, cellAddress] = transformer.transformSingleAst(ast, simpleCellAddress(0, 2, 2)) - - { - const astArray = transformedAst as ArrayAst - const astSum = astArray.args[0][0] as ProcedureAst - const astSumArg = astSum.args[0] as CellRangeAst - expect(astSumArg.start.sheet).toEqual(19) - expect(astSumArg.start.col).toEqual(15) - expect(astSumArg.start.row).toEqual(10) - expect(astSumArg.end.sheet).toEqual(19) - expect(astSumArg.end.col).toEqual(20) - expect(astSumArg.end.row).toEqual(30) - expect(cellAddress.sheet).toEqual(19) - expect(cellAddress.col).toEqual(12) - expect(cellAddress.row).toEqual(50) - } - - }) - -}) diff --git a/test/unit/dependent-formula-transformer.spec.ts b/test/unit/dependent-formula-transformer.spec.ts deleted file mode 100644 index be1e59b970..0000000000 --- a/test/unit/dependent-formula-transformer.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {AbsoluteCellRange} from '../../src/AbsoluteCellRange' -import {adr} from './testUtils' -import {DependentFormulaTransformer} from '../../src/dependencyTransformers/MoveCellsTransformer' - -describe('DependentFormulaTransformer', () => { - it('basic functionality', () => { - const sheetId = 99 - const sourceRange = AbsoluteCellRange.spanFrom(adr('A1', sheetId), 1, 1) - const transformer = new DependentFormulaTransformer(sourceRange, 1, 1, 0) - - expect(transformer.sheet).toBe(sheetId) - expect(transformer.isIrreversible()).toBe(true) - }) -}) diff --git a/test/unit/destruct.spec.ts b/test/unit/destruct.spec.ts deleted file mode 100644 index 13db9dcfbb..0000000000 --- a/test/unit/destruct.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {HyperFormula} from '../../src' - -describe('engine destruct', () => { - it('should throw exception after destruct', () => { - const engine = HyperFormula.buildEmpty() - engine.destroy() - expect(() => engine.getConfig()).toThrow() - }) - - it('should have keys removed', () => { - const engine = HyperFormula.buildEmpty() - engine.destroy() - expect(engine?.dependencyGraph).toBeUndefined() - }) - - it('should not affect other instances', () => { - const engine1 = HyperFormula.buildEmpty() - const engine2 = HyperFormula.buildEmpty() - engine1.destroy() - const engine3 = HyperFormula.buildEmpty() - expect(() => engine2.getConfig()).not.toThrow() - expect(() => engine3.getConfig()).not.toThrow() - }) - - it('should not affect static methods', () => { - const engine1 = HyperFormula.buildEmpty() - engine1.destroy() - expect(() => HyperFormula.getAllFunctionPlugins()).not.toThrow() - }) -}) diff --git a/test/unit/emitting-events.spec.ts b/test/unit/emitting-events.spec.ts deleted file mode 100644 index 6db2665da3..0000000000 --- a/test/unit/emitting-events.spec.ts +++ /dev/null @@ -1,237 +0,0 @@ -import { - ExportedCellChange, - ExportedNamedExpressionChange, - HyperFormula, - ErrorType, - NamedExpressionDoesNotExistError, -} from '../../src' -import {Events} from '../../src/Emitter' -import { ErrorMessage } from '../../src/error-message' - -import {adr, detailedErrorWithOrigin} from './testUtils' - -describe('Events', () => { - it('sheetAdded works', function() { - const engine = HyperFormula.buildEmpty() - const handler = jasmine.createSpy() - - engine.on(Events.SheetAdded, handler) - engine.addSheet('FooBar') - - expect(handler).toHaveBeenCalledTimes(1) - expect(handler).toHaveBeenCalledWith('FooBar') - }) - - it('sheetRemoved works', function() { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [['=Sheet2!A1']], - Sheet2: [['42']], - }) - const handler = jasmine.createSpy() - - engine.on(Events.SheetRemoved, handler) - engine.removeSheet(1) - - expect(handler).toHaveBeenCalledTimes(1) - expect(handler).toHaveBeenCalledWith('Sheet2', [new ExportedCellChange(adr('A1'), detailedErrorWithOrigin(ErrorType.REF, 'Sheet1!A1', ErrorMessage.SheetRef))]) - }) - - it('sheetRemoved name contains actual display name', function() { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [['=Sheet2!A1']], - Sheet2: [['42']], - }) - const handler = jasmine.createSpy() - - engine.on(Events.SheetRemoved, handler) - engine.removeSheet(1) - - expect(handler).toHaveBeenCalledTimes(1) - expect(handler).toHaveBeenCalledWith('Sheet2', [new ExportedCellChange(adr('A1'), detailedErrorWithOrigin(ErrorType.REF, 'Sheet1!A1', ErrorMessage.SheetRef))]) - }) - - it('sheetRenamed works', () => { - const engine = HyperFormula.buildFromArray([[]]) - const handler = jasmine.createSpy() - - engine.on(Events.SheetRenamed, handler) - engine.renameSheet(0, 'SomeNewName') - - expect(handler).toHaveBeenCalledTimes(1) - expect(handler).toHaveBeenCalledWith('Sheet1', 'SomeNewName') - }) - - it('sheetRenamed is not triggered when sheet didnt change', () => { - const engine = HyperFormula.buildFromArray([[]]) - const handler = jasmine.createSpy() - - engine.on(Events.SheetRenamed, handler) - engine.renameSheet(0, 'Sheet1') - - expect(handler).not.toHaveBeenCalled() - }) - - it('namedExpressionAdded works', () => { - const engine = HyperFormula.buildEmpty() - const handler = jasmine.createSpy() - - engine.on(Events.NamedExpressionAdded, handler) - engine.addNamedExpression('myName', 'foobarbaz') - - expect(handler).toHaveBeenCalledTimes(1) - expect(handler).toHaveBeenCalledWith('myName', [new ExportedNamedExpressionChange('myName', 'foobarbaz')]) - }) - - it('namedExpressionRemoved works', () => { - const engine = HyperFormula.buildEmpty() - engine.addNamedExpression('myName', 'foobarbaz') - const handler = jasmine.createSpy() - - engine.on(Events.NamedExpressionRemoved, handler) - engine.removeNamedExpression('myName') - - expect(handler).toHaveBeenCalledTimes(1) - expect(handler).toHaveBeenCalledWith('myName', []) - }) - - it('namedExpressionRemoved throws error when named expression not exists', () => { - const engine = HyperFormula.buildEmpty() - const handler = jasmine.createSpy() - - engine.on(Events.NamedExpressionRemoved, handler) - expect(() => { - engine.removeNamedExpression('myName') - }).toThrow(new NamedExpressionDoesNotExistError('myName')) - }) - - it('namedExpressionRemoved contains an actual named-expression name', () => { - const engine = HyperFormula.buildEmpty() - engine.addNamedExpression('myName', 'foobarbaz') - const handler = jasmine.createSpy() - - engine.on(Events.NamedExpressionRemoved, handler) - engine.removeNamedExpression('MYNAME') - - expect(handler).toHaveBeenCalledTimes(1) - expect(handler).toHaveBeenCalledWith('myName', []) - }) - - it('valuesUpdated works', () => { - const engine = HyperFormula.buildFromArray([ - ['42'] - ]) - const handler = jasmine.createSpy() - - engine.on(Events.ValuesUpdated, handler) - engine.setCellContents(adr('A1'), [['43']]) - - expect(handler).toHaveBeenCalledTimes(1) - expect(handler).toHaveBeenCalledWith([new ExportedCellChange(adr('A1'), 43)]) - }) - - it('valuesUpdated works with named expressions', () => { - const engine = HyperFormula.buildFromArray( - [['42']], - {}, - [{ name: 'NAMED_EXPR', expression: '=Sheet1!$A$1' }] - ) - const handler = jasmine.createSpy() - - engine.on(Events.ValuesUpdated, handler) - engine.setCellContents(adr('A1'), [['43']]) - - expect(handler).toHaveBeenCalledTimes(1) - expect(handler).toHaveBeenCalledWith([ - new ExportedCellChange(adr('A1'), 43), - new ExportedNamedExpressionChange('NAMED_EXPR', 43), - ]) - }) - - it('valuesUpdated may sometimes be triggered even if nothing changed', () => { - const engine = HyperFormula.buildFromArray([ - ['42'] - ]) - const handler = jasmine.createSpy() - - engine.on(Events.ValuesUpdated, handler) - engine.setCellContents(adr('A1'), [['42']]) - - expect(handler).toHaveBeenCalledTimes(1) - expect(handler).toHaveBeenCalledWith([new ExportedCellChange(adr('A1'), 42)]) - }) - - it('suspension and resuming of evaluation', () => { - const engine = HyperFormula.buildFromArray([ - ['42'] - ]) - const handlerUpdated = jasmine.createSpy() - const handlerSuspended = jasmine.createSpy() - const handlerResumed = jasmine.createSpy() - - engine.on(Events.ValuesUpdated, handlerUpdated) - engine.on(Events.EvaluationSuspended, handlerSuspended) - engine.on(Events.EvaluationResumed, handlerResumed) - - engine.suspendEvaluation() - expect(handlerUpdated).not.toHaveBeenCalled() - expect(handlerSuspended).toHaveBeenCalledTimes(1) - expect(handlerResumed).not.toHaveBeenCalled() - - engine.setCellContents(adr('A1'), [['13']]) - expect(handlerUpdated).not.toHaveBeenCalled() - expect(handlerSuspended).toHaveBeenCalledTimes(1) - expect(handlerResumed).not.toHaveBeenCalled() - - engine.resumeEvaluation() - expect(handlerUpdated).toHaveBeenCalledTimes(1) - expect(handlerSuspended).toHaveBeenCalledTimes(1) - expect(handlerResumed).toHaveBeenCalledTimes(1) - expect(handlerResumed).toHaveBeenCalledWith([new ExportedCellChange(adr('A1'), 13)]) - }) - - it('batching', () => { - const engine = HyperFormula.buildFromArray([ - ['42'] - ]) - const handlerUpdated = jasmine.createSpy() - const handlerSuspended = jasmine.createSpy() - const handlerResumed = jasmine.createSpy() - - engine.on(Events.ValuesUpdated, handlerUpdated) - engine.on(Events.EvaluationSuspended, handlerSuspended) - engine.on(Events.EvaluationResumed, handlerResumed) - - engine.batch(() => engine.setCellContents(adr('A1'), [['13']])) - expect(handlerUpdated).toHaveBeenCalledTimes(1) - expect(handlerSuspended).toHaveBeenCalledTimes(1) - expect(handlerResumed).toHaveBeenCalledTimes(1) - expect(handlerResumed).toHaveBeenCalledWith([new ExportedCellChange(adr('A1'), 13)]) - }) -}) - -describe('Subscribing only once', () => { - it('works', function() { - const engine = HyperFormula.buildEmpty() - const handler = jasmine.createSpy() - - engine.once(Events.SheetAdded, handler) - engine.addSheet('FooBar1') - engine.addSheet('FooBar2') - - expect(handler).toHaveBeenCalledTimes(1) - }) -}) - -describe('Unsubscribing', () => { - it('works', function() { - const engine = HyperFormula.buildEmpty() - const handler = jasmine.createSpy() - engine.on(Events.SheetAdded, handler) - engine.addSheet('FooBar1') - - engine.off(Events.SheetAdded, handler) - engine.addSheet('FooBar2') - - expect(handler).toHaveBeenCalledTimes(1) - }) -}) diff --git a/test/unit/engine.spec.ts b/test/unit/engine.spec.ts deleted file mode 100644 index cc3464d02a..0000000000 --- a/test/unit/engine.spec.ts +++ /dev/null @@ -1,1252 +0,0 @@ -import {DetailedCellError, ErrorType, HyperFormula, CellType, CellValueDetailedType, CellValueType, SimpleCellAddress, SimpleCellRange, ExpectedValueOfTypeError} from '../../src' -import {AbsoluteCellRange, simpleCellRange} from '../../src/AbsoluteCellRange' -import {Config} from '../../src/Config' -import {ErrorMessage} from '../../src/error-message' -import {plPL} from '../../src/i18n/languages' -import {adr, detailedError, expectArrayWithSameContent} from './testUtils' -import {simpleCellAddress} from '../../src/Cell' - -describe('#buildFromArray', () => { - it('load single value', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - }) - - it('load simple sheet', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ]) - - expect(engine.getCellValue(adr('C2'))).toBe(6) - }) - - it('evaluate empty vertex', () => { - const engine = HyperFormula.buildFromArray([['=A5']]) - - expect(engine.getCellValue(adr('A5'))).toBe(null) - expect(engine.getCellValue(adr('A1'))).toBe(null) - }) - - it('evaluate empty vertex reference', () => { - const engine = HyperFormula.buildFromArray([[null, '=A1']]) - - expect(engine.getCellValue(adr('B1'))).toBe(null) - }) - - it('cycle', () => { - const engine = HyperFormula.buildFromArray([['=B1', '=C1', '=A1']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.CYCLE)) - }) - - it('cycle with formula', () => { - const engine = HyperFormula.buildFromArray([['5', '=A1+B1']]) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.CYCLE)) - }) - - it('operator precedence', () => { - const engine = HyperFormula.buildFromArray([['=3*7*2-4*1+2']]) - expect(engine.getCellValue(adr('A1'))).toBe(40) - }) - - it('operator precedence and brackets', () => { - const engine = HyperFormula.buildFromArray([['=3*7+((2-4)*(1+2)+3)*2']]) - expect(engine.getCellValue(adr('A1'))).toBe(15) - }) - - it('operator precedence with cells', () => { - const engine = HyperFormula.buildFromArray([['3', '4', '=B1*2+A1']]) - expect(engine.getCellValue(adr('C1'))).toBe(11) - }) - - it('parsing error', () => { - const engine = HyperFormula.buildFromArray([['=1A1']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - }) - - it('dependency before value', () => { - const engine = HyperFormula.buildFromArray([ - ['=B1', '1', '2'], - ['=SUM(B2:C2)', '1', '2'], - ]) - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('A2'))).toBe(3) - }) - - it('should handle different input types', () => { - const engine = HyperFormula.buildFromArray([['', null, undefined, 1, true]]) - - expect(engine.getCellValue(adr('A1'))).toEqual('') - expect(engine.getCellValue(adr('B1'))).toBe(null) - expect(engine.getCellValue(adr('C1'))).toBe(null) - expect(engine.getCellValue(adr('D1'))).toBe(1) - expect(engine.getCellValue(adr('E1'))).toBe(true) - }) - - it('should work with other numerals', () => { - const engine = HyperFormula.buildFromArray([ - [0o777, 0xFF, 0b1010, 1_000_000_000_000], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(511) - expect(engine.getCellValue(adr('B1'))).toBe(255) - expect(engine.getCellValue(adr('C1'))).toBe(10) - expect(engine.getCellValue(adr('D1'))).toBe(1000000000000) - }) - - it('should be possible to build graph with reference to not existing sheet', () => { - const engine = HyperFormula.buildFromArray([['=Sheet2!A2']]) - - expect(engine.getCellFormula(adr('A1'))).toEqual('=Sheet2!A2') - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should propagate parsing errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(', '=A1'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - expect(engine.getCellFormula(adr('A1'))).toEqual('=SUM(') - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - expect(engine.getCellFormula(adr('B1'))).toEqual('=A1') - }) -}) - -describe('#getCellHyperlink', () => { - it('returns hyperlink url when present', () => { - const url = 'https://hyperformula.handsontable.com/' - const linkLabel = 'HyperFormula' - const engine = HyperFormula.buildFromArray([[`=HYPERLINK("${url}","${linkLabel}")`]]) - expect(engine.getCellHyperlink(adr('A1'))).toEqual(url) - }) - - it('returns undefined when url not present', () => { - const engine = HyperFormula.buildFromArray([['=HYPERLINK()', '5', 's1', null]]) - expect(engine.getCellHyperlink(adr('A1'))).toEqual(undefined) - expect(engine.getCellHyperlink(adr('B1'))).toEqual(undefined) - expect(engine.getCellHyperlink(adr('C1'))).toEqual(undefined) - expect(engine.getCellHyperlink(adr('D1'))).toEqual(undefined) - }) - - it('returns undefined when HYPERLINK not the root expression', () => { - const url = 'https://hyperformula.handsontable.com/' - const linkLabel = 'HyperFormula' - const prefix = 'Prefix: ' - const engine = HyperFormula.buildFromArray([[`=CONCATENATE("${prefix}",HYPERLINK("${url}","${linkLabel}"))`]]) - expect(engine.getCellHyperlink(adr('A1'))).toEqual(undefined) - }) - - it('returns hyperlink when HYPERLINK does not use strings directly', () => { - const url = 'https://hyperformula.handsontable.com/' - const linkLabel = 'HyperFormula' - const engine = HyperFormula.buildFromArray([[url, linkLabel, '=HYPERLINK(A1,B1)']]) - expect(engine.getCellHyperlink(adr('C1'))).toEqual(url) - }) - - it('returns hyperlink when HYPERLINK uses complex params', () => { - const url = 'https://hyperformula.handsontable.com/' - const linkLabel = 'HyperFormula' - const engine = HyperFormula.buildFromArray([[url, linkLabel, '=HYPERLINK(INDEX(A:A,ROW()),B1)']]) - expect(engine.getCellHyperlink(adr('C1'))).toEqual(url) - }) - - it('should throw error if cellAddress is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildEmpty() - expect(() => { - engine.getCellHyperlink({} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress')) - }) -}) - -describe('#getCellFormula', () => { - it('returns formula when present', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(1,2,3,C3)'], - ]) - - expect(engine.getCellFormula(adr('A1'))).toEqual('=SUM(1,2,3,C3)') - }) - - it('works with -0', () => { - const engine = HyperFormula.buildFromArray([ - ['=-0'], - ]) - - expect(engine.getCellFormula(adr('A1'))).toEqual('=-0') - }) - - it('returns undefined for simple values', () => { - const engine = HyperFormula.buildFromArray([ - [''], - ['42'], - ['foobar'], - ]) - - expect(engine.getCellFormula(adr('A1'))).toEqual(undefined) - expect(engine.getCellFormula(adr('A2'))).toEqual(undefined) - expect(engine.getCellFormula(adr('A3'))).toEqual(undefined) - expect(engine.getCellFormula(adr('A4'))).toEqual(undefined) - }) - - it('returns matrix formula for matrix vertices', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1'], - ['1', '1'], - ['=MMULT(A1:B2,A1:B2)'], - ]) - - expect(engine.getCellFormula(adr('A3'))).toEqual('=MMULT(A1:B2,A1:B2)') - expect(engine.getCellFormula(adr('A4'))).toEqual(undefined) - expect(engine.getCellFormula(adr('B3'))).toEqual(undefined) - expect(engine.getCellFormula(adr('B4'))).toEqual(undefined) - }) - - it('returns invalid formula literal', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM('] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - expect(engine.getCellFormula(adr('A1'))).toEqual('=SUM(') - }) - - it('returns invalid matrix formula literal', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRANSPOSE('] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - expect(engine.getCellFormula(adr('A1'))).toEqual('=TRANSPOSE(') - }) - - it('should throw error if cellAddress is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildEmpty() - expect(() => { - engine.getCellFormula({} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress')) - }) -}) - -describe('#getAllFormulas', () => { - it('should return formulas from all sheets', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [['=A()']], - Foo: [[1, '=SUM(A1)']], - }) - - expect(engine.getAllSheetsFormulas()).toEqual({'Foo': [[undefined, '=SUM(A1)']], 'Sheet1': [['=A()']]}) - }) -}) - -describe('#getRangeFormulas', () => { - it('should return formulas', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(1, A2)', '=TRUE()'], - ['=SUM(', null, 1] - ]) - - const out = engine.getRangeFormulas(AbsoluteCellRange.spanFrom(adr('A1'), 3, 2)) - - expectArrayWithSameContent([['=SUM(1, A2)', '=TRUE()', undefined], ['=SUM(', undefined, undefined]], out) - }) - - it('should throw error if source is a malformed SimpleCellRange', () => { - const engine = HyperFormula.buildFromArray([]) - expect(() => { - engine.getRangeFormulas({} as SimpleCellRange) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellRange', 'source')) - }) -}) - -describe('#getSheetFormulas', () => { - it('should return formulas from sheet', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(1, A2)', '=TRUE()'], - ['=SUM(', null, 1] - ]) - - const out = engine.getSheetFormulas(0) - - expectArrayWithSameContent([['=SUM(1, A2)', '=TRUE()'], ['=SUM(']], out) - }) -}) - -describe('#getCellValue', () => { - it('should return simple value', () => { - const engine = HyperFormula.buildFromArray([ - ['', 1, '1', 'foo', true, -1.000000000000001] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('') - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('C1'))).toEqual(1) - expect(engine.getCellValue(adr('D1'))).toEqual('foo') - expect(engine.getCellValue(adr('E1'))).toEqual(true) - expect(engine.getCellValue(adr('F1'))).toEqual(-1) - }) - - it('should return null for empty cells', () => { - const engine = HyperFormula.buildFromArray([ - [null, undefined] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(null) - expect(engine.getCellValue(adr('B1'))).toEqual(null) - expect(engine.getCellValue(adr('C1'))).toEqual(null) - }) - - it('should return value of a formula', () => { - const engine = HyperFormula.buildFromArray([ - ['=1', '=SUM(1, A1)', '=TRUE()', '=1/0'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('B1'))).toEqual(2) - expect(engine.getCellValue(adr('C1'))).toEqual(true) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should return parsing error value', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM('] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - }) - - it('should return value of a cell in a formula matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - }) - - it('should return translated error', () => { - HyperFormula.registerLanguage('plPL', plPL) - const engine = HyperFormula.buildFromArray([ - ['=#ARG!'], - ], {language: 'plPL'}) - - const error = engine.getCellValue(adr('A1')) as DetailedCellError - expect(error).toEqualError(detailedError(ErrorType.VALUE, '', new Config({language: 'plPL'}))) - expect(error.value).toEqual('#ARG!') - }) - - it('should throw error if cellAddress is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildEmpty() - expect(() => { - engine.getCellValue({} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress')) - }) -}) - -describe('#getSheetDimensions', () => { - it('should work for empty sheet', () => { - const engine = HyperFormula.buildFromArray([]) - - expect(engine.getSheetDimensions(0)).toEqual({height: 0, width: 0}) - }) - - it('should return sheet dimensions', () => { - const engine = HyperFormula.buildFromArray([ - [1, 1], - [1, null, 1], - ]) - - expect(engine.getSheetDimensions(0)).toEqual({height: 2, width: 3}) - }) -}) - -describe('#getAllSheetsDimensions', () => { - it('should return dimension of all sheets', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [], - 'Sheet2': [[1]], - 'Foo': [[null]], - 'Bar': [[null], [null, 'foo']] - }) - - expect(engine.getAllSheetsDimensions()).toEqual({ - 'Sheet1': {width: 0, height: 0}, - 'Sheet2': {width: 1, height: 1}, - 'Foo': {width: 0, height: 0}, - 'Bar': {width: 2, height: 2}, - }) - }) -}) - -describe('#getRangeValues', () => { - it('should return values from range', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(1, B1)', '=TRUE()', null] - ]) - - const out = engine.getRangeValues(AbsoluteCellRange.spanFrom(adr('A1'), 3, 1)) - - expectArrayWithSameContent([[1, true, null]], out) - }) - - it('should throw error if source is a malformed SimpleCellRange', () => { - const engine = HyperFormula.buildFromArray([]) - expect(() => { - engine.getRangeValues({} as SimpleCellRange) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellRange', 'source')) - }) -}) - -describe('#getSheetValues', () => { - it('should return values from sheet', () => { - const engine = HyperFormula.buildFromArray([ - [1, 'foo', '=SUM(1, A1)', null, '=TRUE()', null] - ]) - - const out = engine.getSheetValues(0) - - expectArrayWithSameContent([[1, 'foo', 2, null, true]], out) - }) -}) - -describe('#getAllValues', () => { - it('should return values from all sheets', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [], - Foo: [[1]], - }) - - expect(engine.getAllSheetsValues()).toEqual({'Foo': [[1]], 'Sheet1': []}) - }) -}) - -describe('#getCellSerialized', () => { - it('should return formula for formula vertex', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(1, A2)'] - ]) - - expect(engine.getCellSerialized(adr('A1'))).toEqual('=SUM(1, A2)') - }) - - it('should return formula for parsing error', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM('] - ]) - - expect(engine.getCellSerialized(adr('A1'))).toEqual('=SUM(') - }) - - it('should return simple value', () => { - const engine = HyperFormula.buildFromArray([ - [1, '2', 'foo', true] - ]) - - expect(engine.getCellSerialized(adr('A1'))).toEqual(1) - expect(engine.getCellSerialized(adr('B1'))).toEqual('2') - expect(engine.getCellSerialized(adr('C1'))).toEqual('foo') - expect(engine.getCellSerialized(adr('D1'))).toEqual(true) - }) - - it('should return empty value', () => { - const engine = HyperFormula.buildFromArray([ - [null, undefined] - ]) - - expect(engine.getCellSerialized(adr('A1'))).toEqual(null) - expect(engine.getCellSerialized(adr('B1'))).toEqual(null) - expect(engine.getCellSerialized(adr('C1'))).toEqual(null) - }) - - it('should return formula of a formula matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['{=TRANSPOSE(A1:B1)}'], - ['{=TRANSPOSE(A1:B1)}'], - ]) - - expect(engine.getCellSerialized(adr('A2'))).toEqual('{=TRANSPOSE(A1:B1)}') - expect(engine.getCellSerialized(adr('A3'))).toEqual('{=TRANSPOSE(A1:B1)}') - }) - - it('should return translated error', () => { - HyperFormula.registerLanguage('plPL', plPL) - const engine = HyperFormula.buildFromArray([ - ['=#ARG!'], - ], {language: 'plPL'}) - - expect(engine.getCellSerialized(adr('A1'))).toEqual('=#ARG!') - }) - - it('should throw error if cellAddress is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildEmpty() - expect(() => { - engine.getCellSerialized({} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress')) - }) -}) - -describe('#getAllSheetsSerialized', () => { - it('should serialize all sheets', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [['=A()']], - Foo: [[1]], - Err1: [['=A1']], - Err2: [['234.23141234.2314']], - Err3: [['#DIV/0!']], - }) - - expect(engine.getAllSheetsSerialized()).toEqual({ - 'Foo': [[1]], - 'Sheet1': [['=A()']], - 'Err1': [['=A1']], - 'Err2': [['234.23141234.2314']], - 'Err3': [['#DIV/0!']], - }) - }) -}) - -describe('#getRangeSerialized', () => { - it('should return empty values', () => { - const engine = HyperFormula.buildFromArray([]) - - expectArrayWithSameContent([[null, null]], engine.getRangeSerialized(AbsoluteCellRange.spanFrom(adr('A1'), 2, 1))) - }) - - it('should return serialized cells from range', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(1, B1)', '2', '#VALUE!', null, '=#DIV/0!', '{=TRANSPOSE(A1:B1)}'] - ]) - - const out = engine.getRangeSerialized(AbsoluteCellRange.spanFrom(adr('A1'), 6, 1)) - - expectArrayWithSameContent([['=SUM(1, B1)', '2', '#VALUE!', null, '=#DIV/0!', '{=TRANSPOSE(A1:B1)}']], out) - }) - - it('should throw error if source is a malformed SimpleCellRange', () => { - const engine = HyperFormula.buildFromArray([]) - expect(() => { - engine.getRangeSerialized({} as SimpleCellRange) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellRange', 'source')) - }) -}) - -describe('#sheetName', () => { - it('returns sheet name if sheet exists', () => { - const engine = HyperFormula.buildEmpty() - - engine.addSheet() - - expect(engine.getSheetName(0)).toEqual('Sheet1') - }) - - it('returns undefined if sheet doesnt exists', () => { - const engine = HyperFormula.buildEmpty() - - expect(engine.getSheetName(0)).toBeUndefined() - }) -}) - -describe('#sheetId', () => { - it('returns id if sheet exists', () => { - const engine = HyperFormula.buildEmpty() - - engine.addSheet('foobar') - - expect(engine.getSheetId('foobar')).toEqual(0) - }) - - it('returns undefined if sheet doesnt exists', () => { - const engine = HyperFormula.buildEmpty() - - expect(engine.getSheetId('doesntexist')).toBeUndefined() - }) -}) - -describe('#doesSheetExist', () => { - it('true if sheet exists', () => { - const engine = HyperFormula.buildEmpty() - - engine.addSheet('foobar') - - expect(engine.doesSheetExist('foobar')).toBe(true) - }) - - it('false if sheet doesnt exist', () => { - const engine = HyperFormula.buildEmpty() - - expect(engine.doesSheetExist('foobar')).toBe(false) - }) -}) - -describe('#numberOfSheets', () => { - it('returns 0 if empty', () => { - const engine = HyperFormula.buildEmpty() - - expect(engine.countSheets()).toBe(0) - }) - - it('returns number of sheets', () => { - const engine = HyperFormula.buildEmpty() - - engine.addSheet('foo') - - expect(engine.countSheets()).toBe(1) - }) -}) - -describe('#sheetNames', () => { - it('empty engine', () => { - const engine = HyperFormula.buildEmpty() - - expect(engine.getSheetNames()).toEqual([]) - }) - - it('returns sheet names', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addSheet('Foo') - - expect(engine.getSheetNames()).toEqual(['Sheet1', 'Foo']) - }) -}) - -describe('#getCellType', () => { - it('empty cell', () => { - const engine = HyperFormula.buildFromArray([[null, undefined]]) - - expect(engine.getCellType(adr('A1'))).toBe(CellType.EMPTY) - expect(engine.getCellType(adr('B1'))).toBe(CellType.EMPTY) - expect(engine.getCellType(adr('C1'))).toBe(CellType.EMPTY) - }) - - it('simple value', () => { - const engine = HyperFormula.buildFromArray([['1', 'foo']]) - - expect(engine.getCellType(adr('A1'))).toBe(CellType.VALUE) - expect(engine.getCellType(adr('B1'))).toBe(CellType.VALUE) - }) - - it('formula', () => { - const engine = HyperFormula.buildFromArray([['=SUM(1, 2)']]) - - expect(engine.getCellType(adr('A1'))).toBe(CellType.FORMULA) - }) - - it('formula matrix', () => { - const engine = HyperFormula.buildFromArray([['=TRANSPOSE(C1:C2)']]) - - expect(engine.getCellType(adr('A1'))).toBe(CellType.ARRAYFORMULA) - expect(engine.getCellType(adr('B1'))).toBe(CellType.ARRAY) - }) - - it('parsing error is a formula cell', () => { - const engine = HyperFormula.buildFromArray([['=SUM(']]) - - expect(engine.getCellType(adr('A1'))).toBe(CellType.FORMULA) - }) - - it('should throw error if cellAddress is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildFromArray([['=TRANSPOSE(C1:C2)']]) - expect(() => { - engine.getCellType({col: 0} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress')) - }) -}) - -describe('#getCellValueDetailedType', () => { - it('string', () => { - const engine = HyperFormula.buildFromArray([['foo']]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.STRING) - }) - - it('number data', () => { - const engine = HyperFormula.buildFromArray([['42']]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_RAW) - }) - - it('number currency', () => { - const engine = HyperFormula.buildFromArray([['42$']]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - }) - - it('number percent', () => { - const engine = HyperFormula.buildFromArray([['42%']]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - }) - - it('number date', () => { - const engine = HyperFormula.buildFromArray([['01/01/1967']]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_DATE) - }) - - it('number datetime', () => { - const engine = HyperFormula.buildFromArray([['01/01/1967 15:34']]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - }) - - it('number time', () => { - const engine = HyperFormula.buildFromArray([['15:34']]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_TIME) - }) - - it('boolean', () => { - const engine = HyperFormula.buildFromArray([['=TRUE()']]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.BOOLEAN) - }) - - it('empty value', () => { - const engine = HyperFormula.buildFromArray([[null]]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.EMPTY) - expect(engine.getCellValueDetailedType(adr('B1'))).toBe(CellValueDetailedType.EMPTY) - }) - - it('error', () => { - const engine = HyperFormula.buildFromArray([['=1/0', '=SU()', '=A1']]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.ERROR) - expect(engine.getCellValueDetailedType(adr('B1'))).toBe(CellValueDetailedType.ERROR) - expect(engine.getCellValueDetailedType(adr('C1'))).toBe(CellValueDetailedType.ERROR) - }) - - it('formula evaluating to range', () => { - const engine = HyperFormula.buildFromArray([['=B1:B2', '=C:D']]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.ERROR) - expect(engine.getCellValueDetailedType(adr('B1'))).toBe(CellValueDetailedType.ERROR) - }) - - it('should throw error if cellAddress is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildFromArray([['=B1:B2', '=C:D']]) - expect(() => { - engine.getCellValueDetailedType({col: 0} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress')) - }) -}) - -describe('#getCellValueFormat', () => { - it('non-currency', () => { - const engine = HyperFormula.buildFromArray([['foo']]) - expect(engine.getCellValueFormat(adr('A1'))).toEqual(undefined) - }) - - it('currency', () => { - const engine = HyperFormula.buildFromArray([['1PLN']], {currencySymbol: ['PLN', '$']}) - expect(engine.getCellValueFormat(adr('A1'))).toEqual('PLN') - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.NUMBER_CURRENCY) - }) - - it('unicode currency', () => { - const engine = HyperFormula.buildFromArray([['1₪']], {currencySymbol: ['₪']}) - expect(engine.getCellValueFormat(adr('A1'))).toEqual('₪') - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.NUMBER_CURRENCY) - }) - - it('should throw error if cellAddress is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildFromArray([[]]) - expect(() => { - engine.getCellValueFormat({col: 0} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress')) - }) -}) - -describe('#getCellValueType', () => { - it('string', () => { - const engine = HyperFormula.buildFromArray([['foo']]) - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.STRING) - }) - - it('number', () => { - const engine = HyperFormula.buildFromArray([['42', '=SUM(1, A1)']]) - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.NUMBER) - expect(engine.getCellValueType(adr('B1'))).toBe(CellValueType.NUMBER) - }) - - it('boolean', () => { - const engine = HyperFormula.buildFromArray([['=TRUE()']]) - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.BOOLEAN) - }) - - it('empty value', () => { - const engine = HyperFormula.buildFromArray([[null]]) - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.EMPTY) - expect(engine.getCellValueType(adr('B1'))).toBe(CellValueType.EMPTY) - }) - - it('error', () => { - const engine = HyperFormula.buildFromArray([['=1/0', '=SU()', '=A1']]) - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.ERROR) - expect(engine.getCellValueType(adr('B1'))).toBe(CellValueType.ERROR) - expect(engine.getCellValueType(adr('C1'))).toBe(CellValueType.ERROR) - }) - - it('formula evaluating to range', () => { - const engine = HyperFormula.buildFromArray([['=B1:B2', '=C:D']]) - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.ERROR) - expect(engine.getCellValueType(adr('B1'))).toBe(CellValueType.ERROR) - }) - - it('should throw error if cellAddress is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildFromArray([['=B1:B2', '=C:D']]) - expect(() => { - engine.getCellValueType({col: 0} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress')) - }) -}) - -describe('#doesCellHaveSimpleValue', () => { - it('true', () => { - const engine = HyperFormula.buildFromArray([['1', 'foo']]) - expect(engine.doesCellHaveSimpleValue(adr('A1'))).toEqual(true) - expect(engine.doesCellHaveSimpleValue(adr('B1'))).toEqual(true) - }) - - it('false', () => { - const engine = HyperFormula.buildFromArray([['=SUM(1, 2)', null, '=TRANSPOSE(A1:A1)']]) - expect(engine.doesCellHaveSimpleValue(adr('A1'))).toEqual(false) - expect(engine.doesCellHaveSimpleValue(adr('B1'))).toEqual(false) - expect(engine.doesCellHaveSimpleValue(adr('C1'))).toEqual(false) - }) - - it('should throw error if cellAddress is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildFromArray([['=SUM(1, 2)', null, '=TRANSPOSE(A1:A1)']]) - expect(() => { - engine.doesCellHaveSimpleValue({col: 0} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress')) - }) -}) - -describe('#doesCellHaveFormula', () => { - it('true', () => { - const engine = HyperFormula.buildFromArray([['=SUM(1, 2)']]) - expect(engine.doesCellHaveFormula(adr('A1'))).toEqual(true) - }) - - it('false', () => { - const engine = HyperFormula.buildFromArray([['1', '', '{=TRANSPOSE(A1:A1)}', 'foo', null]]) - expect(engine.doesCellHaveFormula(adr('A1'))).toEqual(false) - expect(engine.doesCellHaveFormula(adr('B1'))).toEqual(false) - expect(engine.doesCellHaveFormula(adr('C1'))).toEqual(false) - expect(engine.doesCellHaveFormula(adr('D1'))).toEqual(false) - expect(engine.doesCellHaveFormula(adr('E1'))).toEqual(false) - }) - - it('arrayformula', () => { - const engine = HyperFormula.buildFromArray([['=ARRAYFORMULA(ISEVEN(B1:B2*2))']]) - expect(engine.doesCellHaveFormula(adr('A1'))).toEqual(true) - }) - - it('should throw error if cellAddress is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildFromArray([['=ARRAYFORMULA(ISEVEN(B1:B2*2))']]) - expect(() => { - engine.doesCellHaveFormula({col: 0} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress')) - }) -}) - -describe('#isCellEmpty', () => { - it('true', () => { - const engine = HyperFormula.buildFromArray([[null, undefined]]) - expect(engine.isCellEmpty(adr('A1'))).toEqual(true) - expect(engine.isCellEmpty(adr('B1'))).toEqual(true) - expect(engine.isCellEmpty(adr('C1'))).toEqual(true) - }) - - it('false', () => { - const engine = HyperFormula.buildFromArray([['1', '=SUM(1, 2)', '{=TRANSPOSE(A1:A1)}', 'foo']]) - expect(engine.isCellEmpty(adr('A1'))).toEqual(false) - expect(engine.isCellEmpty(adr('B1'))).toEqual(false) - expect(engine.isCellEmpty(adr('C1'))).toEqual(false) - expect(engine.isCellEmpty(adr('D1'))).toEqual(false) - }) - - it('should throw error if cellAddress is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildFromArray([['1', '=SUM(1, 2)', '{=TRANSPOSE(A1:A1)}', 'foo']]) - expect(() => { - engine.isCellEmpty({col: 0} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress')) - }) -}) - -describe('#isCellPartOfArray', () => { - it('true', () => { - const engine = HyperFormula.buildFromArray([['=TRANSPOSE(B1:C1)']]) - expect(engine.isCellPartOfArray(adr('A1'))).toEqual(true) - }) - - it('false', () => { - const engine = HyperFormula.buildFromArray([['1', '', '=SUM(1, 2)', 'foo']]) - expect(engine.isCellPartOfArray(adr('A1'))).toEqual(false) - expect(engine.isCellPartOfArray(adr('B1'))).toEqual(false) - expect(engine.isCellPartOfArray(adr('C1'))).toEqual(false) - expect(engine.isCellPartOfArray(adr('D1'))).toEqual(false) - }) - - it('should throw error if cellAddress is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildFromArray([['1', '', '=SUM(1, 2)', 'foo']]) - expect(() => { - engine.isCellPartOfArray({col: 0} as SimpleCellAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress')) - }) -}) - -describe('dateTime', () => { - it('dateTime', () => { - const engine = HyperFormula.buildEmpty() - expect(engine.numberToDateTime(43845.1)).toEqual({ - 'day': 15, - 'hours': 2, - 'minutes': 24, - 'month': 1, - 'seconds': 0, - 'year': 2020 - }) - expect(engine.numberToDate(43845)).toEqual({'day': 15, 'month': 1, 'year': 2020}) - expect(engine.numberToTime(1.1)).toEqual({'hours': 26, 'minutes': 24, 'seconds': 0}) - }) -}) - -describe('Graph dependency topological ordering module', () => { - it('should build correctly when rows are dependant on cells that are not yet processed #1', () => { - expect(() => HyperFormula.buildFromArray([ - ['=A3+A2'], - ['=A3'], - ])).not.toThrowError() - }) - - it('should build correctly when rows are dependant on cells that are not yet processed #2', () => { - expect(() => HyperFormula.buildFromArray([ - ['=A4+A3+A2'], - ['=A4+A3'], - ['=A4'], - ])).not.toThrowError() - }) - - it('should build correctly when rows are dependant on cells that are not yet processed #3', () => { - expect(() => HyperFormula.buildFromArray([ - ['=A5+A4+A3+A2'], - ['=A5+A4+A3'], - ['=A5+A4'], - ['=A5'], - ])).not.toThrowError() - }) -}) - -describe('#getFillRangeData from corner source', () => { - it('should properly apply wrap-around #1', () => { - const engine = HyperFormula.buildFromArray([[], [undefined, 1, '=A1'], [undefined, '=$A$1', '2']]) - - expect(engine.getFillRangeData(AbsoluteCellRange.spanFrom(adr('B2'), 2, 2), AbsoluteCellRange.spanFrom(adr('C3'), 3, 3)) - ).toEqual([['2', '=$A$1', '2'], ['=A3', 1, '=C3'], ['2', '=$A$1', '2']]) - }) - - it('should properly apply wrap-around #2', () => { - const engine = HyperFormula.buildFromArray([[], [undefined, 1, '=A1'], [undefined, '=$A$1', '2']]) - - expect(engine.getFillRangeData(AbsoluteCellRange.spanFrom(adr('B2'), 2, 2), AbsoluteCellRange.spanFrom(adr('B2'), 3, 3)) - ).toEqual([[1, '=A1', 1], ['=$A$1', '2', '=$A$1'], [1, '=A3', 1]]) - }) - - it('should properly apply wrap-around #3', () => { - const engine = HyperFormula.buildFromArray([[], [undefined, 1, '=A1'], [undefined, '=$A$1', '2']]) - - expect(engine.getFillRangeData(AbsoluteCellRange.spanFrom(adr('B2'), 2, 2), AbsoluteCellRange.spanFrom(adr('A1'), 3, 3)) - ).toEqual([['2', '=$A$1', '2'], ['=#REF!', 1, '=A1'], ['2', '=$A$1', '2']]) - }) -}) - -describe('#getFillRangeData from target source', () => { - it('should properly apply wrap-around #1', () => { - const engine = HyperFormula.buildFromArray([[], [undefined, 1, '=A1'], [undefined, '=$A$1', '2']]) - - expect(engine.getFillRangeData(AbsoluteCellRange.spanFrom(adr('B2'), 2, 2), AbsoluteCellRange.spanFrom(adr('C3'), 3, 3), true) - ).toEqual([[1, '=B2', 1], ['=$A$1', '2', '=$A$1'], [1, '=B4', 1]]) - }) - - it('should properly apply wrap-around #2', () => { - const engine = HyperFormula.buildFromArray([[], [undefined, 1, '=A1'], [undefined, '=$A$1', '2']]) - - expect(engine.getFillRangeData(AbsoluteCellRange.spanFrom(adr('B2'), 2, 2), AbsoluteCellRange.spanFrom(adr('B2'), 3, 3), true) - ).toEqual([[1, '=A1', 1], ['=$A$1', '2', '=$A$1'], [1, '=A3', 1]]) - }) - - it('should properly apply wrap-around #3', () => { - const engine = HyperFormula.buildFromArray([[], [undefined, 1, '=A1'], [undefined, '=$A$1', '2']]) - - expect(engine.getFillRangeData(AbsoluteCellRange.spanFrom(adr('B2'), 2, 2), AbsoluteCellRange.spanFrom(adr('A1'), 3, 3), true) - ).toEqual([[1, '=#REF!', 1], ['=$A$1', '2', '=$A$1'], [1, '=#REF!', 1]]) - }) -}) - -describe('#getFillRangeData', () => { - it('should move between sheets - sheet relative addresses', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [[], [undefined, 1, '=A1'], [undefined, '=$A$1', '2']], - 'Sheet2': [], - } - ) - - expect(engine.getFillRangeData(AbsoluteCellRange.spanFrom(adr('B2', 0), 2, 2), AbsoluteCellRange.spanFrom(adr('C3', 1), 3, 3)) - ).toEqual([['2', '=$A$1', '2'], ['=A3', 1, '=C3'], ['2', '=$A$1', '2']]) - }) - - it('should move between sheets - sheet absolute addresses', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [[], [undefined, 1, '=Sheet1!A1'], [undefined, '=Sheet2!$A$1', '2']], - 'Sheet2': [], - } - ) - - expect(engine.getFillRangeData(AbsoluteCellRange.spanFrom(adr('B2', 0), 2, 2), AbsoluteCellRange.spanFrom(adr('C3', 1), 3, 3)) - ).toEqual([['2', '=Sheet2!$A$1', '2'], ['=Sheet1!A3', 1, '=Sheet1!C3'], ['2', '=Sheet2!$A$1', '2']]) - }) - - it('should move between sheets - no sheet of a given name', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [], - } - ) - - expect(engine.getFillRangeData(AbsoluteCellRange.spanFrom(adr('B2', 0), 1, 1), AbsoluteCellRange.spanFrom(adr('C3', 1), 1, 1)) - ).toEqual([[null]]) - }) - - it('should throw error if source is a malformed SimpleCellRange', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [[], [undefined, 1, '=A1'], [undefined, '=$A$1', '2']], - 'Sheet2': [], - }) - expect(() => { - engine.getFillRangeData({} as SimpleCellRange, AbsoluteCellRange.spanFrom(adr('C3', 1), 3, 3)) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellRange', 'source')) - }) - - it('should throw error if target is a malformed SimpleCellRange', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [[], [undefined, 1, '=A1'], [undefined, '=$A$1', '2']], - 'Sheet2': [], - }) - expect(() => { - engine.getFillRangeData(AbsoluteCellRange.spanFrom(adr('C3', 1), 3, 3), {} as SimpleCellRange) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellRange', 'target')) - }) -}) - -describe('#simpleCellRangeToString', () => { - it('return a string for a cell range', () => { - const engine = HyperFormula.buildFromArray([[]]) - expect(engine.simpleCellRangeToString(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2), 0)).toBe('A1:B2') - }) - - it('should throw error if cellRange is a malformed SimpleCellRange', () => { - const engine = HyperFormula.buildEmpty() - expect(() => { - engine.simpleCellRangeToString({} as SimpleCellRange, 0) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellRange', 'cellRange')) - }) - - it('should work without second argument', () => { - const hf = HyperFormula.buildFromSheets({ - Sheet0: [], - Sheet1: [] - }) - - const cellRange = { start: { sheet: 0, row: 0, col: 0 }, end: { sheet: 0, row: 1, col: 1 }} - const stringRange = hf.simpleCellRangeToString(cellRange) - - expect(stringRange).toEqual('A1:B2') - }) - - it('should work with second argument `undefined`', () => { - const hf = HyperFormula.buildFromSheets({ - Sheet0: [], - Sheet1: [] - }) - - const cellRange = { start: { sheet: 0, row: 0, col: 0 }, end: { sheet: 0, row: 1, col: 1 }} - const stringRange = hf.simpleCellRangeToString(cellRange, undefined) - - expect(stringRange).toEqual('A1:B2') - }) - - it('should work with second argument `{}`', () => { - const hf = HyperFormula.buildFromSheets({ - Sheet0: [], - Sheet1: [] - }) - - const cellRange = { start: { sheet: 0, row: 0, col: 0 }, end: { sheet: 0, row: 1, col: 1 }} - const stringRange = hf.simpleCellRangeToString(cellRange, {}) - - expect(stringRange).toEqual('A1:B2') - }) - - it('should work with second argument `{ includeSheetName: false }`', () => { - const hf = HyperFormula.buildFromSheets({ - Sheet0: [], - Sheet1: [] - }) - - const cellRange = { start: { sheet: 0, row: 0, col: 0 }, end: { sheet: 0, row: 1, col: 1 }} - const stringRange = hf.simpleCellRangeToString(cellRange, { includeSheetName: false }) - - expect(stringRange).toEqual('A1:B2') - }) - - it('should work with second argument `{ includeSheetName: true }`', () => { - const hf = HyperFormula.buildFromSheets({ - Sheet0: [], - Sheet1: [] - }) - - const cellRange = { start: { sheet: 0, row: 0, col: 0 }, end: { sheet: 0, row: 1, col: 1 }} - const stringRange = hf.simpleCellRangeToString(cellRange, { includeSheetName: true }) - - expect(stringRange).toEqual('Sheet0!A1:B2') - }) -}) - -describe('#simpleCellAddressToString', () => { - it('should throw error if cellAddress is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildEmpty() - expect(() => { - engine.simpleCellAddressToString({} as SimpleCellAddress, 0) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress')) - }) - - it('should work without second argument', () => { - const hf = HyperFormula.buildFromSheets({ - Sheet0: [], - Sheet1: [] - }) - - const cellAddress = { sheet: 0, row: 0, col: 0 } - const stringAddress = hf.simpleCellAddressToString(cellAddress) - - expect(stringAddress).toEqual('A1') - }) - - it('should work with second argument `undefined`', () => { - const hf = HyperFormula.buildFromSheets({ - Sheet0: [], - Sheet1: [] - }) - - const cellAddress = { sheet: 0, row: 0, col: 0 } - const stringAddress = hf.simpleCellAddressToString(cellAddress, undefined) - - expect(stringAddress).toEqual('A1') - }) - - it('should work with second argument `{}`', () => { - const hf = HyperFormula.buildFromSheets({ - Sheet0: [], - Sheet1: [] - }) - - const cellAddress = { sheet: 0, row: 0, col: 0 } - const stringAddress = hf.simpleCellAddressToString(cellAddress, {}) - - expect(stringAddress).toEqual('A1') - }) - - it('should work with second argument `{ includeSheetName: false }`', () => { - const hf = HyperFormula.buildFromSheets({ - Sheet0: [], - Sheet1: [] - }) - - const cellAddress = { sheet: 0, row: 0, col: 0 } - const stringAddress = hf.simpleCellAddressToString(cellAddress, { includeSheetName: false }) - - expect(stringAddress).toEqual('A1') - }) - - it('should work with second argument `{ includeSheetName: true }`', () => { - const hf = HyperFormula.buildFromSheets({ - Sheet0: [], - Sheet1: [] - }) - - const cellAddress = { sheet: 0, row: 0, col: 0 } - const stringAddress = hf.simpleCellAddressToString(cellAddress, { includeSheetName: true }) - - expect(stringAddress).toEqual('Sheet0!A1') - }) -}) - -describe('#simpleCellAddressFromString', () => { - it('should convert a string to a simpleCellAddress', () => { - const engine = HyperFormula.buildEmpty() - expect(engine.simpleCellAddressFromString('C5', 2)).toEqual(simpleCellAddress(2, 2, 4)) - }) - - it('should work for address with sheet name', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [], - }) - expect(engine.simpleCellAddressFromString('Sheet1!C5', 2)).toEqual(simpleCellAddress(0, 2, 4)) - }) - - it('should work when sheet name contains unicode characters (sheet name in single quotes)', () => { - // noinspection NonAsciiCharacters - const engine = HyperFormula.buildFromSheets({ - あは: [], - }) - expect(engine.simpleCellAddressFromString("'あは'!C5", 2)).toEqual(simpleCellAddress(0, 2, 4)) - }) - - it('should return undefined when sheet name contains unicode characters (unquoted sheet name)', () => { - // noinspection NonAsciiCharacters - const engine = HyperFormula.buildFromSheets({ - あは: [], - }) - expect(engine.simpleCellAddressFromString('あは!C5', 2)).toBe(undefined) - }) -}) - -describe('#simpleCellRangeFromString', () => { - it('should convert a string to a simpleCellRange', () => { - const engine = HyperFormula.buildEmpty() - expect(engine.simpleCellRangeFromString('A1:C1', 0)).toEqual(simpleCellRange(adr('A1'), adr('C1'))) - }) - - it('should set sheetId to contestSheetId if there is no sheet name in the first argument', () => { - const engine = HyperFormula.buildEmpty() - - expect(engine.simpleCellRangeFromString('A1:C1', 42)).toEqual(simpleCellRange(adr('A1', 42), adr('C1', 42))) - }) - - it('should set sheetId correctly if the sheet name is provided for the start of the range', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet0: [] - }) - - expect(engine.simpleCellRangeFromString('Sheet0!A1:C1', 42)).toEqual(simpleCellRange(adr('A1', 0), adr('C1', 0))) - }) - - it('should set sheetId correctly if the same sheet name is provided for both ends of the range', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet0: [] - }) - - expect(engine.simpleCellRangeFromString('Sheet0!A1:Sheet0!C1', 42)).toEqual(simpleCellRange(adr('A1', 0), adr('C1', 0))) - }) - - it('should return undefined if different sheet names are provided for the start and the end of the range', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet0: [], - Sheet1: [] - }) - - expect(engine.simpleCellRangeFromString('Sheet0!A1:Sheet1!C1', 42)).toEqual(undefined) - }) -}) diff --git a/test/unit/error-address-preservation.spec.ts b/test/unit/error-address-preservation.spec.ts deleted file mode 100644 index 1b04fc92c7..0000000000 --- a/test/unit/error-address-preservation.spec.ts +++ /dev/null @@ -1,86 +0,0 @@ -import {ErrorType, HyperFormula} from '../../src' -import {simpleCellRange} from '../../src/AbsoluteCellRange' -import {adr, detailedErrorWithOrigin} from './testUtils' - -describe('Address preservation.', () => { - it('Should work in the basic case.', () => { - const engine = HyperFormula.buildFromArray([ - ['=NA()', '=A1'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!A1')) - expect(engine.getCellValue(adr('B1'))).toEqual(detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!A1')) - }) - - it('Should work with named expressions.', () => { - const engine = HyperFormula.buildFromArray([ - ['=NAMEDEXPRESSION', '=A1'] - ]) - engine.addNamedExpression('NAMEDEXPRESSION', '=NA()') - expect(engine.getCellValue(adr('A1'))).toEqual(detailedErrorWithOrigin(ErrorType.NA, 'NAMEDEXPRESSION')) - expect(engine.getCellValue(adr('B1'))).toEqual(detailedErrorWithOrigin(ErrorType.NA, 'NAMEDEXPRESSION')) - }) - - it('Should work with operators.', () => { - const engine = HyperFormula.buildFromArray([ - ['=NA()', '=NA()', '=A1+B1'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!A1')) - }) - - it('Should work between sheets.', () => { - const engine = HyperFormula.buildFromSheets({ - sheet1: [['=NA()']], - sheet2: [['=sheet1!A1']] - }) - expect(engine.getCellValue(adr('A1', 0))).toEqual(detailedErrorWithOrigin(ErrorType.NA, 'sheet1!A1')) - expect(engine.getCellValue(adr('A1', 1))).toEqual(detailedErrorWithOrigin(ErrorType.NA, 'sheet1!A1')) - }) - - it('Should work with function calls.', () => { - const engine = HyperFormula.buildFromArray([ - ['=NA()', '=DATE(1,1,A1)'] - ]) - expect(engine.getCellValue(adr('B1'))).toEqual(detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!A1')) - }) - - it('Should work with CYCLE.', () => { - const engine = HyperFormula.buildFromArray([ - ['=B1', '=A1'], - ['=A1', '=B1'], - ['=A1', '=B1'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!A1')) - expect(engine.getCellValue(adr('B1'))).toEqual(detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!B1')) - expect(engine.getCellValue(adr('A2'))).toEqual(detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!A1')) - expect(engine.getCellValue(adr('B2'))).toEqual(detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!B1')) - expect(engine.getCellValue(adr('A3'))).toEqual(detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!A1')) - expect(engine.getCellValue(adr('B3'))).toEqual(detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!B1')) - }) - - it('Should work with CYCLE #2.', () => { - const engine = HyperFormula.buildFromArray([ - ['=B1', '=A1'], - ['=A1'], - ['=A1'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!A1')) - expect(engine.getCellValue(adr('B1'))).toEqual(detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!B1')) - expect(engine.getCellValue(adr('A2'))).toEqual(detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!A1')) - expect(engine.getCellValue(adr('A3'))).toEqual(detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!A1')) - }) - - it('Should work after simple cruds', () => { - const engine = HyperFormula.buildFromArray([ - ['=NA()', '=A1'] - ]) - - engine.addColumns(0, [0, 1]) - expect(engine.getCellValue(adr('C1'))).toEqual(detailedErrorWithOrigin(ErrorType.NA, 'Sheet1!B1')) - - engine.setCellContents(adr('B1'), '=1/0') - expect(engine.getCellValue(adr('C1'))).toEqual(detailedErrorWithOrigin(ErrorType.DIV_BY_ZERO, 'Sheet1!B1')) - - engine.moveCells(simpleCellRange(adr('B1'), adr('B1')), adr('C5')) - expect(engine.getCellValue(adr('C1'))).toEqual(detailedErrorWithOrigin(ErrorType.DIV_BY_ZERO, 'Sheet1!C5')) - }) -}) diff --git a/test/unit/escaping-support.spec.ts b/test/unit/escaping-support.spec.ts deleted file mode 100644 index 11e326c6f7..0000000000 --- a/test/unit/escaping-support.spec.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {HyperFormula} from '../../src' -import {adr} from './testUtils' - -describe('escaped formulas', () => { - it('should serialize properly', () => { - const engine = HyperFormula.buildFromArray([['\'=SUM(2,2)']]) - expect(engine.getCellSerialized(adr('A1'))).toEqual('\'=SUM(2,2)') - expect(engine.getCellValue(adr('A1'))).toEqual('=SUM(2,2)') - }) -}) diff --git a/test/unit/extending-plugins.spec.ts b/test/unit/extending-plugins.spec.ts deleted file mode 100644 index e222c08aa2..0000000000 --- a/test/unit/extending-plugins.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {ErrorType, HyperFormula} from '../../src' -import {ErrorMessage} from '../../src/error-message' -import {InterpreterState} from '../../src/interpreter/InterpreterState' -import {FunctionPlugin, FunctionPluginTypecheck} from '../../src/interpreter/plugin/FunctionPlugin' -import {ProcedureAst} from '../../src/parser' -import {adr, detailedError} from './testUtils' - -class FooPlugin extends FunctionPlugin implements FunctionPluginTypecheck { - public static implementedFunctions = { - 'FOO': { - method: 'foo', - }, - } - - public foo(_ast: ProcedureAst, _state: InterpreterState) { - return 42 - } -} - -describe('Plugins', () => { - it('Extending with a plugin', () => { - HyperFormula.getLanguage('enGB').extendFunctions({'FOO': 'FOO'}) - const engine = HyperFormula.buildFromArray([ - ['=foo()'], - ], {functionPlugins: [FooPlugin]}) - - expect(engine.getCellValue(adr('A1'))).toBe(42) - }) - - it('cleanup', () => { - const engine = HyperFormula.buildFromArray([ - ['=foo()'], - ], {functionPlugins: [FooPlugin]}) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOO'))) - }) -}) diff --git a/test/unit/find-boundaries.spec.ts b/test/unit/find-boundaries.spec.ts deleted file mode 100644 index 8d6e6253a5..0000000000 --- a/test/unit/find-boundaries.spec.ts +++ /dev/null @@ -1,83 +0,0 @@ -import {findBoundaries} from '../../src/Sheet' - -describe('findBoundaries', () => { - it('find correct dimensions', () => { - expect(findBoundaries([ - ['1', '2'], - ['1', '2', '3'], - ])).toMatchObject({height: 2, width: 3}) - }) - - it('find correct dimensions when empty cell at the end of row', () => { - expect(findBoundaries([ - ['1', '2'], - ['1', '2', null], - ])).toMatchObject({height: 2, width: 2}) - }) - - it('find correct dimensions when empty cell in the middle of the row', () => { - expect(findBoundaries([ - ['1', '2'], - ['1', '2', null, '4'], - ])).toMatchObject({height: 2, width: 4}) - }) - - it('find correct dimensions when empty row', () => { - expect(findBoundaries([ - ['1', '2'], - ['1', '2'], - [null], - [], - ])).toMatchObject({height: 2, width: 2}) - }) - - it('returns sane dimensions for empty cases', () => { - expect(findBoundaries([])).toMatchObject({height: 0, width: 0}) - expect(findBoundaries([[]])).toMatchObject({height: 0, width: 0}) - }) - - it('calculate correct fill for array with different size', () => { - expect(findBoundaries([ - ['1', '2'], - ['1', '2', '3'], - ])).toMatchObject({fill: 5 / 6}) - }) - - it('calculate correct fill for empty arrays', () => { - expect(findBoundaries([]).fill).toBe(0) - expect(findBoundaries([[]]).fill).toBe(0) - }) - - it('calculate correct fill for array with one element', () => { - expect(findBoundaries([[null]]).fill).toBe(0) - expect(findBoundaries([['x']]).fill).toBe(1) - }) - - it('does count empty string', () => { - expect(findBoundaries([ - ['1', ''], - ['1', '2'], - ])).toMatchObject({fill: 1}) - }) - - it('does not count empty value', () => { - expect(findBoundaries([ - ['1', null], - ['1', '2'], - ])).toMatchObject({fill: 3 / 4}) - }) - - it('does not count null', () => { - expect(findBoundaries([ - ['1', null], - ['1', '2'], - ])).toMatchObject({fill: 3 / 4}) - }) - - it('does not count undefined', () => { - expect(findBoundaries([ - ['1', undefined], - ['1', '2'], - ])).toMatchObject({fill: 3 / 4}) - }) -}) diff --git a/test/unit/format/format-interpreter.spec.ts b/test/unit/format/format-interpreter.spec.ts deleted file mode 100644 index aac301295f..0000000000 --- a/test/unit/format/format-interpreter.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {Config} from '../../../src/Config' -import {DateTimeHelper} from '../../../src/DateTimeHelper' -import {format} from '../../../src/format/format' - -describe('FormatInterpreter', () => { - const config = new Config() - const dateHelper = new DateTimeHelper(config) - it('works for expression without significant tokens', () => { - expect(format(2, 'Foo', config, dateHelper)).toEqual('Foo') - }) - - it('works for simple date expression', () => { - expect(format(2, 'dd-mm-yyyy', config, dateHelper)).toEqual('01-01-1900') - }) - - it('works with # without decimal separator', () => { - expect(format(1, '###', config, dateHelper)).toEqual('1') - expect(format(12, '###', config, dateHelper)).toEqual('12') - expect(format(123, '###', config, dateHelper)).toEqual('123') - expect(format(123.4, '###', config, dateHelper)).toEqual('123') - expect(format(1234, '###', config, dateHelper)).toEqual('1234') - }) - - it('works with # number format with decimal separator', () => { - expect(format(1, '#.##', config, dateHelper)).toEqual('1.') - expect(format(12, '#.##', config, dateHelper)).toEqual('12.') - expect(format(12.34, '#.##', config, dateHelper)).toEqual('12.34') - expect(format(12.345, '#.##', config, dateHelper)).toEqual('12.35') - }) - - it('works with 0 without decimal separator', () => { - expect(format(1, '000', config, dateHelper)).toEqual('001') - expect(format(12, '000', config, dateHelper)).toEqual('012') - expect(format(123, '000', config, dateHelper)).toEqual('123') - expect(format(123.4, '000', config, dateHelper)).toEqual('123') - expect(format(1234, '000', config, dateHelper)).toEqual('1234') - }) - - it('works with 0 number format', () => { - expect(format(1, '00.00', config, dateHelper)).toEqual('01.00') - expect(format(12, '00.00', config, dateHelper)).toEqual('12.00') - expect(format(12.3, '00.00', config, dateHelper)).toEqual('12.30') - expect(format(12.34, '00.00', config, dateHelper)).toEqual('12.34') - expect(format(12.345, '00.00', config, dateHelper)).toEqual('12.35') - }) - - it('number formatting with additional chars', () => { - expect(format(1, '$0.00', config, dateHelper)).toEqual('$1.00') - }) -}) diff --git a/test/unit/format/format-parser.spec.ts b/test/unit/format/format-parser.spec.ts deleted file mode 100644 index 1e2aadb792..0000000000 --- a/test/unit/format/format-parser.spec.ts +++ /dev/null @@ -1,84 +0,0 @@ -import {FormatExpressionType, parse, TokenType} from '../../../src/format/parser' - -describe('FormatParser', () => { - it('works for escaped characters', () => { - const parseResult = parse('\\ddd') - - expect(parseResult.type).toBe(FormatExpressionType.DATE) - expect(parseResult.tokens.length).toBe(2) - expect(parseResult.tokens[0]).toEqual({ - type: TokenType.FREE_TEXT, - value: '\\d', - }) - expect(parseResult.tokens[1]).toEqual({ - type: TokenType.FORMAT, - value: 'dd', - }) - }) - - it('works only for escaped characters', () => { - const parseResult = parse('\\d\\d') - - expect(parseResult.type).toBe(FormatExpressionType.STRING) - expect(parseResult.tokens.length).toBe(1) - expect(parseResult.tokens[0]).toEqual({ - type: TokenType.FREE_TEXT, - value: '\\d\\d', - }) - }) - - it('works for date format', () => { - const parseResult = parse('dd-mm-yyyy') - - expect(parseResult.type).toBe(FormatExpressionType.DATE) - }) - - it('works for number format', () => { - const parseResult = parse('#.###') - - expect(parseResult.type).toBe(FormatExpressionType.NUMBER) - }) - - it('works for date format and free text', () => { - const parseResult = parse('dd foo') - - expect(parseResult.type).toBe(FormatExpressionType.DATE) - expect(parseResult.tokens.length).toBe(2) - - expect(parseResult.tokens[0]).toEqual({ - type: TokenType.FORMAT, - value: 'dd', - }) - - expect(parseResult.tokens[1]).toEqual({ - type: TokenType.FREE_TEXT, - value: ' foo', - }) - }) - - it('works without any formatting tokens', () => { - const parseResult = parse('foo') - - expect(parseResult.type).toBe(FormatExpressionType.STRING) - expect(parseResult.tokens[0]).toEqual({ - type: TokenType.FREE_TEXT, - value: 'foo', - }) - }) - - it('matches only one number tokens group', () => { - const parseResult = parse('#.### #.###') - - expect(parseResult.type).toBe(FormatExpressionType.NUMBER) - - expect(parseResult.tokens[0]).toEqual({ - type: TokenType.FORMAT, - value: '#.###', - }) - - expect(parseResult.tokens[1]).toEqual({ - type: TokenType.FREE_TEXT, - value: ' #.###', - }) - }) -}) diff --git a/test/unit/functions-metadata.spec.ts b/test/unit/functions-metadata.spec.ts deleted file mode 100644 index ee4337f86f..0000000000 --- a/test/unit/functions-metadata.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {HyperFormula} from '../../src' - -describe('All functions', () => { - it('should all contain metadata', () => { - const engine = HyperFormula.buildEmpty() - for (const functionId of engine.getRegisteredFunctionNames()) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const metadata = engine._functionRegistry.getMetadata(functionId) - expect(metadata).not.toBe(undefined) - } - }) - - it('should all contain param definition in metadata', () => { - const engine = HyperFormula.buildEmpty() - for (const functionId of engine.getRegisteredFunctionNames()) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const params = engine._functionRegistry.getMetadata(functionId)?.parameters - expect(params).not.toBe(undefined) - } - }) -}) diff --git a/test/unit/generate-cells-range.spec.ts b/test/unit/generate-cells-range.spec.ts deleted file mode 100644 index eb82a14341..0000000000 --- a/test/unit/generate-cells-range.spec.ts +++ /dev/null @@ -1,173 +0,0 @@ -import {AbsoluteCellRange} from '../../src/AbsoluteCellRange' -import {SimpleCellAddress} from '../../src/Cell' -import {Config} from '../../src/Config' -import {DependencyGraph} from '../../src/DependencyGraph' -import {FunctionRegistry} from '../../src/interpreter/FunctionRegistry' -import {LazilyTransformingAstService} from '../../src/LazilyTransformingAstService' -import {NamedExpressions} from '../../src/NamedExpressions' -import {RowsSpan} from '../../src/Span' -import {Statistics} from '../../src/statistics/Statistics' -import {adr} from './testUtils' - -describe('generateCellsFromRange', () => { - const config = new Config() - const functionRegistry = new FunctionRegistry(config) - const stats = new Statistics() - const lazilyTransformingAstService = new LazilyTransformingAstService(stats) - const dependencyGraph = DependencyGraph.buildEmpty(lazilyTransformingAstService, config, functionRegistry, new NamedExpressions(), stats) - const generateCellsFromRange = (range: AbsoluteCellRange): SimpleCellAddress[] => { - return Array.from(range.addresses(dependencyGraph)) - } - - it('one element', () => { - expect(generateCellsFromRange(new AbsoluteCellRange(adr('A1'), adr('A1')))).toEqual([ - adr('A1'), - ]) - }) - - it('simple row', () => { - expect(generateCellsFromRange(new AbsoluteCellRange(adr('A1'), adr('B1')))).toEqual([ - adr('A1'), - adr('B1'), - ]) - }) - - it('simple column', () => { - expect(generateCellsFromRange(new AbsoluteCellRange(adr('A1'), adr('A2')))).toEqual([ - adr('A1'), - adr('A2'), - ]) - }) - - it('simple square', () => { - expect(generateCellsFromRange(new AbsoluteCellRange(adr('A1'), adr('B2')))).toEqual([ - adr('A1'), - adr('B1'), - adr('A2'), - adr('B2'), - ]) - }) -}) - -describe('AbsoluteCellRange#sameDimensions', () => { - it('same width and height', () => { - const range1 = new AbsoluteCellRange(adr('B1'), adr('C4')) - const range2 = new AbsoluteCellRange(adr('L11'), adr('M14')) - expect(range1.sameDimensionsAs(range2)).toBe(true) - }) - - it('different width', () => { - const range1 = new AbsoluteCellRange(adr('B1'), adr('C4')) - const range2 = new AbsoluteCellRange(adr('L11'), adr('N14')) - expect(range1.sameDimensionsAs(range2)).toBe(false) - }) - - it('different height', () => { - const range1 = new AbsoluteCellRange(adr('B1'), adr('C4')) - const range2 = new AbsoluteCellRange(adr('L11'), adr('M15')) - expect(range1.sameDimensionsAs(range2)).toBe(false) - }) -}) - -describe('AbsoluteCellRange#doesOverlap', () => { - it('exactly the same', () => { - const range1 = new AbsoluteCellRange(adr('B1'), adr('C4')) - const range2 = new AbsoluteCellRange(adr('B1'), adr('C4')) - expect(range1.doesOverlap(range2)).toBe(true) - }) - - it('different sheets', () => { - const range1 = new AbsoluteCellRange(adr('B1'), adr('C4')) - const range2 = new AbsoluteCellRange(adr('B1', 1), adr('C4', 1)) - expect(range1.doesOverlap(range2)).toBe(false) - }) - - it('second on the right side of the first', () => { - const range1 = new AbsoluteCellRange(adr('B1'), adr('C4')) - const range2 = new AbsoluteCellRange(adr('D1'), adr('E4')) - expect(range1.doesOverlap(range2)).toBe(false) - }) - - it('second on the left side of the first', () => { - const range1 = new AbsoluteCellRange(adr('B1'), adr('C4')) - const range2 = new AbsoluteCellRange(adr('A1'), adr('A4')) - expect(range1.doesOverlap(range2)).toBe(false) - }) - - it('second on the top of the first', () => { - const range1 = new AbsoluteCellRange(adr('B3'), adr('C4')) - const range2 = new AbsoluteCellRange(adr('B1'), adr('C2')) - expect(range1.doesOverlap(range2)).toBe(false) - }) - - it('second on the bottom of the first', () => { - const range1 = new AbsoluteCellRange(adr('B3'), adr('C4')) - const range2 = new AbsoluteCellRange(adr('B5'), adr('C6')) - expect(range1.doesOverlap(range2)).toBe(false) - }) -}) - -describe('AbsoluteCellRange#width', () => { - it('a column', () => { - expect(new AbsoluteCellRange(adr('B1'), adr('B11')).width()).toBe(1) - }) - - it('more columns', () => { - expect(new AbsoluteCellRange(adr('B1'), adr('D11')).width()).toBe(3) - }) -}) - -describe('AbsoluteCellRange#height', () => { - it('a row', () => { - expect(new AbsoluteCellRange(adr('B2'), adr('K2')).height()).toBe(1) - }) - - it('more rows', () => { - expect(new AbsoluteCellRange(adr('B2'), adr('K4')).height()).toBe(3) - }) -}) - -describe('AbsoluteCellRange#removeSpan', () => { - it('rows below', () => { - const range = new AbsoluteCellRange(adr('A1'), adr('A4')) - range.removeSpan(new RowsSpan(0, 4, 5)) - expect(range.start.row).toBe(0) - expect(range.end.row).toBe(3) - }) - - it('rows above', () => { - const range = new AbsoluteCellRange(adr('A3'), adr('A5')) - range.removeSpan(new RowsSpan(0, 0, 1)) - expect(range.start.row).toBe(0) - expect(range.end.row).toBe(2) - }) - - it('middle of the range', () => { - const range = new AbsoluteCellRange(adr('A1'), adr('A5')) - range.removeSpan(new RowsSpan(0, 1, 2)) - expect(range.start.row).toBe(0) - expect(range.end.row).toBe(2) - }) - - it('start above range', () => { - const range = new AbsoluteCellRange(adr('A3'), adr('A5')) - range.removeSpan(new RowsSpan(0, 0, 3)) - expect(range.start.row).toBe(0) - expect(range.end.row).toBe(0) - }) - - it('end below range', () => { - const range = new AbsoluteCellRange(adr('A1'), adr('A5')) - range.removeSpan(new RowsSpan(0, 2, 5)) - expect(range.start.row).toBe(0) - expect(range.end.row).toBe(1) - }) - - it('whole range', () => { - const range = new AbsoluteCellRange(adr('A3'), adr('A5')) - range.removeSpan(new RowsSpan(0, 0, 5)) - expect(range.start.row).toBe(0) - expect(range.end.row).toBe(-1) - expect(range.height()).toBe(0) - }) -}) diff --git a/test/unit/generatorUtils.spec.ts b/test/unit/generatorUtils.spec.ts deleted file mode 100644 index efa90368c1..0000000000 --- a/test/unit/generatorUtils.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -import {empty, first, split} from '../../src/generatorUtils' - -describe('empty', () => { - it('works', () => { - expect(Array.from(empty())).toEqual([]) - }) -}) - -describe('split', () => { - it('works for empty case', () => { - const result = split(empty()) - - expect(result.value).toBe(undefined) - expect(Array.from(result.rest)).toEqual([]) - }) - - it('works for one element case', () => { - const arr = [42] - - const result = split(arr[Symbol.iterator]()) - - expect(result.value).toBe(42) - expect(Array.from(result.rest)).toEqual([]) - }) - - it('works for more elements case', () => { - const arr = [42, 43] - - const result = split(arr[Symbol.iterator]()) - - expect(result.value).toBe(42) - expect(Array.from(result.rest)).toEqual([43]) - }) -}) - -describe('first', () => { - it('works for empty case', () => { - expect(first(empty())).toBe(undefined) - }) - - it('works for one element case', () => { - const arr = [42] - - expect(first(arr[Symbol.iterator]())).toEqual(42) - }) - - it('works for more elements case', () => { - const arr = [42, 43] - - expect(first(arr[Symbol.iterator]())).toEqual(42) - }) -}) diff --git a/test/unit/graph-builder.spec.ts b/test/unit/graph-builder.spec.ts deleted file mode 100644 index 0041398fa3..0000000000 --- a/test/unit/graph-builder.spec.ts +++ /dev/null @@ -1,165 +0,0 @@ -import {HyperFormula} from '../../src' -import {Config} from '../../src/Config' -import {EmptyCellVertex, ValueCellVertex} from '../../src/DependencyGraph' -import {SheetSizeLimitExceededError} from '../../src/errors' -import {adr, colEnd, colStart, graphEdgesCount} from './testUtils' - -describe('GraphBuilder', () => { - it('build sheet with simple number cell', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ]) - - const vertex = engine.addressMapping.getCell(adr('A1')) - expect(vertex).toBeInstanceOf(ValueCellVertex) - expect(vertex!.getCellValue()).toBe(42) - }) - - it('build sheet with simple string cell', () => { - const engine = HyperFormula.buildFromArray([ - ['foo'], - ]) - - const vertex = engine.addressMapping.getCell(adr('A1')) - expect(vertex).toBeInstanceOf(ValueCellVertex) - expect(vertex!.getCellValue()).toBe('foo') - }) - - it('building for cell with null should give empty vertex', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=A1'], - ]) - - const vertex = engine.addressMapping.getCell(adr('A1')) - expect(vertex).toBeInstanceOf(EmptyCellVertex) - }) - - it('#buildGraph works with ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=A1:B1'], - ]) - - const a1 = engine.addressMapping.getCell(adr('A1')) - const b1 = engine.addressMapping.getCell(adr('B1')) - const a1b2 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('B1')) - const a2 = engine.addressMapping.getCell(adr('A2')) - expect(engine.graph.adjacentNodes(a1!)).toContain(a1b2) - expect(engine.graph.adjacentNodes(b1!)).toContain(a1b2) - expect(engine.graph.adjacentNodes(a1b2)).toContain(a2) - }) - - it('#buildGraph works with column ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=A:B'], - ]) - - const a1 = engine.addressMapping.getCell(adr('A1')) - const b1 = engine.addressMapping.getCell(adr('B1')) - const ab = engine.rangeMapping.getVertexOrThrow(colStart('A'), colEnd('B')) - const c1 = engine.addressMapping.getCell(adr('C1')) - expect(engine.graph.adjacentNodes(a1!)).toContain(ab) - expect(engine.graph.adjacentNodes(b1!)).toContain(ab) - expect(engine.graph.adjacentNodes(ab)).toContain(c1) - }) - - it('#loadSheet - it should build graph with only one RangeVertex', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=A1:B1'], - ['=A1:B1'], - ]) - - const a1 = engine.addressMapping.getCell(adr('A1')) - const b1 = engine.addressMapping.getCell(adr('B1')) - const a1b2 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('B1')) - const a2 = engine.addressMapping.getCell(adr('A2')) - const a3 = engine.addressMapping.getCell(adr('A3')) - - expect(engine.graph.existsEdge(a1!, a1b2)).toBe(true) - expect(engine.graph.existsEdge(b1!, a1b2)).toBe(true) - expect(engine.graph.existsEdge(a1b2, a2!)).toBe(true) - expect(engine.graph.existsEdge(a1b2, a3!)).toBe(true) - expect(engine.graph.getNodes().length).toBe( - 4 + // for cells above - 1, // for both ranges (reuse same ranges) - ) - }) - - it('build with range one row smaller', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '0'], - ['3', '=A1:A2'], - ['5', '=A1:A3'], - ]) - - const a3 = engine.addressMapping.getCell(adr('A3')) - const a1a2 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A2')) - const a1a3 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A3')) - - expect(engine.graph.existsEdge(a3!, a1a3)).toBe(true) - expect(engine.graph.existsEdge(a1a2, a1a3)).toBe(true) - expect(graphEdgesCount(engine.graph)).toBe( - 2 + // from cells to range(A1:A2) - 2 + // from A3 and range(A1:A2) to range(A1:A3) - 2, // from range vertexes to formulas - ) - }) - - it('#buildGraph should work even if range dependencies are empty', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '0', '=SUM(A1:B2)'], - ]) - - expect(engine.graph.getNodes().length).toBe( - 3 + // for cells above - 1 + // for range vertex - 2, // for 2 EmptyCellVertex instances - ) - expect(graphEdgesCount(engine.graph)).toBe( - 2 + // from cells to range vertex - 2 + // from EmptyCellVertex instances to range vertices - 1, // from range to cell with SUM - ) - }) - - it("optimization doesn't work if smaller range is after bigger", () => { - const engine = HyperFormula.buildFromArray([ - ['1', '0'], - ['3', '=A1:A3'], - ['5', '=A1:A2'], - ]) - - const a1a2 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A2')) - const a1a3 = engine.rangeMapping.getVertexOrThrow(adr('A1'), adr('A3')) - const a2 = engine.addressMapping.getCell(adr('A2')) - expect(engine.graph.existsEdge(a2!, a1a3)).toBe(true) - expect(engine.graph.existsEdge(a2!, a1a2)).toBe(true) - expect(engine.graph.existsEdge(a1a2, a1a3)).toBe(false) - expect(graphEdgesCount(engine.graph)).toBe( - 3 + // from 3 cells to range(A1:A2) - 2 + // from 2 cells to range(A1:A2) - 2, // from range vertexes to formulas - ) - }) -}) - -describe('Sheet size limits', () => { - it('should throw error when trying to build engine with too many columns', () => { - const maxColumns = Config.defaultConfig.maxColumns - const sheet = [new Array(maxColumns + 1).fill('')] - - expect(() => { - HyperFormula.buildFromArray(sheet) - }).toThrow(new SheetSizeLimitExceededError()) - }) - - it('should throw error when trying to build engine with too many rows', () => { - const maxRows = Config.defaultConfig.maxRows - const sheet = new Array(maxRows + 1).fill(['']) - - expect(() => { - HyperFormula.buildFromArray(sheet) - }).toThrow(new SheetSizeLimitExceededError()) - }) -}) diff --git a/test/unit/graph-dependencies-queries.spec.ts b/test/unit/graph-dependencies-queries.spec.ts deleted file mode 100644 index ea35ed7057..0000000000 --- a/test/unit/graph-dependencies-queries.spec.ts +++ /dev/null @@ -1,169 +0,0 @@ -import {HyperFormula, SimpleCellAddress} from '../../src' -import {simpleCellRange} from '../../src/AbsoluteCellRange' -import {adr} from './testUtils' -import {ExpectedValueOfTypeError} from '../../src/errors' - -describe('address queries', () => { - describe('getCellDependents', () => { - it('should return reversed dependencies', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3], - ['=SUM(A1:B1)', '=SUMSQ(A1:B1)'], - ['=A2+B2'], - ]) - expect(engine.getCellDependents(adr('A1'))).toEqual([simpleCellRange(adr('A1'), adr('B1'))]) - expect(engine.getCellDependents(adr('D1'))).toEqual([]) - expect(engine.getCellDependents(adr('A2'))).toEqual([adr('A3')]) - expect(engine.getCellDependents(adr('B2'))).toEqual([adr('A3')]) - expect(engine.getCellDependents(adr('A3'))).toEqual([]) - - expect(engine.getCellDependents(simpleCellRange(adr('A1'), adr('B1')))).toEqual([adr('A2'), adr('B2')]) - expect(engine.getCellDependents(simpleCellRange(adr('A3'), adr('B3')))).toEqual([]) - }) - - it('should return reversed dependencies across sheets', () => { - const engine = HyperFormula.buildFromSheets( - { - 'DataSheet': [[1, 2, 3, '=A1']], - 'DependentSheet': [['=DataSheet!A1', '=DataSheet!A1', '=DataSheet!A1+DataSheet!B1', '=A1']], - } - ) - - const dataSheetId = engine.getSheetId('DataSheet') - const dependentSheetId = engine.getSheetId('DependentSheet') - - expect(engine.getCellDependents(adr('A1', dataSheetId))).toEqual([ - adr('D1', dataSheetId), - adr('A1', dependentSheetId), - adr('B1', dependentSheetId), - adr('C1', dependentSheetId) - ]) - expect(engine.getCellDependents(adr('B1', dataSheetId))).toEqual([adr('C1', dependentSheetId)]) - expect(engine.getCellDependents(adr('C1', dataSheetId))).toEqual([]) - expect(engine.getCellDependents(adr('D1', dataSheetId))).toEqual([]) - }) - - it('should return only immediate reversed dependencies', () => { - const hfInstance = HyperFormula.buildFromArray([[ '1', '=A1', '=A1+B1', '=B1+C1' ]]) - - const dependents = hfInstance.getCellDependents({ sheet: 0, col: 0, row: 0 }) - - expect(dependents).toEqual([ - { sheet: 0, col: 1, row: 0 }, - { sheet: 0, col: 2, row: 0 }, - ]) - }) - - it('should return named expressions dependents as cell references in sheet -1', () => { - const hfInstance = HyperFormula.buildFromArray([[ '1' ]]) - hfInstance.addNamedExpression('foo', '=Sheet1!$A$1') - - const dependents = hfInstance.getCellDependents({ sheet: 0, col: 0, row: 0 }) - - expect(dependents).toEqual([ - { sheet: -1, col: 0, row: 0 }, - ]) - }) - - it('should throw error if address is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3], - ['=SUM(A1:B1)', '=SUMSQ(A1:B1)'], - ['=A2+B2'], - ]) - - const malformedAddress = {col: 0} as SimpleCellAddress - - expect(() => { - engine.getCellDependents(malformedAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress | SimpleCellRange', malformedAddress.toString())) - }) - - it('should return empty array when sheet does not exist', () => { - const engine = HyperFormula.buildFromArray([[1]]) - - const nonExistentSheetId = 999 - - expect(engine.getCellDependents({ sheet: nonExistentSheetId, col: 0, row: 0 })).toEqual([]) - }) - }) - - describe('getCellPrecedents', () => { - it('should return dependencies', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3], - ['=SUM(A1:B1)', '=SUMSQ(A1:B1)'], - ['=A2+B2'], - ]) - expect(engine.getCellPrecedents(adr('A1'))).toEqual([]) - expect(engine.getCellPrecedents(adr('D1'))).toEqual([]) - expect(engine.getCellPrecedents(adr('A2'))).toEqual([simpleCellRange(adr('A1'), adr('B1'))]) - expect(engine.getCellPrecedents(adr('B2'))).toEqual([simpleCellRange(adr('A1'), adr('B1'))]) - expect(engine.getCellPrecedents(adr('A3'))).toEqual([adr('A2'), adr('B2')]) - - expect(engine.getCellPrecedents(simpleCellRange(adr('A1'), adr('B1')))).toEqual([adr('A1'), adr('B1')]) - expect(engine.getCellPrecedents(simpleCellRange(adr('A3'), adr('B3')))).toEqual([]) - }) - - it('should return only immediate dependencies', () => { - const hfInstance = HyperFormula.buildFromArray([[ '1', '2', '=A1', '=B1+C1' ]]) - - const precedents = hfInstance.getCellPrecedents({ sheet: 0, col: 3, row: 0 }) - - expect(precedents).toEqual([ - { sheet: 0, col: 1, row: 0 }, - { sheet: 0, col: 2, row: 0 }, - ]) - }) - - it('should return named expressions dependencies as cell references in sheet -1', () => { - const hfInstance = HyperFormula.buildFromArray([[ '=foo' ]]) - hfInstance.addNamedExpression('foo', '=42') - - const dependents = hfInstance.getCellPrecedents({ sheet: 0, col: 0, row: 0 }) - - expect(dependents).toEqual([ - { sheet: -1, col: 0, row: 0 }, - ]) - }) - - it('should throw error if address is a malformed SimpleCellAddress', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3], - ['=SUM(A1:B1)', '=SUMSQ(A1:B1)'], - ['=A2+B2'], - ]) - const malformedAddress = {col: 0} as SimpleCellAddress - expect(() => { - engine.getCellPrecedents(malformedAddress) - }).toThrow(new ExpectedValueOfTypeError('SimpleCellAddress | SimpleCellRange', malformedAddress.toString())) - }) - - it('should correctly process cell dependencies with multiple range types', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3, 4], - [5, 6, 7, 8], - ['=SUM(A1:D1)', '=SUM(A2:D2)', '=SUM(A1:D2)', '=A1+B1'], - ]) - - expect(engine.getCellValue(adr('A3'))).toBe(10) - expect(engine.getCellValue(adr('B3'))).toBe(26) - expect(engine.getCellValue(adr('C3'))).toBe(36) - expect(engine.getCellValue(adr('D3'))).toBe(3) - }) - - it('should correctly process named expression dependencies', () => { - const engine = HyperFormula.buildFromArray([ - [42], - ['=MyValue * 2'], - ], {}, [ - {name: 'MyValue', expression: '=Sheet1!$A$1'}, - ]) - - expect(engine.getCellValue(adr('A2'))).toBe(84) - - engine.setCellContents(adr('A1'), 100) - expect(engine.getCellValue(adr('A2'))).toBe(200) - }) - }) -}) diff --git a/test/unit/graph-garbage-collection.spec.ts b/test/unit/graph-garbage-collection.spec.ts deleted file mode 100644 index c7c0aa0603..0000000000 --- a/test/unit/graph-garbage-collection.spec.ts +++ /dev/null @@ -1,350 +0,0 @@ -import {HyperFormula} from '../../src' -import {AbsoluteCellRange} from '../../src/AbsoluteCellRange' -import {adr} from './testUtils' - -describe('vertex counting', () => { - it('one-time formula', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'] - ]) - expect(engine.dependencyGraph.graph.getNodes().length).toBe(4) - engine.calculateFormula('=SUM(A1:B2)', 0) - expect(engine.dependencyGraph.graph.getNodes().length).toBe(4) - }) - - it('cruds', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'] - ]) - expect(engine.dependencyGraph.graph.getNodes().length).toBe(4) - engine.setCellContents(adr('A1'), '=SUM(A2:B2)') - expect(engine.dependencyGraph.graph.getNodes().length).toBe(5) - engine.setCellContents(adr('A1'), 1) - expect(engine.dependencyGraph.graph.getNodes().length).toBe(4) - }) -}) - -describe('range mapping', () => { - it('one-time formula', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'] - ]) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - engine.calculateFormula('=SUM(A1:B2)', 0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('cruds', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'] - ]) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - engine.setCellContents(adr('A1'), '=SUM(A2:B2)') - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(1) - engine.setCellContents(adr('A1'), 1) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) -}) - -function randomInteger(min: number, max: number) { - return Math.floor(Math.random() * (max - min + 1)) + min -} - -describe('larger tests', () => { - - it('large fixed', () => { - const arr = [ - [ - '=SUM(B2:C4)', - ], - [ - null, - '=SUM(A3:B3)', - ], - [ - '=SUM(B2:C3)', - ], - ] - const engine = HyperFormula.buildFromArray(arr) - for (let x = 0; x < 3; x++) { - for (let y = 0; y < 3; y++) { - engine.setCellContents({sheet: 0, col: x, row: y}, null) - } - } - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('large fixed #2', () => { - const arr = [ - [ - null, - '=SUM(A1:A2)', - '=SUM(A1:A1)', - '=SUM(A1:A2)', - ], - ] - const engine = HyperFormula.buildFromArray(arr) - engine.setCellContents({sheet: 0, col: 1, row: 0}, null) - engine.setCellContents({sheet: 0, col: 2, row: 0}, null) - engine.setCellContents({sheet: 0, col: 3, row: 0}, null) - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('large fixed #3', () => { - const arr = [ - [ - '=SUM(A1:B1)', - ], - ] - const engine = HyperFormula.buildFromArray(arr) - - engine.setCellContents({sheet: 0, col: 0, row: 0}, null) - - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('large fixed #4', () => { - const arr = [ - [ - null, '=SUM(A1:A1)', '=SUM(A1:A2)', '=SUM(A1:A3)', '=SUM(A1:A4)', - ], - ] - const engine = HyperFormula.buildFromArray(arr) - - engine.setCellContents({sheet: 0, col: 1, row: 0}, null) - engine.setCellContents({sheet: 0, col: 2, row: 0}, null) - engine.setCellContents({sheet: 0, col: 3, row: 0}, null) - engine.setCellContents({sheet: 0, col: 4, row: 0}, null) - - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('repeat the same crud', () => { - const engine = HyperFormula.buildFromArray([]) - for (let tmp = 0; tmp < 3; tmp++) { - for (let x = 0; x < 10; x++) { - for (let y = 0; y < 10; y++) { - const col1 = randomInteger(2, 7) - const row1 = randomInteger(2, 7) - const col2 = col1 + randomInteger(-2, 2) - const row2 = row1 + randomInteger(-2, 2) - const startAddress = engine.simpleCellAddressToString({ - sheet: 0, - row: Math.min(row1, row2), - col: Math.min(col1, col2) - }, 0) as string - const endAddress = engine.simpleCellAddressToString({ - sheet: 0, - row: Math.max(row1, row2), - col: Math.max(col1, col2) - }, 0) as string - const formula = '=SUM(' + startAddress + ':' + endAddress + ')' - engine.setCellContents({sheet: 0, col: x, row: y}, formula) - } - } - } - for (let x = 0; x < 10; x++) { - for (let y = 0; y < 10; y++) { - engine.setCellContents({sheet: 0, col: x, row: y}, null) - } - } - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) -}) - -describe('cruds', () => { - it('should collect empty vertices when bigger range is no longer bind to smaller range #1', () => { - const engine = HyperFormula.buildFromArray([ - [], - [], - [], - [], - ['=SUM(A1:A2)'], - ['=SUM(A1:A3)'], - ]) - - engine.removeRows(0, [0, 2]) - engine.removeRows(0, [2, 2]) - - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('should collect empty vertices when bigger range is no longer bind to smaller range #2', () => { - const engine = HyperFormula.buildFromArray([ - [], - [], - [], - ['=SUM(A1:A2)'], - ['=SUM(A1:A3)'], - ]) - - engine.addRows(0, [2, 1]) - engine.removeRows(0, [0, 2]) - engine.removeRows(0, [2, 2]) - - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('should collect empty vertices when bigger range is no longer bind to smaller range #3', () => { - const engine = HyperFormula.buildFromArray([ - [], - [], - [], - ['=SUM(A1:A2)'], - ['=SUM(A1:A3)'], - ]) - engine.addRows(0, [2, 1]) - - engine.removeRows(0, [4, 2]) - - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('should collect empty vertices when bigger range is no longer bind to smaller range #4', () => { - const engine = HyperFormula.buildFromArray([ - [], - [], - [], - ['=SUM(A1:A3)'], - ['=SUM(A1:A2)'], - ]) - engine.addRows(0, [1, 1]) - - engine.setCellContents(adr('A1'), [[1], [2], [3], [4]]) - - expect(engine.getCellValue(adr('A5'))).toBe(10) - expect(engine.getCellValue(adr('A6'))).toBe(6) - - engine.removeRows(0, [0, 6]) - - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('should collect empty vertices when bigger range is no longer bind to smaller range #5', () => { - const engine = HyperFormula.buildFromArray([ - [], - [], - [], - [], - ['=SUM(A1:A4)'], - ['=SUM(A1:A3)'], - ]) - - engine.setCellContents(adr('A1'), [[1], [2], [3], [4]]) - - expect(engine.getCellValue(adr('A5'))).toBe(10) - expect(engine.getCellValue(adr('A6'))).toBe(6) - - engine.removeRows(0, [0, 6]) - - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('should collect empty vertices when bigger range is no longer bind to smaller range #6', () => { - const engine = HyperFormula.buildFromArray([ - [1], - [2], - [3], - [4], - ['=SUM(A1:A4)'], - ['=SUM(A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A5'))).toBe(10) - expect(engine.getCellValue(adr('A6'))).toBe(6) - - engine.removeRows(0, [0, 6]) - - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('should collect empty vertices when bigger range is no longer bind to smaller range #7', () => { - const engine = HyperFormula.buildFromArray([ - [], - [], - [], - ['=SUM(A1:A2)', '=SUM(B1:B3)'], - ['=SUM(A1:A3)', '=SUM(B1:B2)'], - ]) - engine.addRows(0, [1, 1]) - - engine.setCellContents(adr('A1'), [[1, 1], [2, 2], [3, 3], [4, 4]]) - - expect(engine.getCellValue(adr('A5'))).toBe(6) - expect(engine.getCellValue(adr('B5'))).toBe(10) - expect(engine.getCellValue(adr('A6'))).toBe(10) - expect(engine.getCellValue(adr('B6'))).toBe(6) - - engine.removeRows(0, [0, 6]) - - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('column adding', () => { - const engine = HyperFormula.buildFromArray([ - [0, 0], - [0, 0], - [0, 0], - ['=SUM(A1:B2)'], - ['=SUM(A1:B3)'] - ]) - engine.addColumns(0, [1, 1]) - engine.setCellContents(adr('B3'), 1) - expect(engine.getCellSerialized(adr('A4'))).toBe('=SUM(A1:C2)') - expect(engine.getCellSerialized(adr('A5'))).toBe('=SUM(A1:C3)') - expect(engine.getCellValue(adr('A4'))).toBe(0) - expect(engine.getCellValue(adr('A5'))).toBe(1) - }) - - it('movecell', () => { - const engine = HyperFormula.buildFromArray([ - [1], - [2], - [3], - [4], - ['=SUM(A1:A3)'], - ['=SUM(A1:A4)'], - ]) - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 3), adr('B1')) - engine.setCellContents(adr('B1'), null) - engine.setCellContents(adr('B2'), null) - engine.setCellContents(adr('B3'), null) - engine.setCellContents(adr('A4'), null) - engine.setCellContents(adr('A5'), null) - engine.setCellContents(adr('A6'), null) - - expect(engine.dependencyGraph.graph.getNodes().length).toBe(0) - expect(engine.dependencyGraph.rangeMapping.getNumberOfRangesInSheet(0)).toBe(0) - }) - - it('addColumns after addRows', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2', '=SUM($A$1:A1)'], - ['3', '=SUM($A$1:A2)'], - ]) - - engine.addRows(0, [1, 1]) - engine.addColumns(0, [0, 1]) - engine.removeColumns(0, [0, 1]) - - expect(engine.getCellValue(adr('B3'))).toEqual(1) - expect(engine.getCellValue(adr('B4'))).toEqual(3) - }) -}) diff --git a/test/unit/graph-vertex.spec.ts b/test/unit/graph-vertex.spec.ts deleted file mode 100644 index 7ee458689f..0000000000 --- a/test/unit/graph-vertex.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {Graph, ValueCellVertex, Vertex} from '../../src/DependencyGraph' - -const dummyGetDependenciesQuery: () => any[] = () => [] - -describe('Graph with Vertex', () => { - it('#addNodeIfNotExists works correctly with Vertex instances', () => { - const graph = new Graph(dummyGetDependenciesQuery) - - const v1 = new ValueCellVertex("1'", "1'") - const v2 = new ValueCellVertex('2', '2') - graph.addNodeIfNotExists(v1) - graph.addNodeIfNotExists(v1) - graph.addNodeIfNotExists(v2) - - expect(graph.getNodes().length).toBe(2) - }) -}) diff --git a/test/unit/graph.spec.ts b/test/unit/graph.spec.ts deleted file mode 100644 index 0525d70835..0000000000 --- a/test/unit/graph.spec.ts +++ /dev/null @@ -1,612 +0,0 @@ -import {Graph} from '../../src/DependencyGraph' -import {DependencyQuery, GraphNode} from '../../src/DependencyGraph/Graph' -import {graphEdgesCount} from './testUtils' - -class IdentifiableString implements GraphNode { - public idInGraph?: number = undefined - - constructor( - public str: string) { - } -} - -const dummyDependencyQuery: DependencyQuery = () => [] - -describe('Graph class', () => { - describe('addNodeIfNotExists', () => { - it('adds a node to the empty graph', () => { - const graph = new Graph(dummyDependencyQuery) - - const node = new IdentifiableString('foo') - graph.addNodeIfNotExists(node) - - expect(graph.getNodes().length).toBe(1) - }) - - it('does not add duplicate nodes', () => { - const graph = new Graph(dummyDependencyQuery) - - const node = new IdentifiableString('foo') - graph.addNodeIfNotExists(node) - graph.addNodeIfNotExists(node) - - expect(graph.getNodes().length).toBe(1) - }) - - it('keeps existing edges when dealing with duplicates', () => { - const graph = new Graph(dummyDependencyQuery) - - const node0 = new IdentifiableString('foo') - const node1 = new IdentifiableString('bar') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - expect(graph.adjacentNodes(node0)).toEqual(new Set([])) - - graph.addEdge(node0, node1) - expect(graph.adjacentNodes(node0)).toEqual(new Set([node1])) - - graph.addNodeIfNotExists(node0) - - expect(graph.adjacentNodes(node0)).toEqual(new Set([node1])) - }) - }) - - describe('removeNode', () => { - it('removes a node if it exists', () => { - const graph = new Graph(dummyDependencyQuery) - - const node = new IdentifiableString('foo') - graph.addNodeIfNotExists(node) - graph.removeNode(node) - - expect(graph.getNodes().length).toBe(0) - }) - - it('throws error when node does not exist', () => { - const graph = new Graph(dummyDependencyQuery) - - expect(() => graph.removeNode(new IdentifiableString('foo'))).toThrowError(/Unknown node/) - }) - }) - - describe('hasNode', () => { - it('returns false when graph is empty', () => { - const graph = new Graph(dummyDependencyQuery) - - expect(graph.hasNode(new IdentifiableString('foo'))).toBe(false) - }) - - it('returns true if node exists', () => { - const graph = new Graph(dummyDependencyQuery) - - const node = new IdentifiableString('foo') - graph.addNodeIfNotExists(node) - - expect(graph.hasNode(node)).toBe(true) - }) - - it('returns false if node does not exist', () => { - const graph = new Graph(dummyDependencyQuery) - - const node = new IdentifiableString('foo') - graph.addNodeIfNotExists(node) - - expect(graph.hasNode(new IdentifiableString('foo'))).toBe(false) - }) - - it('returns false if node was removed', () => { - const graph = new Graph(dummyDependencyQuery) - - const node = new IdentifiableString('foo') - graph.addNodeIfNotExists(node) - graph.removeNode(node) - - expect(graph.hasNode(node)).toBe(false) - }) - }) - - describe('addEdge', () => { - it('does not add duplicated edges', () => { - const graph = new Graph(dummyDependencyQuery) - - const node0 = new IdentifiableString('foo') - const node1 = new IdentifiableString('bar') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addEdge(node0, node1) - graph.addEdge(node0, node1) - - expect(graph.adjacentNodes(node0)).toEqual(new Set([node1])) - }) - - it('throws error when the origin node is not present', () => { - const graph = new Graph(dummyDependencyQuery) - const node = new IdentifiableString('target') - graph.addNodeIfNotExists(node) - - expect(() => { - graph.addEdge(new IdentifiableString('origin'), node) - }).toThrowError(/Unknown node/) - }) - - it('throws error when the target node is not present', () => { - const graph = new Graph(dummyDependencyQuery) - const node = new IdentifiableString('origin') - graph.addNodeIfNotExists(node) - - expect(() => { - graph.addEdge(node, new IdentifiableString('target')) - }).toThrowError(/Unknown node/) - }) - }) - - describe('removeEdge', () => { - it('throws error when source node does not exist', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('x0') - const node1 = new IdentifiableString('x1') - graph.addNodeIfNotExists(node1) - - expect(() => graph.removeEdge(node0, node1)).toThrowError(/Unknown node/) - }) - - it('throws error when target node does not exist', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('x0') - const node1 = new IdentifiableString('x1') - graph.addNodeIfNotExists(node0) - - expect(() => graph.removeEdge(node0, node1)).toThrowError(/Unknown node/) - }) - - it('throws error when edge does not exist', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('x0') - const node1 = new IdentifiableString('x1') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - - expect(() => graph.removeEdge(node0, node1)).toThrowError('Edge does not exist') - }) - - it('removes an edge it it exists', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('x0') - const node1 = new IdentifiableString('x1') - - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - expect(graphEdgesCount(graph)).toEqual(0) - - graph.addEdge(node0, node1) - expect(graphEdgesCount(graph)).toEqual(1) - expect(graph.existsEdge(node0, node1)).toBe(true) - - graph.removeEdge(node0, node1) - expect(graphEdgesCount(graph)).toEqual(0) - expect(graph.existsEdge(node0, node1)).toBe(false) - }) - }) - - describe('existsEdge', () => { - it('returns true if edge is present in the graph', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('foo') - const node1 = new IdentifiableString('bar') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addEdge(node0, node1) - - expect(graph.existsEdge(node0, node1)).toBe(true) - }) - - it('returns false if edge is not present', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('foo') - const node1 = new IdentifiableString('bar') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - - expect(graph.existsEdge(node0, node1)).toBe(false) - }) - - it('returns false if nodes are not present in the graph', () => { - const graph = new Graph(dummyDependencyQuery) - - expect(graph.existsEdge(new IdentifiableString('foo'), new IdentifiableString('bar'))).toBe(false) - }) - }) - - describe('adjacentNodes', () => { - it('returns all target nodes adjacent to the given node', () => { - const graph = new Graph(dummyDependencyQuery) - - const node0 = new IdentifiableString('foo') - const node1 = new IdentifiableString('bar') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addEdge(node0, node1) - - expect(graph.adjacentNodes(node0)).toEqual(new Set([node1])) - }) - - it('throws error if the source node is not present in the graph', () => { - const graph = new Graph(dummyDependencyQuery) - - const node0 = new IdentifiableString('foo') - const node1 = new IdentifiableString('bar') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addEdge(node0, node1) - - expect(() => graph.adjacentNodes(new IdentifiableString('baz'))).toThrowError(/Unknown node/) - }) - }) - - describe('adjacentNodesCount', () => { - it('returns number of outgoing edges from a given node', () => { - const graph = new Graph(dummyDependencyQuery) - - const node0 = new IdentifiableString('foo') - const node1 = new IdentifiableString('bar') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addEdge(node0, node1) - - expect(graph.adjacentNodesCount(node0)).toEqual(1) - }) - - it('throws error if the source node is not present in the graph', () => { - const graph = new Graph(dummyDependencyQuery) - - const node0 = new IdentifiableString('foo') - const node1 = new IdentifiableString('bar') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addEdge(node0, node1) - - expect(() => graph.adjacentNodesCount(new IdentifiableString('baz'))).toThrowError(/Unknown node/) - }) - }) - - describe('topSortWithScc', () => { - it('works for empty graph', () => { - const graph = new Graph(dummyDependencyQuery) - - expect(graph.topSortWithScc().sorted).toEqual([]) - expect(graph.topSortWithScc().cycled).toEqual([]) - }) - - it('returns isolated vertices', () => { - const graph = new Graph(dummyDependencyQuery) - const node = new IdentifiableString('foo') - graph.addNodeIfNotExists(node) - - expect(graph.topSortWithScc().sorted).toEqual([node]) - expect(graph.topSortWithScc().cycled).toEqual([]) - }) - - it('returns vertices in order corresponding to the edge direction', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('foo') - const node1 = new IdentifiableString('bar') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addEdge(node1, node0) - - expect(graph.topSortWithScc().sorted).toEqual([node1, node0]) - expect(graph.topSortWithScc().cycled).toEqual([]) - }) - - it('works for 4-edges acyclic graph', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('x0') - const node1 = new IdentifiableString('x1') - const node2 = new IdentifiableString('x2') - const node3 = new IdentifiableString('x3') - const node4 = new IdentifiableString('x4') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addNodeIfNotExists(node2) - graph.addNodeIfNotExists(node3) - graph.addNodeIfNotExists(node4) - graph.addEdge(node0, node2) - graph.addEdge(node1, node2) - graph.addEdge(node2, node3) - graph.addEdge(node4, node3) - - expect(graph.topSortWithScc().sorted).toEqual([node0, node1, node2, node4, node3]) - expect(graph.topSortWithScc().cycled).toEqual([]) - }) - - it('works for a graph with multiple connected components', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('x0') - const node1 = new IdentifiableString('x1') - const node2 = new IdentifiableString('x2') - const node3 = new IdentifiableString('x3') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addNodeIfNotExists(node2) - graph.addNodeIfNotExists(node3) - graph.addEdge(node0, node2) - graph.addEdge(node1, node3) - - expect(graph.topSortWithScc().sorted).toEqual([node0, node1, node2, node3]) - expect(graph.topSortWithScc().cycled).toEqual([]) - }) - - it('detects cycles', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('x0') - const node1 = new IdentifiableString('x1') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addEdge(node0, node1) - graph.addEdge(node1, node0) - - expect(graph.topSortWithScc().sorted).toEqual([]) - expect(new Set(graph.topSortWithScc().cycled)).toEqual(new Set([node0, node1])) - }) - - it('detects 1-node cycles', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('x0') - const node1 = new IdentifiableString('x1') - const node2 = new IdentifiableString('x2') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addNodeIfNotExists(node2) - graph.addEdge(node0, node1) - graph.addEdge(node1, node2) - graph.addEdge(node1, node1) - - expect(graph.topSortWithScc().sorted).toEqual([node0, node2]) - expect(graph.topSortWithScc().cycled).toEqual([node1]) - }) - }) - - describe('getTopSortedWithSccSubgraphFrom', () => { - it('calls the operatingFunction callback for sorted nodes', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('foo') - const node1 = new IdentifiableString('bar') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - - const operatingFunction = jasmine.createSpy().and.returnValue(true) - const onCycle = jasmine.createSpy() - - graph.getTopSortedWithSccSubgraphFrom([node0], operatingFunction, onCycle) - - expect(operatingFunction).toHaveBeenCalledTimes(1) - expect(operatingFunction.calls.argsFor(0)).toContain(node0) - }) - - it('works for graph with an edge', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('foo') - const node1 = new IdentifiableString('bar') - - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addEdge(node0, node1) - - const operatingFunction = jasmine.createSpy().and.returnValue(true) - const onCycle = jasmine.createSpy() - - graph.getTopSortedWithSccSubgraphFrom([node0], operatingFunction, onCycle) - - expect(operatingFunction).toHaveBeenCalledTimes(2) - expect(operatingFunction.calls.argsFor(0)).toContain(node0) - expect(operatingFunction.calls.argsFor(1)).toContain(node1) - }) - - it('omits nodes not reachable from the "modifiedNodes" array', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('foo') - const node1 = new IdentifiableString('bar') - - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addEdge(node0, node1) - - const operatingFunction = jasmine.createSpy().and.returnValue(false) - const onCycle = jasmine.createSpy() - - graph.getTopSortedWithSccSubgraphFrom([node0], operatingFunction, onCycle) - - expect(operatingFunction).toHaveBeenCalledTimes(1) - expect(operatingFunction.calls.argsFor(0)).toContain(node0) - }) - - it('calls the operatingFunction for a node not included but reachable from the "modifiedNodes" array', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('foo') - const node1 = new IdentifiableString('bar') - const node2 = new IdentifiableString('baz') - - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addNodeIfNotExists(node2) - graph.addEdge(node0, node2) - graph.addEdge(node1, node2) - - const operatingFunction = jasmine.createSpy().and.callFake((node: IdentifiableString) => node === node0) - const onCycle = jasmine.createSpy() - - graph.getTopSortedWithSccSubgraphFrom([node0, node1], operatingFunction, onCycle) - - expect(operatingFunction).toHaveBeenCalledTimes(3) - expect(operatingFunction.calls.argsFor(2)).toContain(node2) - }) - - it('calls onCycle callback for nodes that are on cycle', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('foo') - const node1 = new IdentifiableString('c0') - const node2 = new IdentifiableString('c1') - const node3 = new IdentifiableString('c2') - - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addNodeIfNotExists(node2) - graph.addNodeIfNotExists(node3) - graph.addEdge(node0, node1) - graph.addEdge(node1, node2) - graph.addEdge(node2, node3) - graph.addEdge(node3, node1) - - const operatingFunction = jasmine.createSpy().and.returnValue(true) - const onCycle = jasmine.createSpy() - const cycled = graph.getTopSortedWithSccSubgraphFrom([node0], operatingFunction, onCycle).cycled - - expect(operatingFunction).toHaveBeenCalledTimes(1) - expect(onCycle).toHaveBeenCalledTimes(3) - expect(cycled).toEqual([node1, node2, node3]) - }) - - it('does not call operatingFunction callback for nodes that are on cycle', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('c0') - const node1 = new IdentifiableString('c1') - const node2 = new IdentifiableString('c2') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addNodeIfNotExists(node2) - graph.addEdge(node0, node1) - graph.addEdge(node1, node2) - graph.addEdge(node2, node0) - - const operatingFunction = jasmine.createSpy().and.returnValue(true) - const onCycle = jasmine.createSpy() - const cycled = graph.getTopSortedWithSccSubgraphFrom([node0], operatingFunction, onCycle).cycled - - expect(operatingFunction).not.toHaveBeenCalled() - expect(cycled).toEqual([node0, node1, node2]) - }) - - it('detects a cycle consisting of nodes not included but reachable from the "modifiedNodes" array', () => { - const graph = new Graph(dummyDependencyQuery) - const node0 = new IdentifiableString('foo') - const node1 = new IdentifiableString('c0') - const node2 = new IdentifiableString('c1') - const node3 = new IdentifiableString('c2') - graph.addNodeIfNotExists(node0) - graph.addNodeIfNotExists(node1) - graph.addNodeIfNotExists(node2) - graph.addNodeIfNotExists(node3) - graph.addEdge(node0, node1) - graph.addEdge(node1, node2) - graph.addEdge(node2, node3) - graph.addEdge(node3, node1) - - const operatingFunction = jasmine.createSpy().and.returnValue(true) - const onCycle = jasmine.createSpy() - const cycled = graph.getTopSortedWithSccSubgraphFrom([node0], operatingFunction, onCycle).cycled - - expect(operatingFunction).toHaveBeenCalledTimes(1) - expect(cycled).toEqual([node1, node2, node3]) - }) - }) - - describe('markNodeAsVolatile', () => { - it('adds a node to volatile nodes array', () => { - const graph = new Graph(dummyDependencyQuery) - const node = new IdentifiableString('foo') - - graph.addNodeIfNotExists(node) - graph.markNodeAsVolatile(node) - - expect(graph.getDirtyAndVolatileNodes()).toEqual([node]) - }) - - it('does nothing if node is not in a graph', () => { - const graph = new Graph(dummyDependencyQuery) - const node = new IdentifiableString('foo') - - graph.markNodeAsVolatile(node) - expect(graph.getDirtyAndVolatileNodes()).toEqual([]) - }) - }) - - describe('markNodeAsChangingWithStructure', () => { - it('adds a node to special nodes structural changes array', () => { - const graph = new Graph(dummyDependencyQuery) - const node = new IdentifiableString('foo') - - graph.addNodeIfNotExists(node) - graph.markNodeAsChangingWithStructure(node) - graph.markChangingWithStructureNodesAsDirty() - - expect(graph.getDirtyAndVolatileNodes()).toEqual([node]) - }) - - it('does nothing if node is not in a graph', () => { - const graph = new Graph(dummyDependencyQuery) - const node = new IdentifiableString('foo') - - graph.markNodeAsChangingWithStructure(node) - graph.markChangingWithStructureNodesAsDirty() - - expect(graph.getDirtyAndVolatileNodes()).toEqual([]) - }) - }) - - describe('markNodeAsInfiniteRange', () => { - it('adds a node to the infinite ranges array', () => { - const graph = new Graph(dummyDependencyQuery) - const node = new IdentifiableString('foo') - - graph.addNodeIfNotExists(node) - graph.markNodeAsInfiniteRange(node) - - expect(graph.getInfiniteRanges()).toEqual([node]) - }) - - it('does nothing if node is not in a graph', () => { - const graph = new Graph(dummyDependencyQuery) - const node = new IdentifiableString('foo') - - graph.markNodeAsInfiniteRange(node) - expect(graph.getInfiniteRanges()).toEqual([]) - }) - }) - - describe('idInGraph property', () => { - it('is set when node is added to graph', () => { - const graph = new Graph(dummyDependencyQuery) - const node = new IdentifiableString('foo') - - expect(node.idInGraph).toBeUndefined() - - graph.addNodeIfNotExists(node) - - expect(node.idInGraph).toBeDefined() - }) - - it('is reset to undefined when node is removed from graph', () => { - const graph = new Graph(dummyDependencyQuery) - const node = new IdentifiableString('foo') - - graph.addNodeIfNotExists(node) - expect(node.idInGraph).toBeDefined() - - graph.removeNode(node) - - expect(node.idInGraph).toBeUndefined() - }) - - it('allows node to be re-added after removal', () => { - const graph = new Graph(dummyDependencyQuery) - const node = new IdentifiableString('foo') - - graph.addNodeIfNotExists(node) - graph.removeNode(node) - graph.addNodeIfNotExists(node) - - expect(graph.hasNode(node)).toBe(true) - expect(node.idInGraph).toBeDefined() - }) - }) -}) diff --git a/test/unit/graphComparator.ts b/test/unit/graphComparator.ts deleted file mode 100644 index e9d977bced..0000000000 --- a/test/unit/graphComparator.ts +++ /dev/null @@ -1,133 +0,0 @@ -import {deepStrictEqual, equal} from 'assert' -import {CellError, HyperFormula} from '../../src' -import {AbsoluteCellRange} from '../../src/AbsoluteCellRange' -import {SimpleCellAddress, simpleCellAddress} from '../../src/Cell' -import { - ArrayFormulaVertex, - EmptyCellVertex, - ScalarFormulaVertex, - ParsingErrorVertex, - RangeVertex, - ValueCellVertex, - Vertex, -} from '../../src/DependencyGraph' -import {InterpreterValue} from '../../src/interpreter/InterpreterValue' -import {simpleCellAddressToString} from '../../src/parser' - -export class EngineComparator { - - constructor(private expected: HyperFormula, - private actual: HyperFormula) { - } - - public compare(): void { - const expectedNumberOfSheets = this.expected.sheetMapping.numberOfSheets({includePlaceholders: true}) - const numberOfSheets = this.actual.sheetMapping.numberOfSheets({includePlaceholders: true}) - - if (expectedNumberOfSheets !== numberOfSheets) { - throw Error(`Expected number of sheets ${expectedNumberOfSheets}, actual: ${numberOfSheets}`) - } - - this.expected.dependencyGraph.forceApplyPostponedTransformations() - this.actual.dependencyGraph.forceApplyPostponedTransformations() - - for (let sheet = 0; sheet < numberOfSheets; ++sheet) { - this.compareSheet(sheet) - } - } - - private compareSheet(sheet: number): void { - const expectedGraph = this.expected.graph - const actualGraph = this.actual.graph - - const expectedSheetName = this.expected.getSheetName(sheet) - const actualSheetName = this.actual.getSheetName(sheet) - equal(expectedSheetName, actualSheetName, `Expected sheet name '${expectedSheetName}', actual '${actualSheetName}'`) - - const expectedWidth = this.expected.addressMapping.getSheetWidth(sheet) - const expectedHeight = this.expected.addressMapping.getSheetHeight(sheet) - const actualWidth = this.actual.addressMapping.getSheetWidth(sheet) - const actualHeight = this.actual.addressMapping.getSheetHeight(sheet) - - this.compareMatrixMappings() - - for (let x = 0; x < Math.max(expectedWidth, actualWidth); ++x) { - for (let y = 0; y < Math.max(expectedHeight, actualHeight); ++y) { - const address = simpleCellAddress(sheet, x, y) - const expectedVertex = this.expected.addressMapping.getCell(address) - const actualVertex = this.actual.addressMapping.getCell(address) - if (expectedVertex === undefined && actualVertex === undefined) { - continue - } else if ( - (expectedVertex instanceof ScalarFormulaVertex && actualVertex instanceof ScalarFormulaVertex) || - (expectedVertex instanceof ArrayFormulaVertex && actualVertex instanceof ArrayFormulaVertex) - ) { - const actualVertexAddress = actualVertex.getAddress(this.actual.dependencyGraph.lazilyTransformingAstService) - const expectedVertexAddress = expectedVertex.getAddress(this.expected.dependencyGraph.lazilyTransformingAstService) - deepStrictEqual(actualVertexAddress, expectedVertexAddress, `Different addresses in formulas. expected: ${actualVertexAddress}, actual: ${expectedVertexAddress}`) - deepStrictEqual(actualVertex.getFormula(this.actual.lazilyTransformingAstService), expectedVertex.getFormula(this.expected.lazilyTransformingAstService), 'Different AST in formulas') - deepStrictEqual(this.normalizeCellValue(actualVertex.getCellValue()), this.normalizeCellValue(expectedVertex.getCellValue()), `Different values of formulas. expected: ${expectedVertex.getCellValue().toString()}, actual: ${actualVertex.getCellValue().toString()}`) - } else if (expectedVertex instanceof ValueCellVertex && actualVertex instanceof ValueCellVertex) { - deepStrictEqual(actualVertex.getCellValue(), expectedVertex.getCellValue(), `Different values. expected: ${expectedVertex.getCellValue().toString()}, actual: ${actualVertex.getCellValue().toString()}`) - } else if (expectedVertex instanceof EmptyCellVertex && actualVertex instanceof EmptyCellVertex) { - continue - } else if (expectedVertex instanceof ParsingErrorVertex && actualVertex instanceof ParsingErrorVertex) { - deepStrictEqual(expectedVertex.rawInput, actualVertex.rawInput, `Different raw input. expected: ${expectedVertex.rawInput}, actual: ${actualVertex.rawInput}`) - } else { - throw Error('Different vertex types') - } - - const expectedAdjacentAddresses = new Set() - const actualAdjacentAddresses = new Set() - - for (const adjacentNode of expectedGraph.adjacentNodes(expectedVertex)) { - expectedAdjacentAddresses.add(this.getAddressOfVertex(this.expected, adjacentNode)) - } - for (const adjacentNode of actualGraph.adjacentNodes(actualVertex)) { - actualAdjacentAddresses.add(this.getAddressOfVertex(this.actual, adjacentNode)) - } - const sheetMapping = this.expected.sheetMapping - deepStrictEqual(actualAdjacentAddresses, expectedAdjacentAddresses, `Dependent vertices of ${ - simpleCellAddressToString(sheetMapping.getSheetName.bind(sheetMapping), address, 0) ?? 'ERROR' - } (Sheet '${sheetMapping.getSheetName(address.sheet) ?? 'Unknown sheet'}') are not same`) - } - } - } - - private normalizeCellValue(value: InterpreterValue): any { - if (value instanceof CellError) { - return { - type: value.type, - message: value.message, - root: (value.root as any)?.cellAddress - } - } - return value - } - - private compareMatrixMappings() { - const actual = this.actual.arrayMapping.arrayMapping - const expected = this.expected.arrayMapping.arrayMapping - - expect(actual.size).toEqual(expected.size) - - for (const [key, value] of expected.entries()) { - - const actualEntry = actual.get(key)! - expect(actualEntry).toBeDefined() - expect(actualEntry.array.size.isRef).toBe(value.array.size.isRef) - } - } - - private getAddressOfVertex(engine: HyperFormula, vertex: Vertex): SimpleCellAddress | AbsoluteCellRange { - if (vertex instanceof RangeVertex) { - return vertex.range - } - for (const [address, v] of engine.addressMapping.entries()) { - if (v === vertex) { - return address - } - } - throw Error('No such vertex in address mapping: ') - } -} diff --git a/test/unit/helpers/licenseKeyValidator.spec.ts b/test/unit/helpers/licenseKeyValidator.spec.ts deleted file mode 100644 index bfcad8c48f..0000000000 --- a/test/unit/helpers/licenseKeyValidator.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {HyperFormula} from '../../../src' -import {LicenseKeyValidityState} from '../../../src/helpers/licenseKeyValidator' - -describe('license key', () => { - describe('valid key', () => { - it('should verify "gpl-v3" as a valid license key', () => { - const hf = HyperFormula.buildEmpty({ - licenseKey: 'gpl-v3', - }) - - expect(hf.licenseKeyValidityState).toEqual(LicenseKeyValidityState.VALID) - }) - - it('should verify "internal-use-in-handsontable" as a valid license key', () => { - const hf = HyperFormula.buildEmpty({ - licenseKey: 'internal-use-in-handsontable', - }) - - expect(hf.licenseKeyValidityState).toEqual(LicenseKeyValidityState.VALID) - }) - }) - - describe('invalid key', () => { - it('should verify "gpl" as an invalid license key', () => { - const hf = HyperFormula.buildEmpty({ - licenseKey: 'gpl-v1', - }) - - expect(hf.licenseKeyValidityState).toEqual(LicenseKeyValidityState.INVALID) - }) - - it('should verify license keys correctnes', () => { - const hf = HyperFormula.buildEmpty({ - licenseKey: '11111-11111-11111-11111-11111', - }) - - expect(hf.licenseKeyValidityState).toEqual(LicenseKeyValidityState.INVALID) - }) - }) - - describe('missing key', () => { - it('should verify an empty string as an invalid license key', () => { - const hf = HyperFormula.buildEmpty({ - licenseKey: '', - }) - - expect(hf.licenseKeyValidityState).toEqual(LicenseKeyValidityState.MISSING) - }) - }) - - describe('expired key', () => { - it('should verify that key is expired', () => { - const hf = HyperFormula.buildEmpty({ - licenseKey: '80584-cc272-2e7c4-06f16-4db00', - }) - - expect(hf.licenseKeyValidityState).toEqual(LicenseKeyValidityState.EXPIRED) - }) - }) -}) diff --git a/test/unit/i18n.spec.ts b/test/unit/i18n.spec.ts deleted file mode 100644 index 8ad8370a0d..0000000000 --- a/test/unit/i18n.spec.ts +++ /dev/null @@ -1,194 +0,0 @@ -import {ErrorType, HyperFormula, LanguageAlreadyRegisteredError, LanguageNotRegisteredError} from '../../src' -import {ProtectedFunctionTranslationError} from '../../src' -import {ErrorTranslationSet, RawTranslationPackage, TranslationPackage} from '../../src/i18n' -import * as languages from '../../src/i18n/languages' -import {enGB, plPL, enUS} from '../../src/i18n/languages' -import {FunctionRegistry} from '../../src/interpreter/FunctionRegistry' -import {CellAddress} from '../../src/parser' -import {adr, extractReference} from './testUtils' -import {MissingTranslationError} from '../../src/errors' - -describe('i18n', () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const allLanguages: RawTranslationPackage[] = Object.getOwnPropertyNames(languages).filter(lang => !lang.startsWith('_')).map(lang => languages[lang]) - - beforeEach(() => { - HyperFormula.registerLanguage('plPL', plPL) - }) - - it('using functions in different languages', () => { - const enginePL = HyperFormula.buildFromArray([ - ['=SUMA(42)'], - ], {language: 'plPL'}) - const engineEN = HyperFormula.buildFromArray([ - ['=SUM(42)'], - ], {language: 'enGB'}) - - expect(enginePL.getCellValue(adr('A1'))).toBe(42) - expect(engineEN.getCellValue(adr('A1'))).toBe(42) - }) - - it('using functions in different languages with not standard characters', () => { - const enginePL = HyperFormula.buildFromArray([ - ['0'], - ['1'], - ['2'], - ['=LICZ.JEŻELI(A1:A3, ">=1")'], - ], {language: 'plPL'}) - const engineEN = HyperFormula.buildFromArray([ - ['0'], - ['1'], - ['2'], - ['=COUNTIF(A1:A3, ">=1")'], - ], {language: 'enGB'}) - - expect(enginePL.getCellValue(adr('A4'))).toBe(2) - expect(engineEN.getCellValue(adr('A4'))).toBe(2) - }) - - it('translation works for parser hardcoded offset procedure', () => { - const enginePL = HyperFormula.buildFromArray([ - ['=PRZESUNIĘCIE(A1, 1, 1)'], - ], {language: 'plPL'}) - const engineEN = HyperFormula.buildFromArray([ - ['=OFFSET(A1, 1, 1)'], - ]) - - expect(extractReference(enginePL, adr('A1'))).toEqual(CellAddress.relative(1, 1)) - expect(extractReference(engineEN, adr('A1'))).toEqual(CellAddress.relative(1, 1)) - }) - - it('all function translation keys has to be upper cased', () => { - for (const lang of Object.getOwnPropertyNames(languages)) { - if (!lang.startsWith('_')) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const translationPackage = languages[lang] - for (const translationKey in translationPackage.functions) { - expect(translationPackage.functions[translationKey]).toEqual(translationPackage.functions[translationKey].toUpperCase()) - } - } - } - }) - - it('all translation packages should not include protected function definition', () => { - allLanguages.forEach(lang => { - const translatedFunctionsInLang = new Set(Object.keys(lang.functions)) - expect(translatedFunctionsInLang.has('VERSION')).toEqual(false) - }) - }) - - it('all translation packages should translate all implemented functions', () => { - const implementedFunctions = new Set(Array.from(FunctionRegistry.getRegisteredFunctionIds())) - - allLanguages.forEach(lang => { - const translatedFunctionsInLang = new Set(Object.keys(lang.functions)) - translatedFunctionsInLang.add('VERSION') /* missing protected function */ - expect(translatedFunctionsInLang).toEqual(implementedFunctions) - }) - }) - - it('functions should have unique names in a single language', () => { - allLanguages.forEach(lang => { - const names = Object.values(lang.functions) - names.sort() - for (let i = 0; i < names.length - 1; i++) { - expect(names[i]).not.toEqual(names[i + 1]) - } - }) - }) - - it('translation package sanitization', () => { - // eslint-disable-next-line - // @ts-ignore - expect(() => new TranslationPackage({}, {}, {})).toThrowError() - }) - - it('should not be possible to construct TranslationPackage with protected translation', () => { - expect(() => - new TranslationPackage({'VERSION': 'FOO'}, plPL.errors, plPL.ui) - ).toThrow(new ProtectedFunctionTranslationError('VERSION')) - }) - - it('should not be possible to construct TranslationPackage with untranslated memebrs of Error Type', () => { - const unknownErrorType = ErrorType.SPILL as string - const errorsMap: Record = {} - for (const key of Object.values(ErrorType)) { - errorsMap[key] = ErrorType[key] - } - delete errorsMap[unknownErrorType] - expect(() => - new TranslationPackage(plPL.functions, errorsMap as ErrorTranslationSet, plPL.ui) - ).toThrow(new MissingTranslationError(`errors.${unknownErrorType}`)) - }) - - it('should not be possible to extend TranslationPackage with protected translation', () => { - const translationPackage = HyperFormula.getLanguage('plPL') - expect(() => - translationPackage.extendFunctions({'VERSION': 'FOO'}) - ).toThrow(new ProtectedFunctionTranslationError('VERSION')) - }) - - it('should not be possible to register language with protected translation', () => { - const rawTranslationPackage: RawTranslationPackage = { - ...plPL, - functions: {'VERSION': 'FOO'} - } - - expect(() => - HyperFormula.registerLanguage('foo', rawTranslationPackage) - ).toThrow(new ProtectedFunctionTranslationError('VERSION')) - }) - - it('should throw error when trying to register same language twice', () => { - expect(() => - HyperFormula.registerLanguage('plPL', plPL) - ).toThrow(new LanguageAlreadyRegisteredError()) - }) - - it('should throw error when trying to unregister not registered language', () => { - expect(() => - HyperFormula.unregisterLanguage('foo') - ).toThrow(new LanguageNotRegisteredError()) - }) - - it('should throw error when trying to retrieve not registered language', () => { - expect(() => - HyperFormula.getLanguage('foo') - ).toThrow(new LanguageNotRegisteredError()) - }) - - it('`languages` static property should contain only default enGB language when using ES module', () => { - expect(Object.keys(HyperFormula.languages)).toEqual(['enGB']) - }) - - describe('language "enUS"', () => { - it('should be available', () => { - HyperFormula.registerLanguage('enUS', enUS) - const engineUS = HyperFormula.buildFromArray([['=SUM(42)']], { language: 'enUS' }) - - expect(engineUS.getCellValue(adr('A1'))).toBe(42) - }) - - it('should langCode = "enUS"', () => { - expect(enUS.langCode).toEqual('enUS') - }) - - it('should have the same translations as "enGB"', () => { - const allFunctions = Object.keys(enGB.functions) - const areAllTranslationsTheSame = allFunctions.every(key => enGB.functions[key] === enUS.functions[key]) - - expect(areAllTranslationsTheSame).toBeTruthy() - }) - }) - - describe('registerLanguage', () => { - it('should throw error when language code is not a string', () => { - expect(() => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - HyperFormula.registerLanguage(42 as any, enGB) - }).toThrowError('Expected value of type: string for config parameter: languageCode') - }) - }) -}) diff --git a/test/unit/interpreter.spec.ts b/test/unit/interpreter.spec.ts deleted file mode 100644 index 96b8630efc..0000000000 --- a/test/unit/interpreter.spec.ts +++ /dev/null @@ -1,348 +0,0 @@ -import {HyperFormula} from '../../src' -import {ErrorType} from '../../src/Cell' -import {ErrorMessage} from '../../src/error-message' -import {adr, detailedError} from './testUtils' - -describe('Interpreter', () => { - it('relative addressing formula', () => { - const engine = HyperFormula.buildFromArray([['42', '=A1']]) - - expect(engine.getCellValue(adr('B1'))).toBe(42) - }) - - it('number literal', () => { - const engine = HyperFormula.buildFromArray([['3']]) - - expect(engine.getCellValue(adr('A1'))).toBe(3) - }) - - it('negative number literal', () => { - const engine = HyperFormula.buildFromArray([['=-3']]) - - expect(engine.getCellValue(adr('A1'))).toBe(-3) - }) - - it('negative number literal - non numeric value', () => { - const engine = HyperFormula.buildFromArray([['=-"foo"']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('string literals - faulty tests', () => { - const engine = HyperFormula.buildFromArray([ - ['www', '1www', 'www1'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe('www') - expect(engine.getCellValue(adr('B1'))).toBe('1www') - expect(engine.getCellValue(adr('C1'))).toBe('www1') - }) - - it('string literals in formula - faulty tests', () => { - const engine = HyperFormula.buildFromArray([ - ['="www"', '="1www"', '="www1"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe('www') - expect(engine.getCellValue(adr('B1'))).toBe('1www') - expect(engine.getCellValue(adr('C1'))).toBe('www1') - }) - - it('ranges - VALUE error when evaluating without context', () => { - const engine = HyperFormula.buildFromArray([['1'], ['2'], ['=A1:A2']]) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) - - it('procedures - SUM with bad args', () => { - const engine = HyperFormula.buildFromArray([['=SUM(B1)', 'asdf']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) - - it('procedures - not known procedure', () => { - const engine = HyperFormula.buildFromArray([['=FOO()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOO'))) - }) - - it('errors - parsing errors', () => { - const engine = HyperFormula.buildFromArray([['=1A1', '=foo(', '=)(asdf']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - }) - - it('function OFFSET basic use', () => { - const engine = HyperFormula.buildFromArray([['5', '=OFFSET(B1, 0, -1)', '=OFFSET(A1, 0, 0)']]) - - expect(engine.getCellValue(adr('B1'))).toEqual(5) - expect(engine.getCellValue(adr('C1'))).toEqual(5) - }) - - it('function OFFSET out of range', () => { - const engine = HyperFormula.buildFromArray([['=OFFSET(A1, -1, 0)', '=OFFSET(A1, 0, -1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.OutOfSheet)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.OutOfSheet)) - }) - - it('function OFFSET returns bigger range', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(OFFSET(A1, 0, 1,2,1))', '5', '6'], - ['2', '3', '4'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(8) - }) - - it('function OFFSET returns rectangular range and fails', () => { - const engine = HyperFormula.buildFromArray([ - ['=OFFSET(A1, 0, 1,2,1))'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - }) - - it('function OFFSET used twice in a range', () => { - const engine = HyperFormula.buildFromArray([ - ['5', '6', '=SUM(OFFSET(A2,-1,0):OFFSET(A2,0,1))'], - ['2', '3', '4'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(16) - }) - - it('function OFFSET as a reference inside SUM', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '0', '10'], - ['5', '6', '=SUM(SUM(OFFSET(C2,-1,0),A2),-B2)'], - ]) - - expect(engine.getCellValue(adr('C2'))).toEqual(9) - }) - - it('initializing engine with multiple sheets', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['0', '1'], - ['2', '3'], - ], - Sheet2: [ - ['=SUM(Sheet1!A1:Sheet1!B2)'], - ], - }) - expect(engine.getCellValue(adr('A1', 1))).toEqual(6) - }) - - it('using bad range reference', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['0', '1'], - ['2', '3'], - ], - Sheet2: [ - ['=SUM(Sheet1!A1:Sheet2!A2)'], - [''], - ], - }) - expect(engine.getCellValue(adr('A1', 1))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.RangeManySheets)) - }) - - it('expression with parenthesis', () => { - const engine = HyperFormula.buildFromArray([ - ['=(1+2)*3'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(9) - }) - - it('should return #REF when range is pointing to multiple sheets', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [ - ['=SUM(Sheet1!A2:Sheet2!B3)'], - ['=SUM(Sheet1!A:Sheet2!B)'], - ['=SUM(Sheet1!2:Sheet2!3)'], - ['=Sheet1!A2:Sheet2!B3'], - ['=Sheet1!A:Sheet2!B'], - ['=Sheet1!2:Sheet2!3'], - ], - 'Sheet2': [] - }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.RangeManySheets)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.RangeManySheets)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.RangeManySheets)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.RangeManySheets)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.RangeManySheets)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.RangeManySheets)) - }) - - it('should return #REF when referencing non-existing sheet - just cell reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=NonExistingSheet!A1'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when referencing non-existing sheet - cell reference inside a formula', () => { - const engine = HyperFormula.buildFromArray([ - ['=ABS(NonExistingSheet!A1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when referencing non-existing sheet - cell reference inside a numeric aggregation formula', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(NonExistingSheet!A1, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when referencing non-existing sheet - cell range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(NonExistingSheet!C4:F16)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when referencing non-existing sheet - cell range - numeric aggregation function', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(NonExistingSheet!C4:F16)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when referencing non-existing sheet - row range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(NonExistingSheet!1:2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when referencing non-existing sheet - row range - numeric aggregation function', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(NonExistingSheet!1:2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when referencing non-existing sheet - column range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(NonExistingSheet!A:B)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when referencing non-existing sheet - column range - numeric aggregation function', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(NonExistingSheet!A:B)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when range starts with non-existing sheet - cell range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(NonExistingSheet!A1:Sheet1!B2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when range starts with non-existing sheet - cell range - numeric aggregation function', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(NonExistingSheet!A1:Sheet1!B2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when range ends with non-existing sheet - cell range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(Sheet1!A1:NonExistingSheet!B2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when range ends with non-existing sheet - cell range - numeric aggregation function', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(Sheet1!A1:NonExistingSheet!B2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when range starts with non-existing sheet - row range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(NonExistingSheet!1:Sheet1!2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when range starts with non-existing sheet - row range - numeric aggregation function', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(NonExistingSheet!1:Sheet1!2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when range ends with non-existing sheet - row range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(Sheet1!1:NonExistingSheet!2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when range ends with non-existing sheet - row range - numeric aggregation function', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(Sheet1!1:NonExistingSheet!2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when range starts with non-existing sheet - column range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(NonExistingSheet!A:Sheet1!B)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when range starts with non-existing sheet - column range - numeric aggregation function', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(NonExistingSheet!A:Sheet1!B)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when range ends with non-existing sheet - column range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(Sheet1!A:NonExistingSheet!B)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) - - it('should return #REF when range ends with non-existing sheet - column range - numeric aggregation function', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(Sheet1!A:NonExistingSheet!B)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF)) - }) -}) diff --git a/test/unit/interpreter/aggregation-arguments.spec.ts b/test/unit/interpreter/aggregation-arguments.spec.ts deleted file mode 100644 index 52fb28462c..0000000000 --- a/test/unit/interpreter/aggregation-arguments.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {HyperFormula} from '../../../src' -import {adr} from '../testUtils' - -describe('AVERAGE function', () => { - it('should work for empty arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGE(1,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.5) - }) - - it('should work for empty reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGE(A2,B2)'], - [1, null] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('should work for range with empty val', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGE(A2:B2)'], - [1, null] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('should work for empty reference + empty arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGE(A2,B2,)'], - [1, null] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.5) - }) - - it('should work for range with empty val + empty arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGE(A2:B2,)'], - [1, null] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.5) - }) - - it('should work for coercible arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGE(2,TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1.5) - }) - - it('should work for coercible value in reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGE(A2,B2)'], - [2, true] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('should work for coercible value in range', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGE(A2:B2)'], - [2, true] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) -}) diff --git a/test/unit/interpreter/aliases.spec.ts b/test/unit/interpreter/aliases.spec.ts deleted file mode 100644 index 7a5627d2aa..0000000000 --- a/test/unit/interpreter/aliases.spec.ts +++ /dev/null @@ -1,208 +0,0 @@ -import {HyperFormula} from '../../../src' - -describe('Function aliases', () => { - const engine = HyperFormula.buildEmpty() - it('NEGBINOMDIST should be an alias of NEGBINOM.DIST', () => { - expect(engine.getFunctionPlugin('NEGBINOMDIST')!.aliases!['NEGBINOMDIST']).toEqual('NEGBINOM.DIST') - }) - - it('BETADIST should be an alias of BETA.DIST', () => { - expect(engine.getFunctionPlugin('BETADIST')!.aliases!['BETADIST']).toEqual('BETA.DIST') - }) - - it('EXPONDIST should be an alias of EXPON.DIST', () => { - expect(engine.getFunctionPlugin('EXPONDIST')!.aliases!['EXPONDIST']).toEqual('EXPON.DIST') - }) - - it('NORMDIST should be an alias of NORM.DIST', () => { - expect(engine.getFunctionPlugin('NORMDIST')!.aliases!['NORMDIST']).toEqual('NORM.DIST') - }) - - it('NORMINV should be an alias of NORM.INV', () => { - expect(engine.getFunctionPlugin('NORMINV')!.aliases!['NORMINV']).toEqual('NORM.INV') - }) - - it('NORMSDIST should be an alias of NORM.S.DIST', () => { - expect(engine.getFunctionPlugin('NORMSDIST')!.aliases!['NORMSDIST']).toEqual('NORM.S.DIST') - }) - - it('NORMSINV should be an alias of NORM.S.INV', () => { - expect(engine.getFunctionPlugin('NORMSINV')!.aliases!['NORMSINV']).toEqual('NORM.S.INV') - }) - - it('LOGINV should be an alias of LOGNORM.INV', () => { - expect(engine.getFunctionPlugin('LOGINV')!.aliases!['LOGINV']).toEqual('LOGNORM.INV') - }) - - it('LOGNORMDIST should be an alias of LOGNORM.DIST', () => { - expect(engine.getFunctionPlugin('LOGNORMDIST')!.aliases!['LOGNORMDIST']).toEqual('LOGNORM.DIST') - }) - - it('TINV should be an alias of T.INV.2T', () => { - expect(engine.getFunctionPlugin('TINV')!.aliases!['TINV']).toEqual('T.INV.2T') - }) - - it('HYPGEOMDIST should be an alias of HYPGEOM.DIST', () => { - expect(engine.getFunctionPlugin('HYPGEOMDIST')!.aliases!['HYPGEOMDIST']).toEqual('HYPGEOM.DIST') - }) - - it('POISSON should be an alias of POISSON.DIST', () => { - expect(engine.getFunctionPlugin('POISSON')!.aliases!['POISSON']).toEqual('POISSON.DIST') - }) - - it('WEIBULL should be an alias of WEIBULL.DIST', () => { - expect(engine.getFunctionPlugin('WEIBULL')!.aliases!['WEIBULL']).toEqual('WEIBULL.DIST') - }) - - it('FINV should be an alias of F.INV.RT', () => { - expect(engine.getFunctionPlugin('FINV')!.aliases!['FINV']).toEqual('F.INV.RT') - }) - - it('FDIST should be an alias of F.DIST.RT', () => { - expect(engine.getFunctionPlugin('FDIST')!.aliases!['FDIST']).toEqual('F.DIST.RT') - }) - - it('CHIDIST should be an alias of CHISQ.DIST.RT', () => { - expect(engine.getFunctionPlugin('CHIDIST')!.aliases!['CHIDIST']).toEqual('CHISQ.DIST.RT') - }) - - it('CHIINV should be an alias of CHISQ.INV.RT', () => { - expect(engine.getFunctionPlugin('CHIINV')!.aliases!['CHIINV']).toEqual('CHISQ.INV.RT') - }) - - it('GAMMADIST should be an alias of GAMMA.DIST', () => { - expect(engine.getFunctionPlugin('GAMMADIST')!.aliases!['GAMMADIST']).toEqual('GAMMA.DIST') - }) - - it('GAMMALN.PRECISE should be an alias of GAMMALN', () => { - expect(engine.getFunctionPlugin('GAMMALN.PRECISE')!.aliases!['GAMMALN.PRECISE']).toEqual('GAMMALN') - }) - - it('GAMMAINV should be an alias of GAMMA.INV', () => { - expect(engine.getFunctionPlugin('GAMMAINV')!.aliases!['GAMMAINV']).toEqual('GAMMA.INV') - }) - - it('BETAINV should be an alias of BETA.INV', () => { - expect(engine.getFunctionPlugin('BETAINV')!.aliases!['BETAINV']).toEqual('BETA.INV') - }) - - it('BINOMDIST should be an alias of BINOM.DIST', () => { - expect(engine.getFunctionPlugin('BINOMDIST')!.aliases!['BINOMDIST']).toEqual('BINOM.DIST') - }) - - it('STDEV should be an alias of STDEV.S', () => { - expect(engine.getFunctionPlugin('STDEV')!.aliases!['STDEV']).toEqual('STDEV.S') - }) - - it('STDEVP should be an alias of STDEV.P', () => { - expect(engine.getFunctionPlugin('STDEVP')!.aliases!['STDEVP']).toEqual('STDEV.P') - }) - - it('VAR should be an alias of VAR.S', () => { - expect(engine.getFunctionPlugin('VAR')!.aliases!['VAR']).toEqual('VAR.S') - }) - - it('VARP should be an alias of VAR.P', () => { - expect(engine.getFunctionPlugin('VARP')!.aliases!['VARP']).toEqual('VAR.P') - }) - - it('CONFIDENCE should be an alias of CONFIDENCE.NORM', () => { - expect(engine.getFunctionPlugin('CONFIDENCE')!.aliases!['CONFIDENCE']).toEqual('CONFIDENCE.NORM') - }) - - it('COVAR should be an alias of COVARIANCE.P', () => { - expect(engine.getFunctionPlugin('COVAR')!.aliases!['COVAR']).toEqual('COVARIANCE.P') - }) - - it('CRITBINOM should be an alias of BINOM.INV', () => { - expect(engine.getFunctionPlugin('CRITBINOM')!.aliases!['CRITBINOM']).toEqual('BINOM.INV') - }) - - it('FTEST should be an alias of F.TEST', () => { - expect(engine.getFunctionPlugin('FTEST')!.aliases!['FTEST']).toEqual('F.TEST') - }) - - it('PEARSON should be an alias of CORREL', () => { - expect(engine.getFunctionPlugin('PEARSON')!.aliases!['PEARSON']).toEqual('CORREL') - }) - - it('ZTEST should be an alias of Z.TEST', () => { - expect(engine.getFunctionPlugin('ZTEST')!.aliases!['ZTEST']).toEqual('Z.TEST') - }) - - it('WEIBULLDIST should be an alias of WEIBULL.DIST', () => { - expect(engine.getFunctionPlugin('WEIBULLDIST')!.aliases!['WEIBULLDIST']).toEqual('WEIBULL.DIST') - }) - - it('VARS should be an alias of VAR.S', () => { - expect(engine.getFunctionPlugin('VARS')!.aliases!['VARS']).toEqual('VAR.S') - }) - - it('TINV2T should be an alias of T.INV.2T', () => { - expect(engine.getFunctionPlugin('TINV2T')!.aliases!['TINV2T']).toEqual('T.INV.2T') - }) - - it('TDISTRT should be an alias of T.DIST.RT', () => { - expect(engine.getFunctionPlugin('TDISTRT')!.aliases!['TDISTRT']).toEqual('T.DIST.RT') - }) - - it('TDIST2T should be an alias of T.DIST.2T', () => { - expect(engine.getFunctionPlugin('TDIST2T')!.aliases!['TDIST2T']).toEqual('T.DIST.2T') - }) - - it('STDEVS should be an alias of STDEV.S', () => { - expect(engine.getFunctionPlugin('STDEVS')!.aliases!['STDEVS']).toEqual('STDEV.S') - }) - - it('FINVRT should be an alias of F.INV.RT', () => { - expect(engine.getFunctionPlugin('FINVRT')!.aliases!['FINVRT']).toEqual('F.INV.RT') - }) - - it('FDISTRT should be an alias of F.DIST.RT', () => { - expect(engine.getFunctionPlugin('FDISTRT')!.aliases!['FDISTRT']).toEqual('F.DIST.RT') - }) - - it('CHIDISTRT should be an alias of CHISQ.DIST.RT', () => { - expect(engine.getFunctionPlugin('CHIDISTRT')!.aliases!['CHIDISTRT']).toEqual('CHISQ.DIST.RT') - }) - - it('CHIINVRT should be an alias of CHISQ.INV.RT', () => { - expect(engine.getFunctionPlugin('CHIINVRT')!.aliases!['CHIINVRT']).toEqual('CHISQ.INV.RT') - }) - - it('COVARIANCEP should be an alias of COVARIANCE.P', () => { - expect(engine.getFunctionPlugin('COVARIANCEP')!.aliases!['COVARIANCEP']).toEqual('COVARIANCE.P') - }) - - it('COVARIANCES should be an alias of COVARIANCE.S', () => { - expect(engine.getFunctionPlugin('COVARIANCES')!.aliases!['COVARIANCES']).toEqual('COVARIANCE.S') - }) - - it('LOGNORMINV should be an alias of LOGNORM.INV', () => { - expect(engine.getFunctionPlugin('LOGNORMINV')!.aliases!['LOGNORMINV']).toEqual('LOGNORM.INV') - }) - - it('POISSONDIST should be an alias of POISSON.DIST', () => { - expect(engine.getFunctionPlugin('POISSONDIST')!.aliases!['POISSONDIST']).toEqual('POISSON.DIST') - }) - - it('SKEWP should be an alias of SKEW.P', () => { - expect(engine.getFunctionPlugin('SKEWP')!.aliases!['SKEWP']).toEqual('SKEW.P') - }) - - it('TTEST should be an alias of T.TEST', () => { - expect(engine.getFunctionPlugin('TTEST')!.aliases!['TTEST']).toEqual('T.TEST') - }) - - it('CHITEST should be an alias of CHISQ.TEST', () => { - expect(engine.getFunctionPlugin('CHITEST')!.aliases!['CHITEST']).toEqual('CHISQ.TEST') - }) - - it('ISO.CEILING should be an alias of CEILING.PRECISE', () => { - expect(engine.getFunctionPlugin('ISO.CEILING')!.aliases!['ISO.CEILING']).toEqual('CEILING.PRECISE') - }) - - it('TRUNC should be an alias of ROUNDDOWN', () => { - expect(engine.getFunctionPlugin('TRUNC')!.aliases!['TRUNC']).toEqual('ROUNDDOWN') - }) -}) diff --git a/test/unit/interpreter/binary-search.spec.ts b/test/unit/interpreter/binary-search.spec.ts deleted file mode 100644 index 2fb5df89e0..0000000000 --- a/test/unit/interpreter/binary-search.spec.ts +++ /dev/null @@ -1,122 +0,0 @@ -import {compare, findLastOccurrenceInOrderedArray} from '../../../src/interpreter/binarySearch' -import {EmptyValue} from '../../../src/interpreter/InterpreterValue' -import {CellError, ErrorType} from '../../../src' - -describe('findLastOccurrenceInOrderedArray', () => { - it('returns -1 when empty array', () => { - const values: number[] = [] - expect(findLastOccurrenceInOrderedArray(1, values)).toBe(-1) - }) - - it('works for one element', () => { - const values: number[] = [1] - expect(findLastOccurrenceInOrderedArray(1, values)).toBe(0) - }) - - it('returns -1 when all elements are greater', () => { - const values: number[] = [3, 5, 10] - expect(findLastOccurrenceInOrderedArray(1, values)).toBe(-1) - }) - - it('finds index of element in values of odd length', () => { - const values: number[] = [3, 5, 10] - expect(findLastOccurrenceInOrderedArray(3, values)).toBe(0) - expect(findLastOccurrenceInOrderedArray(5, values)).toBe(1) - expect(findLastOccurrenceInOrderedArray(10, values)).toBe(2) - }) - - it('finds index of element in values of even length', () => { - const values: number[] = [3, 5, 10, 11] - expect(findLastOccurrenceInOrderedArray(3, values)).toBe(0) - expect(findLastOccurrenceInOrderedArray(5, values)).toBe(1) - expect(findLastOccurrenceInOrderedArray(10, values)).toBe(2) - expect(findLastOccurrenceInOrderedArray(11, values)).toBe(3) - }) - - it('finds index of lower bound', () => { - const values: number[] = [1, 2, 3, 7] - expect(findLastOccurrenceInOrderedArray(5, values)).toBe(2) - expect(findLastOccurrenceInOrderedArray(10, values)).toBe(3) - }) - - it('works for strings', () => { - const values: string[] = ['aaaa', 'bar', 'foo', 'xyz'] - expect(findLastOccurrenceInOrderedArray('foo', values)).toBe(2) - }) - - it('works for bools', () => { - const values: boolean[] = [false, false, false, true, true] - expect(findLastOccurrenceInOrderedArray(true, values)).toBe(4) - }) - - it('works for different types in array', () => { - const values = [3, 5, 7, 'aaaa', 'bar', 'foo', false, false, true] - expect(findLastOccurrenceInOrderedArray(5, values)).toBe(1) - expect(findLastOccurrenceInOrderedArray('foo', values)).toBe(5) - expect(findLastOccurrenceInOrderedArray(false, values)).toBe(7) - expect(findLastOccurrenceInOrderedArray(10, values)).toBe(2) - expect(findLastOccurrenceInOrderedArray('xyz', values)).toBe(5) - }) - - it('returns the last occurrence', () => { - const values = [1, 2, 2, 2, 2, 2, 3, 3, 3] - expect(findLastOccurrenceInOrderedArray(2, values)).toBe(5) - }) - - it('works for arrays ordered descending', () => { - const values: number[] = [11, 10, 5, 3] - expect(findLastOccurrenceInOrderedArray(3, values, 'desc')).toBe(3) - expect(findLastOccurrenceInOrderedArray(5, values, 'desc')).toBe(2) - expect(findLastOccurrenceInOrderedArray(10, values, 'desc')).toBe(1) - expect(findLastOccurrenceInOrderedArray(11, values, 'desc')).toBe(0) - }) -}) - -describe('compare', () => { - it('number < string', () => { - expect(compare(42, 'foobar')).toBe(-1) - expect(compare('foobar', 42)).toBe(1) - }) - - it('number < boolean', () => { - expect(compare(42, false)).toBe(-1) - expect(compare(false, 42)).toBe(1) - }) - - it('string < boolean', () => { - expect(compare('foobar', false)).toBe(-1) - expect(compare(false, 'foobar')).toBe(1) - }) - - it('numbers', () => { - expect(compare(1, 2)).toBe(-1) - expect(compare(2, 2)).toBe(0) - expect(compare(3, 2)).toBe(1) - }) - - it('bool', () => { - expect(compare(false, true)).toBe(-1) - expect(compare(true, true)).toBe(0) - expect(compare(true, false)).toBe(1) - }) - - it('string', () => { - expect(compare('a', 'b')).toBe(-1) - expect(compare('a', 'a')).toBe(0) - expect(compare('b', 'a')).toBe(1) - }) - - it('string length', () => { - expect(compare('a', 'aa')).toBe(-1) - }) - - it('empty value', () => { - expect(compare(EmptyValue, EmptyValue)).toBe(0) - expect(compare(EmptyValue, 'foo')).toBe(-1) - expect(compare('foo', EmptyValue)).toBe(1) - }) - - it('error', () => { - expect(compare('foo', new CellError(ErrorType.DIV_BY_ZERO))).toBe(-1) - }) -}) diff --git a/test/unit/interpreter/boolean-operators.spec.ts b/test/unit/interpreter/boolean-operators.spec.ts deleted file mode 100644 index 16cec42b89..0000000000 --- a/test/unit/interpreter/boolean-operators.spec.ts +++ /dev/null @@ -1,340 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Interpreter - Boolean operators', () => { - it('Equals operator - numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=1=2', '=1=1', '=1+2=3'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('B1'))).toBe(true) - expect(engine.getCellValue(adr('C1'))).toBe(true) - }) - - it('Equals operator - strings', () => { - const engine = HyperFormula.buildFromArray([ - ['="abc"="abc"', '="foo"="bar"', '="a"="foo"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('C1'))).toBe(false) - }) - - it('Equals operator - booleans', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRUE()=TRUE()', '=FALSE()=FALSE()', '=TRUE()=FALSE()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(true) - expect(engine.getCellValue(adr('C1'))).toBe(false) - }) - - it('Equal operator with different types', () => { - const engine = HyperFormula.buildFromArray([ - ['="foo"=1', '="foo"=TRUE()', '=1="foo"', '=TRUE()="foo"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('C1'))).toBe(false) - expect(engine.getCellValue(adr('D1'))).toBe(false) - }) - - it('Equals operator with error', () => { - const engine = HyperFormula.buildFromArray([ - ['=1/0', '=A1=2', '=2=A1'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('Not equals operator - numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=1<>2', '=1<>1', '=1+2<>3'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('C1'))).toBe(false) - }) - - it('Not equals operator - strings', () => { - const engine = HyperFormula.buildFromArray([ - ['="abc"<>"abc"', '="foo"<>"bar"', '="a"<>"foo"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('B1'))).toBe(true) - expect(engine.getCellValue(adr('C1'))).toBe(true) - }) - - it('Not equals operator - booleans', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRUE()<>TRUE()', '=FALSE()<>FALSE()', '=TRUE()<>FALSE()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('C1'))).toBe(true) - }) - - it('Not equals operator with error', () => { - const engine = HyperFormula.buildFromArray([ - ['=1/0', '=A1<>2', '=2<>A1'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('Not Equal operator with different types', () => { - const engine = HyperFormula.buildFromArray([ - ['="foo"<>1', '="foo"<>TRUE()', '=1<>"foo"', '=TRUE()<>"foo"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(true) - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('D1'))).toBe(true) - }) - - it('Less than operator with number arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=1<2', '=2<2', '=-3<4', '=-4<-3'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('D1'))).toBe(true) - }) - - it('Less than operator with different types', () => { - const engine = HyperFormula.buildFromArray([ - ['="foo"<1', '="foo" { - const engine = HyperFormula.buildFromArray([ - ['="abcd"<"abc"', '="11"<"2"', '="02/02/2020"<"true"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('B1'))).toBe(true) - expect(engine.getCellValue(adr('C1'))).toBe(true) - }) - - it('Less than operator with coercions', () => { - const engine = HyperFormula.buildFromArray([ - ['=FALSE()<"2"', '="1"<"2"', '=TRUE() { - const engine = HyperFormula.buildFromArray([ - ['=2>1', '=2>2', '=4>-3', '=-3>-4'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('D1'))).toBe(true) - }) - - it('Greater than operator with different types', () => { - const engine = HyperFormula.buildFromArray([ - ['="foo">1', '="foo">TRUE()', '=1>"0"', '=2 > TRUE()', '="02/02/2020">"000"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('C1'))).toBe(false) - expect(engine.getCellValue(adr('D1'))).toBe(false) - expect(engine.getCellValue(adr('E1'))).toBe(true) - }) - - it('Greater than operator with strings', () => { - const engine = HyperFormula.buildFromArray([ - ['="abcd">"abc"', '="11">"2"', '="02/02/2020">"true"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('C1'))).toBe(false) - }) - - it('Greater than operator with coercions', () => { - const engine = HyperFormula.buildFromArray([ - ['=FALSE()>"2"', '="1">"2"', '=TRUE()>FALSE()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('C1'))).toBe(true) - }) - - it('Less than or equal operator with number arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=1<=2', '=2<=2', '=-3<=4', '=-4<=-3', '=5<=4'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(true) - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('D1'))).toBe(true) - expect(engine.getCellValue(adr('E1'))).toBe(false) - }) - - it('Less than or equal operator with strings', () => { - const engine = HyperFormula.buildFromArray([ - ['="abcd"<="abc"', '="11"<="2"', '="02/02/2020"<="true"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('B1'))).toBe(true) - expect(engine.getCellValue(adr('C1'))).toBe(true) - }) - it('Less than or equal operator with coercions', () => { - const engine = HyperFormula.buildFromArray([ - ['=FALSE()<="2"', '="1"<="2"', '=TRUE()<=FALSE()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('B1'))).toBe(true) - expect(engine.getCellValue(adr('C1'))).toBe(false) - }) - - it('Less than or equal operator with different types', () => { - const engine = HyperFormula.buildFromArray([ - ['="foo"<=1', '="foo"<=TRUE()', '=1<="0"', '=2 <= TRUE()', '="02/02/2020"<="000"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('B1'))).toBe(true) - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('D1'))).toBe(true) - expect(engine.getCellValue(adr('E1'))).toBe(false) - }) - - it('Greater than or equal operator with number arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=2>=1', '=2>=2', '=4>=-3', '=-3>=-4', '=4>=5'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(true) - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('D1'))).toBe(true) - expect(engine.getCellValue(adr('E1'))).toBe(false) - }) - - it('Greater than or equal operator with different types', () => { - const engine = HyperFormula.buildFromArray([ - ['="foo">=1', '="foo">=TRUE()', '=1>="0"', '=2 >= TRUE()', '="02/02/2020">="000"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('C1'))).toBe(false) - expect(engine.getCellValue(adr('D1'))).toBe(false) - expect(engine.getCellValue(adr('E1'))).toBe(true) - }) - - it('Greater than or equal operator with strings', () => { - const engine = HyperFormula.buildFromArray([ - ['="abcd">="abc"', '="11">="2"', '="02/02/2020">="true"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('C1'))).toBe(false) - }) - - it('Greater than or equal operator with coercions', () => { - const engine = HyperFormula.buildFromArray([ - ['=FALSE()>="2"', '="1">="2"', '=TRUE()>=FALSE()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('C1'))).toBe(true) - }) - - it('Less than propagates errors correctly', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=(1/0)<2', '=2<(1/0)', '=(A1:B1)<(1/0)', '=(1/0)<(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - it('Greater than propagates errors correctly', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=(1/0)>2', '=2>(1/0)', '=(A1:B1)>(1/0)', '=(1/0)>(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - it('Less than or equal propagates errors correctly', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=(1/0)<=2', '=2<=(1/0)', '=(A1:B1)<=(1/0)', '=(1/0)<=(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - it('Greater than or equal propagates errors correctly', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=(1/0)>=2', '=2>=(1/0)', '=(A1:B1)>=(1/0)', '=(1/0)>=(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - it('Equal propagates errors correctly', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=(1/0)=2', '=2=(1/0)', '=(A1:B1)=(1/0)', '=(1/0)=(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - it('Not equal propagates errors correctly', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=(1/0)<>2', '=2<>(1/0)', '=(A1:B1)<>(1/0)', '=(1/0)<>(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/coercions.spec.ts b/test/unit/interpreter/coercions.spec.ts deleted file mode 100644 index 6fed020c8f..0000000000 --- a/test/unit/interpreter/coercions.spec.ts +++ /dev/null @@ -1,378 +0,0 @@ -import {ErrorType, HyperFormula, SimpleRangeValue} from '../../../src' -import {CellError} from '../../../src/Cell' -import {Config} from '../../../src/Config' -import {DateTimeHelper} from '../../../src/DateTimeHelper' -import {ErrorMessage} from '../../../src/error-message' -import { - ArithmeticHelper, - coerceBooleanToNumber, - coerceScalarToBoolean, - coerceToRangeNumbersOrError, - coerceScalarToString -} from '../../../src/interpreter/ArithmeticHelper' -import {DateNumber, EmptyValue, TimeNumber} from '../../../src/interpreter/InterpreterValue' -import {NumberLiteralHelper} from '../../../src/NumberLiteralHelper' -import {adr, detailedError} from '../testUtils' - -describe('#coerceNonDateScalarToMaybeNumber', () => { - const config = new Config() - const dateTimeHelper = new DateTimeHelper(config) - const numberLiteralsHelper = new NumberLiteralHelper(config) - const arithmeticHelper = new ArithmeticHelper(config, dateTimeHelper, numberLiteralsHelper) - it('works', () => { - expect(arithmeticHelper.coerceNonDateScalarToMaybeNumber(42)).toBe(42) - expect(arithmeticHelper.coerceNonDateScalarToMaybeNumber('42')).toBe(42) - expect(arithmeticHelper.coerceNonDateScalarToMaybeNumber(' 42')).toBe(42) - expect(arithmeticHelper.coerceNonDateScalarToMaybeNumber('42 ')).toBe(42) - expect(arithmeticHelper.coerceNonDateScalarToMaybeNumber('0000042')).toBe(42) - expect(arithmeticHelper.coerceNonDateScalarToMaybeNumber('42foo')).toEqual(undefined) - expect(arithmeticHelper.coerceNonDateScalarToMaybeNumber('foo42')).toEqual(undefined) - expect(arithmeticHelper.coerceNonDateScalarToMaybeNumber(true)).toBe(1) - expect(arithmeticHelper.coerceNonDateScalarToMaybeNumber(false)).toBe(0) - expect(arithmeticHelper.coerceNonDateScalarToMaybeNumber(EmptyValue)).toBe(0) - expect(arithmeticHelper.coerceNonDateScalarToMaybeNumber('')).toBe(0) - expect(arithmeticHelper.coerceNonDateScalarToMaybeNumber(' ')).toEqual(undefined) - expect(arithmeticHelper.coerceNonDateScalarToMaybeNumber(new CellError(ErrorType.DIV_BY_ZERO))).toEqual(undefined) - }) -}) - -describe('#coerceScalarToComplex', () => { - const config = new Config() - const dateTimeHelper = new DateTimeHelper(config) - const numberLiteralsHelper = new NumberLiteralHelper(config) - const arithmeticHelper = new ArithmeticHelper(config, dateTimeHelper, numberLiteralsHelper) - it('works', () => { - expect(arithmeticHelper.coerceScalarToComplex(42)).toEqual([42, 0]) - expect(arithmeticHelper.coerceScalarToComplex(true)).toEqual(new CellError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - expect(arithmeticHelper.coerceScalarToComplex(EmptyValue)).toEqual([0, 0]) - expect(arithmeticHelper.coerceScalarToComplex('')).toEqual(new CellError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - expect(arithmeticHelper.coerceScalarToComplex('1')).toEqual([1, 0]) - expect(arithmeticHelper.coerceScalarToComplex('-1.1')).toEqual([-1.1, 0]) - expect(arithmeticHelper.coerceScalarToComplex('+.1')).toEqual([0.1, 0]) - expect(arithmeticHelper.coerceScalarToComplex('1e1')).toEqual([10, 0]) - expect(arithmeticHelper.coerceScalarToComplex('1 i')).toEqual([0, 1]) - expect(arithmeticHelper.coerceScalarToComplex('-1.1j')).toEqual([0, -1.1]) - expect(arithmeticHelper.coerceScalarToComplex('+.1 i')).toEqual([0, 0.1]) - expect(arithmeticHelper.coerceScalarToComplex('1e1j')).toEqual([0, 10]) - expect(arithmeticHelper.coerceScalarToComplex('i')).toEqual([0, 1]) - expect(arithmeticHelper.coerceScalarToComplex('-i')).toEqual([0, -1]) - expect(arithmeticHelper.coerceScalarToComplex('+i')).toEqual([0, 1]) - expect(arithmeticHelper.coerceScalarToComplex('i1')).toEqual(new CellError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - expect(arithmeticHelper.coerceScalarToComplex('--1')).toEqual(new CellError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - expect(arithmeticHelper.coerceScalarToComplex('i+1')).toEqual(new CellError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - expect(arithmeticHelper.coerceScalarToComplex('1+-i')).toEqual([1, -1]) - expect(arithmeticHelper.coerceScalarToComplex('0.1+.1 i')).toEqual([0.1, 0.1]) - expect(arithmeticHelper.coerceScalarToComplex(' - 1.0e+1 - - 1.0e+1j')).toEqual([-10, 10]) - expect(arithmeticHelper.coerceScalarToComplex(new CellError(ErrorType.DIV_BY_ZERO))).toEqual(new CellError(ErrorType.DIV_BY_ZERO)) - }) -}) - -describe('#coerceToRangeNumbersOrError', () => { - it('works', () => { - const simpleRangeValueOnlyNumbers = SimpleRangeValue.onlyNumbers([[1, 2]]) - const timeNumber = new TimeNumber(0, 'hh:mm:ss.ss') - expect(coerceToRangeNumbersOrError(simpleRangeValueOnlyNumbers)).toEqual(simpleRangeValueOnlyNumbers) - expect(coerceToRangeNumbersOrError(new CellError(ErrorType.DIV_BY_ZERO))).toEqual(new CellError(ErrorType.DIV_BY_ZERO)) - expect(coerceToRangeNumbersOrError(999)).toEqual(SimpleRangeValue.onlyValues([[999]])) - expect(coerceToRangeNumbersOrError(timeNumber)).toEqual(SimpleRangeValue.onlyValues([[timeNumber]])) - expect(coerceToRangeNumbersOrError('foo')).toEqual(null) - }) -}) - -describe('#coerceBooleanToNumber', () => { - it('works', () => { - expect(coerceBooleanToNumber(true)).toBe(1) - expect(coerceBooleanToNumber(false)).toBe(0) - }) - - it('behaves the same as more general coercion', () => { - const config = new Config() - const dateHelper = new DateTimeHelper(config) - const numberLiteralsHelper = new NumberLiteralHelper(config) - const arithmeticHelper = new ArithmeticHelper(config, dateHelper, numberLiteralsHelper) - expect(coerceBooleanToNumber(true)).toBe(arithmeticHelper.coerceScalarToNumberOrError(true) as number) - expect(coerceBooleanToNumber(false)).toBe(arithmeticHelper.coerceScalarToNumberOrError(false) as number) - }) -}) - -describe('#coerceScalarToBoolean', () => { - it('works', () => { - expect(coerceScalarToBoolean(true)).toBe(true) - expect(coerceScalarToBoolean(false)).toBe(false) - - expect(coerceScalarToBoolean(1)).toBe(true) - expect(coerceScalarToBoolean(0)).toBe(false) - expect(coerceScalarToBoolean(2)).toBe(true) - expect(coerceScalarToBoolean(-1)).toBe(true) - - expect(coerceScalarToBoolean('false')).toBe(false) - expect(coerceScalarToBoolean('FALSE')).toBe(false) - expect(coerceScalarToBoolean('true')).toBe(true) - expect(coerceScalarToBoolean('TRUE')).toBe(true) - expect(coerceScalarToBoolean(' ')).toBe(undefined) - expect(coerceScalarToBoolean(' true')).toBe(undefined) - expect(coerceScalarToBoolean('true ')).toBe(undefined) - expect(coerceScalarToBoolean('prawda')).toBe(undefined) - expect(coerceScalarToBoolean('')).toBe(false) - - expect(coerceScalarToBoolean(EmptyValue)).toBe(false) - - expect(coerceScalarToBoolean(new CellError(ErrorType.DIV_BY_ZERO))).toEqual(new CellError(ErrorType.DIV_BY_ZERO)) - }) -}) - -describe('#coerceScalarToNumberOrError', () => { - it('works', () => { - const config = new Config() - const dateHelper = new DateTimeHelper(config) - const numberLiteralsHelper = new NumberLiteralHelper(config) - const arithmeticHelper = new ArithmeticHelper(config, dateHelper, numberLiteralsHelper) - expect(arithmeticHelper.coerceScalarToNumberOrError(1)).toEqual(1) - - expect(arithmeticHelper.coerceScalarToNumberOrError(new CellError(ErrorType.DIV_BY_ZERO))).toEqual(new CellError(ErrorType.DIV_BY_ZERO)) - - expect(arithmeticHelper.coerceScalarToNumberOrError('31/12/1899')).toEqual(new DateNumber(1, 'DD/MM/YYYY')) - expect(arithmeticHelper.coerceScalarToNumberOrError('00:00:00')).toEqual(new TimeNumber(0, 'hh:mm:ss.sss')) - expect(arithmeticHelper.coerceScalarToNumberOrError(true)).toEqual(1) - - expect(arithmeticHelper.coerceScalarToNumberOrError('foo42')).toEqual(new CellError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - - expect(arithmeticHelper.coerceScalarToNumberOrError('1')).toEqual(1) - }) - -}) - -describe('#coerceScalarToString', () => { - it('works', () => { - expect(coerceScalarToString(true)).toBe('TRUE') - expect(coerceScalarToString(false)).toBe('FALSE') - - expect(coerceScalarToString(1)).toBe('1') - expect(coerceScalarToString(0)).toBe('0') - expect(coerceScalarToString(2)).toBe('2') - expect(coerceScalarToString(-1)).toBe('-1') - - expect(coerceScalarToString('foo')).toBe('foo') - - expect(coerceScalarToString(EmptyValue)).toBe('') - - expect(coerceScalarToString(1.42)).toBe('1.42') - - expect(coerceScalarToString(new CellError(ErrorType.DIV_BY_ZERO))).toEqual(new CellError(ErrorType.DIV_BY_ZERO)) - }) -}) - -describe('check if type coercions are applied', () => { - it('boolean to int, true vs null', () => { - const engine = HyperFormula.buildFromArray([ - [true, null, '=A1+B1', '=A1-B1', '=A1*B1', '=A1/B1', '=A1^B1', '=+A1', '=-A1', '=A1%'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(1) //ADD - expect(engine.getCellValue(adr('D1'))).toEqual(1) //SUB - expect(engine.getCellValue(adr('E1'))).toEqual(0) //MULT - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // DIV - expect(engine.getCellValue(adr('G1'))).toEqual(1) // EXP - expect(engine.getCellValue(adr('H1'))).toEqual(true) // UNARY PLUS - expect(engine.getCellValue(adr('I1'))).toEqual(-1) // UNARY MINUS - expect(engine.getCellValue(adr('J1'))).toEqual(0.01) // PERCENTAGE - }) - - it('boolean to int, null vs true', () => { - const engine = HyperFormula.buildFromArray([ - [null, true, '=A1+B1', '=A1-B1', '=A1*B1', '=A1/B1', '=A1^B1', '=+A1', '=-A1', '=A1%'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(1) //ADD - expect(engine.getCellValue(adr('D1'))).toEqual(-1) //SUB - expect(engine.getCellValue(adr('E1'))).toEqual(0) //MULT - expect(engine.getCellValue(adr('F1'))).toEqual(0) // DIV - expect(engine.getCellValue(adr('G1'))).toEqual(0) // EXP - expect(engine.getCellValue(adr('H1'))).toBe(null) // UNARY PLUS - expect(engine.getCellValue(adr('I1'))).toEqual(0) // UNARY MINUS - expect(engine.getCellValue(adr('J1'))).toEqual(0) // PERCENTAGE - }) - - it('boolean to int, true vs true', () => { - const engine = HyperFormula.buildFromArray([ - [true, true, '=A1+B1', '=A1-B1', '=A1*B1', '=A1/B1', '=A1^B1'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(2) //ADD - expect(engine.getCellValue(adr('D1'))).toEqual(0) //SUB - expect(engine.getCellValue(adr('E1'))).toEqual(1) //MULT - expect(engine.getCellValue(adr('F1'))).toEqual(1) // DIV - expect(engine.getCellValue(adr('G1'))).toEqual(1) // EXP - }) - - it('boolean to int, true vs false', () => { - const engine = HyperFormula.buildFromArray([ - [true, false, '=A1+B1', '=A1-B1', '=A1*B1', '=A1/B1', '=A1^B1'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(1) //ADD - expect(engine.getCellValue(adr('D1'))).toEqual(1) //SUB - expect(engine.getCellValue(adr('E1'))).toEqual(0) //MULT - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // DIV - expect(engine.getCellValue(adr('G1'))).toEqual(1) // EXP - }) - - it('boolean to int, false vs true', () => { - const engine = HyperFormula.buildFromArray([ - [false, true, '=A1+B1', '=A1-B1', '=A1*B1', '=A1/B1', '=A1^B1'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(1) //ADD - expect(engine.getCellValue(adr('D1'))).toEqual(-1) //SUB - expect(engine.getCellValue(adr('E1'))).toEqual(0) //MULT - expect(engine.getCellValue(adr('F1'))).toEqual(0) // DIV - expect(engine.getCellValue(adr('G1'))).toEqual(0) // EXP - }) - - it('boolean to int, false vs false', () => { - const engine = HyperFormula.buildFromArray([ - [false, false, '=A1+B1', '=A1-B1', '=A1*B1', '=A1/B1', '=A1^B1', '=+A1', '=-A1', '=A1%'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(0) //ADD - expect(engine.getCellValue(adr('D1'))).toEqual(0) //SUB - expect(engine.getCellValue(adr('E1'))).toEqual(0) //MULT - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // DIV - expect(engine.getCellValue(adr('G1'))).toEqual(1) // EXP - expect(engine.getCellValue(adr('H1'))).toEqual(false) // UNARY PLUS - expect(engine.getCellValue(adr('I1'))).toEqual(0) // UNARY MINUS - expect(engine.getCellValue(adr('J1'))).toEqual(0) // PERCENTAGE - }) - - it('boolean to int, null vs false', () => { - const engine = HyperFormula.buildFromArray([ - [null, false, '=A1+B1', '=A1-B1', '=A1*B1', '=A1/B1', '=A1^B1'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(0) //ADD - expect(engine.getCellValue(adr('D1'))).toEqual(0) //SUB - expect(engine.getCellValue(adr('E1'))).toEqual(0) //MULT - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // DIV - expect(engine.getCellValue(adr('G1'))).toEqual(1) // EXP - }) - it('order operations, \'\' vs null', () => { - const engine = HyperFormula.buildFromArray([ - ['', null, '=A1>B1', '=A1=B1', '=A1<=B1'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - expect(engine.getCellValue(adr('D1'))).toEqual(false) - expect(engine.getCellValue(adr('E1'))).toEqual(true) - expect(engine.getCellValue(adr('F1'))).toEqual(true) - }) - it('order operations, string vs boolean', () => { - const engine = HyperFormula.buildFromArray([ - ['string', false, '=A1>B1', '=A1=B1', '=A1<=B1'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - expect(engine.getCellValue(adr('D1'))).toEqual(true) - expect(engine.getCellValue(adr('E1'))).toEqual(false) - expect(engine.getCellValue(adr('F1'))).toEqual(true) - }) - it('order operations, null vs false', () => { - const engine = HyperFormula.buildFromArray([ - [null, false, '=A1>B1', '=A1=B1', '=A1<=B1'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - expect(engine.getCellValue(adr('D1'))).toEqual(false) - expect(engine.getCellValue(adr('E1'))).toEqual(true) - expect(engine.getCellValue(adr('F1'))).toEqual(true) - }) - it('order operations, null vs 1', () => { - const engine = HyperFormula.buildFromArray([ - [null, 1, '=A1>B1', '=A1=B1', '=A1<=B1'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - expect(engine.getCellValue(adr('D1'))).toEqual(true) - expect(engine.getCellValue(adr('E1'))).toEqual(false) - expect(engine.getCellValue(adr('F1'))).toEqual(true) - }) - - it('order operations, -1 vs null', () => { - const engine = HyperFormula.buildFromArray([ - [-1, null, '=A1>B1', '=A1=B1', '=A1<=B1'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - expect(engine.getCellValue(adr('D1'))).toEqual(true) - expect(engine.getCellValue(adr('E1'))).toEqual(false) - expect(engine.getCellValue(adr('F1'))).toEqual(true) - }) - - it('order operations, 0 vs null', () => { - const engine = HyperFormula.buildFromArray([ - [0, null, '=A1>B1', '=A1=B1', '=A1<=B1'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - expect(engine.getCellValue(adr('D1'))).toEqual(false) - expect(engine.getCellValue(adr('E1'))).toEqual(true) - expect(engine.getCellValue(adr('F1'))).toEqual(true) - }) - - it('order operations, 0 vs false', () => { - const engine = HyperFormula.buildFromArray([ - [0, false, '=A1>B1', '=A1=B1', '=A1<=B1', '=A1=B1'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - expect(engine.getCellValue(adr('D1'))).toEqual(true) - expect(engine.getCellValue(adr('E1'))).toEqual(false) - expect(engine.getCellValue(adr('F1'))).toEqual(true) - expect(engine.getCellValue(adr('G1'))).toEqual(false) - }) - - it('order operations, 1 vs true', () => { - const engine = HyperFormula.buildFromArray([ - [1, true, '=A1>B1', '=A1=B1', '=A1<=B1', '=A1=B1'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - expect(engine.getCellValue(adr('D1'))).toEqual(true) - expect(engine.getCellValue(adr('E1'))).toEqual(false) - expect(engine.getCellValue(adr('F1'))).toEqual(true) - expect(engine.getCellValue(adr('G1'))).toEqual(false) - }) -}) - -describe('#requiresRegex', () => { - it('config.useRegularExpressions = false && config.useWildcards = false)', () => { - const config = new Config({ useRegularExpressions: false, useWildcards: false }) - const dateTimeHelper = new DateTimeHelper(config) - const numberLiteralsHelper = new NumberLiteralHelper(config) - const arithmeticHelper = new ArithmeticHelper(config, dateTimeHelper, numberLiteralsHelper) - expect(arithmeticHelper.requiresRegex('')).toEqual(!config.matchWholeCell) - }) - - it('config.useRegularExpressions = false && config.useWildcards = true)', () => { - const config = new Config({ useRegularExpressions: false, useWildcards: true }) - const dateTimeHelper = new DateTimeHelper(config) - const numberLiteralsHelper = new NumberLiteralHelper(config) - const arithmeticHelper = new ArithmeticHelper(config, dateTimeHelper, numberLiteralsHelper) - expect(arithmeticHelper.requiresRegex('')).toEqual(false) - expect(arithmeticHelper.requiresRegex('foo')).toEqual(false) - expect(arithmeticHelper.requiresRegex('foo*bar')).toEqual(true) - expect(arithmeticHelper.requiresRegex('foo!bar')).toEqual(false) - expect(arithmeticHelper.requiresRegex('[ab][0-9]')).toEqual(false) - expect(arithmeticHelper.requiresRegex('[ab].*[0-9]')).toEqual(true) - }) - - it('config.useRegularExpressions = true && config.useWildcards = false)', () => { - const config = new Config({ useRegularExpressions: true, useWildcards: false }) - const dateTimeHelper = new DateTimeHelper(config) - const numberLiteralsHelper = new NumberLiteralHelper(config) - const arithmeticHelper = new ArithmeticHelper(config, dateTimeHelper, numberLiteralsHelper) - expect(arithmeticHelper.requiresRegex('')).toEqual(false) - expect(arithmeticHelper.requiresRegex('foo')).toEqual(false) - expect(arithmeticHelper.requiresRegex('foo*bar')).toEqual(true) - expect(arithmeticHelper.requiresRegex('foo!bar')).toEqual(true) - expect(arithmeticHelper.requiresRegex('[ab][0-9]')).toEqual(true) - expect(arithmeticHelper.requiresRegex('[ab].*[0-9]')).toEqual(true) - }) - - it('config.useRegularExpressions = true && config.useWildcards = true)', () => { - const config = new Config({ useRegularExpressions: true, useWildcards: true }) - const dateTimeHelper = new DateTimeHelper(config) - const numberLiteralsHelper = new NumberLiteralHelper(config) - const arithmeticHelper = new ArithmeticHelper(config, dateTimeHelper, numberLiteralsHelper) - expect(arithmeticHelper.requiresRegex('')).toEqual(false) - expect(arithmeticHelper.requiresRegex('foo')).toEqual(false) - expect(arithmeticHelper.requiresRegex('foo*bar')).toEqual(true) - expect(arithmeticHelper.requiresRegex('foo!bar')).toEqual(true) - expect(arithmeticHelper.requiresRegex('[ab][0-9]')).toEqual(true) - expect(arithmeticHelper.requiresRegex('[ab].*[0-9]')).toEqual(true) - }) -}) diff --git a/test/unit/interpreter/criterion-computations.spec.ts b/test/unit/interpreter/criterion-computations.spec.ts deleted file mode 100644 index bc56cbfe25..0000000000 --- a/test/unit/interpreter/criterion-computations.spec.ts +++ /dev/null @@ -1,318 +0,0 @@ -import {HyperFormula} from '../../../src' -import {adr} from '../testUtils' - -describe('Criterions - operators computations', () => { - it('usage of greater than operator', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '3'], - ['1', '5'], - ['2', '7'], - ['=SUMIF(A1:A3, ">1", B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(7) - }) - - it('usage of greater than or equal operator', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '3'], - ['1', '5'], - ['2', '7'], - ['=SUMIF(A1:A3, ">=1", B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(12) - }) - - it('usage of less than operator', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '3'], - ['1', '5'], - ['2', '7'], - ['=SUMIF(A1:A2, "<1", B1:B2)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(3) - }) - - it('usage of less than or equal operator', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '3'], - ['1', '5'], - ['2', '7'], - ['=SUMIF(A1:A3, "<=1", B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(8) - }) - - it('usage of equal operator', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '3'], - ['1', '5'], - ['2', '7'], - ['=SUMIF(A1:A3, "=1", B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(5) - }) - - it('usage of not equal operator', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '3'], - ['1', '5'], - ['2', '7'], - ['=SUMIF(A1:A3, "<>1", B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(10) - }) - - it('empty values #1', () => { - const engine = HyperFormula.buildFromArray([ - ['1', ''], - ['2', '8'], - ['3', '9'], - ['=SUMIF(B1:B3, "=", A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(0) - }) - - it('empty values #2', () => { - const engine = HyperFormula.buildFromArray([ - ['1', ''], - ['2', '8'], - ['3', '9'], - ['=SUMIF(B1:B3, "<>", A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(6) - }) - - it('empty values #3', () => { - const engine = HyperFormula.buildFromArray([ - ['1', ' '], - ['2', '1'], - ['3', 'TRUE'], - ['=SUMIF(B1:B3, "=0", A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(0) - }) - - it('empty values #4', () => { - const engine = HyperFormula.buildFromArray([ - [''], - ['8'], - ['9'], - ['=COUNTIF(A1:A3, "=")'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(0) - }) -}) - -describe('big test', () => { - it('regex example', () => { - const formulas = [ - ['w b 2r2 x', 8.89999999999418, null, 'w', '[^hrcsx]*w ?f?', '=COUNTIF(A1:A49,E1)', '=SUMIF($A$1:$A$49,E1,$B$1:$B$49)'], - ['w F', 2.20000000001164, null, 'w b', '[^hrcsx]*w*b ?f?', '=COUNTIF(A2:A50,E2)', '=SUMIF($A$1:$A$49,E2,$B$1:$B$49)'], - ['w', 2.19999999999709, null, 'r &/or c', '[^hsx]*[cr][^hsx]*', '=COUNTIF(A3:A51,E3)', '=SUMIF($A$1:$A$49,E3,$B$1:$B$49)'], - ['w', 2.09999999999127, null, '1h', '.*1h[^sx]*', '=COUNTIF(A4:A52,E4)', '=SUMIF($A$1:$A$49,E4,$B$1:$B$49)'], - ['w b 1c2 x', 2.40000000000873, null, '2h', '.*2h[^sx]*', '=COUNTIF(A5:A53,E5)', '=SUMIF($A$1:$A$49,E5,$B$1:$B$49)'], - ['w 1c1 F', 4.39999999999418, null, '1h 1s', '.*1h.*1s.*[^x]', '=COUNTIF(A9:A57,E6)', '=SUMIF($A$1:$A$49,E6,$B$1:$B$49)'], - ['w b F', 2.30000000000291, null, '2h 1s', '.*2h.*1s.*[^x]', '=COUNTIF(A11:A59,E7)', '=SUMIF($A$1:$A$49,E7,$B$1:$B$49)'], - ['w F', 2.30000000000291, null, '3h 1s', '.*3h.*1s.*[^x]', '=COUNTIF(A12:A60,E8)', '=SUMIF($A$1:$A$49,E8,$B$1:$B$49)'], - ['w F', 2.09999999999127, null, '2h 2s', '.*2h.*2s.*[^x]', '=COUNTIF(A15:A63,E9)', '=SUMIF($A$1:$A$49,E9,$B$1:$B$49)'], - ['w F b', 2.20000000001164, null, '3h 2s', '.*3h.*2s.*[^x]', '=COUNTIF(A16:A64,E10)', '=SUMIF($A$1:$A$49,E10,$B$1:$B$49)'], - ['w', 2.29999999998836, null, '4h 2s', '.*4h.*2s.*[^x]', '=COUNTIF(A19:A67,E11)', '=SUMIF($A$1:$A$49,E11,$B$1:$B$49)'], - ['w 2r2', 3.30000000000291, null, '5h 2s', '.*5h.*2s.*[^x]', '=COUNTIF(A20:A68,E12)', '=SUMIF($A$1:$A$49,E12,$B$1:$B$49)'], - ['w 1r3', 2.30000000000291, null, '4h 3s', '.*4h.*3s.*[^x]', '=COUNTIF(A22:A70,E13)', '=SUMIF($A$1:$A$49,E13,$B$1:$B$49)'], - ['wF', 3.39999999999418, null, '5h 3s', '.*5h.*3s.*[^x]', '=COUNTIF(A23:A71,E14)', '=SUMIF($A$1:$A$49,E14,$B$1:$B$49)'], - ['wF', 3.20000000001164, null, '5h 4s', '.*5h.*4s.*[^x]', '=COUNTIF(A24:A72,E15)', '=SUMIF($A$1:$A$49,E15,$B$1:$B$49)'], - ['wF', 3.29999999998836, null, '6h 4s', '.*6h.*4s.*[^x]', '=COUNTIF(A26:A74,E16)', '=SUMIF($A$1:$A$49,E16,$B$1:$B$49)'], - ['wF', 3.30000000000291, null, '7h 4s', '.*7h.*4s.*[^x]', '=COUNTIF(A28:A76,E17)', '=SUMIF($A$1:$A$49,E17,$B$1:$B$49)'], - ['wF', 3.30000000000291, null, '7h 5s', '.*7h.*5s.*[^x]', '=COUNTIF(A29:A77,E18)', '=SUMIF($A$1:$A$49,E18,$B$1:$B$49)'], - ['w F 2r3', 5.60000000000582, null, '9h 9s', '.*9h.*9s.*[^x]', '=COUNTIF(A30:A78,E19)', '=SUMIF($A$1:$A$49,E19,$B$1:$B$49)'], - ['w 2r1', 3.29999999998836, null, 'x', '.*x.*', '=COUNTIF(A1:A49,E20)', '=SUMIF($A$1:$A$49,E20,$B$1:$B$49)'], - ['w F 2r1', 4.5], - ['w 1r2', 3.30000000000291], - ['w b 2r1 1c1', 5.60000000000582], - ['w 1r1 1c1', 3.30000000000291], - ['w F', 2.29999999998836], - ['F w', 2.20000000001164], - ['Fw', 2.19999999999709], - ['w 2r2 1c1', 5.59999999999127], - ['w 1c2', 2.20000000001164], - ['w 2r2 1h1 F x', 6.59999999999127], - ['w 2r2 1h1 x', 6.69999999999709], - ['w 2h1 F', 5.60000000000582], - ['w 2h1', 6.60000000000582], - ['F w 2h1', 5.59999999999127], - ['w 2h1', 6.69999999999709], - ['w 2h1', 6.60000000000582], - ['w 2h1 1s1', 13.3999999999942], - ['w 2h1 1s1', 11.1000000000058], - ['w 2h1 1s1 F', 11.1000000000058], - ['w 2h1 1s1 F b', 12.1999999999971], - ['w 2h1 1s1', 11.0999999999913], - ['w 2h1 1s1 F b', 12.2000000000116], - ['w 2h1 1s1 F', 11.0999999999913], - ['w 3h1 1s1 F', 11.1999999999971], - ['w 3h1 1s1 F', 12.2000000000116], - ['w 3h1 1s1 F 1r1', 12.1999999999971], - ['w 3h1 1s1 Fx', 24.5], - ['w 3h1 1s1 F', 10], - ['w 3h1 1s1 F', 12.1999999999971], - [], - ['=COUNTA(A1:A49)', '=SUM(B1:B50)', null, null, 'codes counted > ', '=SUM(F1:F20)', '=SUM(G1:G21)'], - ] - - const engine = HyperFormula.buildFromArray(formulas, {useRegularExpressions: true, precisionRounding: 13}) - - expect(engine.getCellValue(adr('B51'))).toEqual(304.5) - expect(engine.getCellValue(adr('G51'))).toEqual(304.5) - - expect(engine.getCellValue(adr('F1'))).toEqual(14) - expect(engine.getCellValue(adr('F2'))).toEqual(2) - expect(engine.getCellValue(adr('F3'))).toEqual(11) - expect(engine.getCellValue(adr('F4'))).toEqual(0) - expect(engine.getCellValue(adr('F5'))).toEqual(5) - expect(engine.getCellValue(adr('F6'))).toEqual(0) - expect(engine.getCellValue(adr('F7'))).toEqual(7) - expect(engine.getCellValue(adr('F8'))).toEqual(5) - expect(engine.getCellValue(adr('F9'))).toEqual(0) - expect(engine.getCellValue(adr('F10'))).toEqual(0) - expect(engine.getCellValue(adr('F11'))).toEqual(0) - expect(engine.getCellValue(adr('F12'))).toEqual(0) - expect(engine.getCellValue(adr('F13'))).toEqual(0) - expect(engine.getCellValue(adr('F14'))).toEqual(0) - expect(engine.getCellValue(adr('F15'))).toEqual(0) - expect(engine.getCellValue(adr('F16'))).toEqual(0) - expect(engine.getCellValue(adr('F17'))).toEqual(0) - expect(engine.getCellValue(adr('F18'))).toEqual(0) - expect(engine.getCellValue(adr('F19'))).toEqual(0) - expect(engine.getCellValue(adr('F20'))).toEqual(5) - - expect(engine.getCellValue(adr('G1'))).toEqual(36.39999999998) - expect(engine.getCellValue(adr('G2'))).toEqual(4.5000000000146) - expect(engine.getCellValue(adr('G3'))).toEqual(43.400000000009) - expect(engine.getCellValue(adr('G4'))).toEqual(0) - expect(engine.getCellValue(adr('G5'))).toEqual(31.100000000006) - expect(engine.getCellValue(adr('G6'))).toEqual(0) - expect(engine.getCellValue(adr('G7'))).toEqual(82.199999999997) - expect(engine.getCellValue(adr('G8'))).toEqual(57.800000000003) - expect(engine.getCellValue(adr('G9'))).toEqual(0) - expect(engine.getCellValue(adr('G10'))).toEqual(0) - expect(engine.getCellValue(adr('G11'))).toEqual(0) - expect(engine.getCellValue(adr('G12'))).toEqual(0) - expect(engine.getCellValue(adr('G13'))).toEqual(0) - expect(engine.getCellValue(adr('G14'))).toEqual(0) - expect(engine.getCellValue(adr('G15'))).toEqual(0) - expect(engine.getCellValue(adr('G16'))).toEqual(0) - expect(engine.getCellValue(adr('G17'))).toEqual(0) - expect(engine.getCellValue(adr('G18'))).toEqual(0) - expect(engine.getCellValue(adr('G19'))).toEqual(0) - expect(engine.getCellValue(adr('G20'))).toEqual(49.099999999991) - }) - - it('Gnumeric test file', () => { - const formulas = [ - [null, null, '=IF(AND(C10:C49), "All ok", "Bug!")'], - [], - [], - [], - [], - ['1', null, null, null, '1', null, null, 'TRUE', null, null, 'TRUE', '\'1', 'Jesper'], - ['2', '8', null, null, '2', '1', null, 'FALSE', '8', null, '\'TRUE', '1', 'apples'], - ['3', '9', null, null, '3', 'TRUE', null, '\'1.0', '9', null, null, '\'1.0', ], - [], - ['=SUMIF($B$6:$B$8, "=", $A$6:$A$8)', '1', '=A10=B10'], - ['=SUMIF($B$6:$B$8, ">=", $A$6:$A$8)', '0', '=A11=B11'], - ['=SUMIF($B$6:$B$8, "<=", $A$6:$A$8)', '0', '=A12=B12'], - ['=SUMIF($B$6:$B$8, "<>", $A$6:$A$8)', '5', '=A13=B13'], - ['=SUMIF($B$6:$B$8, "<>x", $A$6:$A$8)', '6', '=A14=B14'], - ['=SUMIF($B$6:$B$8, ">", $A$6:$A$8)', '0', '=A15=B15'], - ['=SUMIF($B$6:$B$8, "<", $A$6:$A$8)', '0', '=A16=B16'], - ['=SUMIF($B$6:$B$8, "=8.0", $A$6:$A$8)', '2', '=A17=B17'], - ['=SUMIF($B$6:$B$8, "= 8.0", $A$6:$A$8)', '2', '=A18=B18'], - ['=SUMIF($B$6:$B$8, "= 8.0 ", $A$6:$A$8)', '2', '=A19=B19'], - ['=SUMIF($B$6:$B$8, "= 8.0 ", $A$6:$A$8)', '2', '=A20=B20'], - ['=SUMIF($F$6:$F$8, "=TRUE", $E$6:$E$8)', '3', '=A21=B21'], - ['=SUMIF($F$6:$F$8, "=1", $E$6:$E$8)', '2', '=A22=B22'], - ['=SUMIF($F$6:$F$8, "=0", $E$6:$E$8)', '0', '=A23=B23'], - ['=SUMIF($F$6:$F$8, "=FALSE", $E$6:$E$8)', '0', '=A24=B24'], - ['=SUMIF($F$6:$F$8, "=T", $E$6:$E$8)', '0', '=A25=B25'], - ['=SUMIF($F$6:$F$8, "=1.0", $E$6:$E$8)', '2', '=A26=B26'], - ['=SUMIF($F$6:$F$8, "=x", $E$6:$E$8)', '0', '=A27=B27'], - ['=SUMIF($I$6:$I$8, "=", $H$6:$H$8)', '0', '=A28=B28'], - ['=SUMIF($I$6:$I$8, "=9", $H$6:$H$8)', '0', '=A29=B29'], - ['=SUMIF($I$6:$I$8, "=1.0", $H$6:$H$8)', '0', '=A30=B30'], - ['=COUNTIF($I$6:$I$8, "=")', '1', '=A31=B31'], - ['=COUNTIF($H$6:$H$8, "1")', '1', '=A32=B32'], - ['=SUMIF($H$6:$H$8, "=TRUE")', '0', '=A33=B33'], - ['=COUNTIF($K$6:$K$6, "TRUE")', '1', '=A34=B34'], - ['=COUNTIF($K$6:$K$6, TRUE)', '1', '=A35=B35'], - ['=COUNTIF($K$7:$K$7, "TRUE")', '0', '=A36=B36'], - ['=COUNTIF($K$7:$K$7, TRUE)', '0', '=A37=B37'], - ['=COUNTIF($K$6:$K$8, "TRUE")', '1', '=A38=B38'], - ['=COUNTIF($K$6:$K$8, "=TRUE")', '1', '=A39=B39'], - ['=COUNTIF($K$6:$K$8, "~TRUE")', '0', '=A40=B40'], - ['=COUNTIF($K$6:$K$8, TRUE)', '1', '=A41=B41'], - ['=COUNTIF($L$6:$L$8, "1")', '3', '=A42=B42'], - ['=COUNTIF($L$6:$L$8, "1.0")', '3', '=A43=B43'], - ['=COUNTIF($L$6:$L$8, 1)', '3', '=A44=B44'], - ['=COUNTIF($M$6:$M$7, "es")', '0', '=A45=B45'], - ['=COUNTIF($M$6:$M$7, "*es")', '1', '=A46=B46'], - ['=COUNTIF($M$6:$M$7, "es*")', '0', '=A47=B47'], - ['=COUNTIF($M$6:$M$7, "*es*")', '2', '=A48=B48'], - ['=COUNTIF($M$6:$M$7, "*ES*")', '2', '=A49=B49'] - ] - const engine = HyperFormula.buildFromArray([]) - engine.addNamedExpression('TRUE', '=TRUE()', undefined) - engine.addNamedExpression('FALSE', '=FALSE()', undefined) - engine.setCellContents(adr('A1'), formulas) - expect(engine.getCellValue(adr('A10'))).toEqual(1) - expect(engine.getCellValue(adr('A11'))).toEqual(0) - expect(engine.getCellValue(adr('A12'))).toEqual(0) - expect(engine.getCellValue(adr('A13'))).toEqual(5) - expect(engine.getCellValue(adr('A14'))).toEqual(6) - expect(engine.getCellValue(adr('A15'))).toEqual(0) - expect(engine.getCellValue(adr('A16'))).toEqual(0) - expect(engine.getCellValue(adr('A17'))).toEqual(2) - expect(engine.getCellValue(adr('A18'))).toEqual(2) - expect(engine.getCellValue(adr('A19'))).toEqual(2) - expect(engine.getCellValue(adr('A20'))).toEqual(2) - expect(engine.getCellValue(adr('A21'))).toEqual(3) - expect(engine.getCellValue(adr('A22'))).toEqual(2) - expect(engine.getCellValue(adr('A23'))).toEqual(0) - expect(engine.getCellValue(adr('A24'))).toEqual(0) - expect(engine.getCellValue(adr('A25'))).toEqual(0) - expect(engine.getCellValue(adr('A26'))).toEqual(2) - expect(engine.getCellValue(adr('A27'))).toEqual(0) - expect(engine.getCellValue(adr('A28'))).toEqual(0) - expect(engine.getCellValue(adr('A29'))).toEqual(0) - expect(engine.getCellValue(adr('A30'))).toEqual(0) - expect(engine.getCellValue(adr('A31'))).toEqual(1) - expect(engine.getCellValue(adr('A32'))).toEqual(1) - expect(engine.getCellValue(adr('A33'))).toEqual(0) - expect(engine.getCellValue(adr('A34'))).toEqual(1) - // expect(engine.getCellValue(adr('A35'))).toEqual(1) - expect(engine.getCellValue(adr('A36'))).toEqual(0) - // expect(engine.getCellValue(adr('A37'))).toEqual(0) - expect(engine.getCellValue(adr('A38'))).toEqual(1) - expect(engine.getCellValue(adr('A39'))).toEqual(1) - // expect(engine.getCellValue(adr('A40'))).toEqual(0) - // expect(engine.getCellValue(adr('A41'))).toEqual(1) - expect(engine.getCellValue(adr('A42'))).toEqual(3) - expect(engine.getCellValue(adr('A43'))).toEqual(3) - // expect(engine.getCellValue(adr('A44'))).toEqual(3) - expect(engine.getCellValue(adr('A45'))).toEqual(0) - expect(engine.getCellValue(adr('A46'))).toEqual(1) - expect(engine.getCellValue(adr('A47'))).toEqual(0) - expect(engine.getCellValue(adr('A48'))).toEqual(2) - expect(engine.getCellValue(adr('A49'))).toEqual(2) - // expect(engine.getCellValue(adr('C1'))).toEqual('All ok') - }) -}) diff --git a/test/unit/interpreter/cyclical-deps.spec.ts b/test/unit/interpreter/cyclical-deps.spec.ts deleted file mode 100644 index e6555e1d83..0000000000 --- a/test/unit/interpreter/cyclical-deps.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {adr, detailedError} from '../testUtils' - -describe('Cyclical dependencies and error literals', () => { - it('Cyclical errors might not propagate', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2', '=A1'], - ['=ISERROR(A1)', '=ISERROR(B1)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('B2'))).toEqual(true) - }) - it('Errors should be parsed and propagated', () => { - const engine = HyperFormula.buildFromArray([ - ['=B1', '=A1', '=ISERROR(B1)', '=C1+D1', '=ISERROR(D1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('C1'))).toEqual(true) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('E1'))).toEqual(true) - }) -}) diff --git a/test/unit/interpreter/date-and-time-arithmetic.spec.ts b/test/unit/interpreter/date-and-time-arithmetic.spec.ts deleted file mode 100644 index eede5f2e6d..0000000000 --- a/test/unit/interpreter/date-and-time-arithmetic.spec.ts +++ /dev/null @@ -1,311 +0,0 @@ -import {CellValueDetailedType, HyperFormula} from '../../../src' -import {adr} from '../testUtils' - -describe('Date arithmetic', () => { - it('subtract two dates', () => { - const engine = HyperFormula.buildFromArray([ - ['02/02/2020', '06/02/2019', '=A1-B1'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(361) - }) - - it('subtract two dates with custom date format', () => { - const engine = HyperFormula.buildFromArray([ - ['09/20/2022', '09/25/2022', '=B1-A1'], - ], { dateFormats: ['MM/DD/YYYY'] }) - - expect(engine.getCellValue(adr('C1'))).toBe(5) - }) - - it('compare two dates', () => { - const engine = HyperFormula.buildFromArray([ - ['02/02/2020', '02/06/2019', '=A1>B1', '=A1=B1', '=A1<=B1'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('D1'))).toBe(false) - expect(engine.getCellValue(adr('E1'))).toBe(true) - expect(engine.getCellValue(adr('F1'))).toBe(false) - }) - - it('compare two datestrings', () => { - const engine = HyperFormula.buildFromArray([ - ['="02/02/2020"', '="02/06/2019"', '=A1>B1', '=A1=B1', '=A1<=B1'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('D1'))).toBe(false) - expect(engine.getCellValue(adr('E1'))).toBe(true) - expect(engine.getCellValue(adr('F1'))).toBe(false) - }) - - it('compare date with datestring, different dates', () => { - const engine = HyperFormula.buildFromArray([ - ['="02/02/2020"', '02/06/2019', '=A1>B1', '=A1=B1', '=A1<=B1', '=A1=B1', '=A1<>B1'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('D1'))).toBe(false) - expect(engine.getCellValue(adr('E1'))).toBe(true) - expect(engine.getCellValue(adr('F1'))).toBe(false) - expect(engine.getCellValue(adr('G1'))).toBe(false) - expect(engine.getCellValue(adr('H1'))).toBe(true) - }) - - it('compare date with datestring, the same dates', () => { - const engine = HyperFormula.buildFromArray([ - ['="02/02/2020"', '02/02/2020', '=A1>B1', '=A1=B1', '=A1<=B1', '=A1=B1', '=A1<>B1'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(false) - expect(engine.getCellValue(adr('D1'))).toBe(false) - expect(engine.getCellValue(adr('E1'))).toBe(true) - expect(engine.getCellValue(adr('F1'))).toBe(true) - expect(engine.getCellValue(adr('G1'))).toBe(true) - expect(engine.getCellValue(adr('H1'))).toBe(false) - }) - - it('compare date with bool', () => { - const engine = HyperFormula.buildFromArray([ - ['="02/02/2020"', '=TRUE()', '=A1>B1', '=A1=B1', '=A1<=B1'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(false) - expect(engine.getCellValue(adr('D1'))).toBe(true) - expect(engine.getCellValue(adr('E1'))).toBe(false) - expect(engine.getCellValue(adr('F1'))).toBe(true) - }) - - it('compare date with number', () => { - const engine = HyperFormula.buildFromArray([ - ['02/02/2020', '2', '=A1>B1', '=A1=B1', '=A1<=B1'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('D1'))).toBe(false) - expect(engine.getCellValue(adr('E1'))).toBe(true) - expect(engine.getCellValue(adr('F1'))).toBe(false) - }) - - it('sum date with number', () => { - const engine = HyperFormula.buildFromArray([ - ['02/02/2020', '2', '=A1+B1'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(43865) - }) - - it('sum date with boolean', () => { - const engine = HyperFormula.buildFromArray([ - ['02/02/2020', '=TRUE()', '=A1+B1'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(43864) - }) - - it('functions on dates', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISEVEN("02/02/2020")', '=COS("02/02/2020")', '=BITOR("02/02/2020","16/08/1985")'], - ], {smartRounding: false}) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('B1'))).toBe(0.9965266857693633) - expect(engine.getCellValue(adr('C1'))).toBe(64383) - }) -}) - -describe('Time arithmetic', () => { - it('subtract two time values', () => { - const engine = HyperFormula.buildFromArray([ - ['13:13', '11:50', '=TEXT(A1-B1, "hh:mm")'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe('01:23') - }) - - it('subtract two time values - rounding test', () => { - const engine = HyperFormula.buildFromArray([ - ['15:00', '14:00', '=TEXT(A1-B1, "hh:mm")'], - ['15:01', '14:00', '=TEXT(A2-B2, "hh:mm")'], - ['15:02', '14:00', '=TEXT(A3-B3, "hh:mm")'], - ['15:03', '14:00', '=TEXT(A4-B4, "hh:mm")'], - ['15:04', '14:00', '=TEXT(A5-B5, "hh:mm")'], - ['15:05', '14:00', '=TEXT(A6-B6, "hh:mm")'], - ['15:06', '14:00', '=TEXT(A7-B7, "hh:mm")'], - ['15:07', '14:00', '=TEXT(A8-B8, "hh:mm")'], - ['15:08', '14:00', '=TEXT(A9-B9, "hh:mm")'], - ['15:09', '14:00', '=TEXT(A10-B10, "hh:mm")'], - ['15:10', '14:00', '=TEXT(A11-B11, "hh:mm")'], - ['15:11', '14:00', '=TEXT(A12-B12, "hh:mm")'], - ['15:12', '14:00', '=TEXT(A13-B13, "hh:mm")'], - ['15:13', '14:00', '=TEXT(A14-B14, "hh:mm")'], - ['15:14', '14:00', '=TEXT(A15-B15, "hh:mm")'], - ['15:15', '14:00', '=TEXT(A16-B16, "hh:mm")'], - ['15:16', '14:00', '=TEXT(A17-B17, "hh:mm")'], - ['15:17', '14:00', '=TEXT(A18-B18, "hh:mm")'], - ['15:18', '14:00', '=TEXT(A19-B19, "hh:mm")'], - ['15:19', '14:00', '=TEXT(A20-B20, "hh:mm")'], - ['15:20', '14:00', '=TEXT(A21-B21, "hh:mm")'], - ['15:21', '14:00', '=TEXT(A22-B22, "hh:mm")'], - ['15:22', '14:00', '=TEXT(A23-B23, "hh:mm")'], - ['15:23', '14:00', '=TEXT(A24-B24, "hh:mm")'], - ['15:24', '14:00', '=TEXT(A25-B25, "hh:mm")'], - ['15:25', '14:00', '=TEXT(A26-B26, "hh:mm")'], - ['15:26', '14:00', '=TEXT(A27-B27, "hh:mm")'], - ['15:27', '14:00', '=TEXT(A28-B28, "hh:mm")'], - ['15:28', '14:00', '=TEXT(A29-B29, "hh:mm")'], - ['15:29', '14:00', '=TEXT(A30-B30, "hh:mm")'], - ['15:30', '14:00', '=TEXT(A31-B31, "hh:mm")'], - ['15:31', '14:00', '=TEXT(A32-B32, "hh:mm")'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe('01:00') - expect(engine.getCellValue(adr('C2'))).toBe('01:01') - expect(engine.getCellValue(adr('C3'))).toBe('01:02') - expect(engine.getCellValue(adr('C4'))).toBe('01:03') - expect(engine.getCellValue(adr('C5'))).toBe('01:04') - expect(engine.getCellValue(adr('C6'))).toBe('01:05') - expect(engine.getCellValue(adr('C7'))).toBe('01:06') - expect(engine.getCellValue(adr('C8'))).toBe('01:07') - expect(engine.getCellValue(adr('C9'))).toBe('01:08') - expect(engine.getCellValue(adr('C10'))).toBe('01:09') - expect(engine.getCellValue(adr('C11'))).toBe('01:10') - expect(engine.getCellValue(adr('C12'))).toBe('01:11') - expect(engine.getCellValue(adr('C13'))).toBe('01:12') - expect(engine.getCellValue(adr('C14'))).toBe('01:13') - expect(engine.getCellValue(adr('C15'))).toBe('01:14') - expect(engine.getCellValue(adr('C16'))).toBe('01:15') - expect(engine.getCellValue(adr('C17'))).toBe('01:16') - expect(engine.getCellValue(adr('C18'))).toBe('01:17') - expect(engine.getCellValue(adr('C19'))).toBe('01:18') - expect(engine.getCellValue(adr('C20'))).toBe('01:19') - expect(engine.getCellValue(adr('C21'))).toBe('01:20') - expect(engine.getCellValue(adr('C22'))).toBe('01:21') - expect(engine.getCellValue(adr('C23'))).toBe('01:22') - expect(engine.getCellValue(adr('C24'))).toBe('01:23') - expect(engine.getCellValue(adr('C25'))).toBe('01:24') - expect(engine.getCellValue(adr('C26'))).toBe('01:25') - expect(engine.getCellValue(adr('C27'))).toBe('01:26') - expect(engine.getCellValue(adr('C28'))).toBe('01:27') - expect(engine.getCellValue(adr('C29'))).toBe('01:28') - expect(engine.getCellValue(adr('C30'))).toBe('01:29') - expect(engine.getCellValue(adr('C31'))).toBe('01:30') - expect(engine.getCellValue(adr('C32'))).toBe('01:31') - }) - - it('compare two time values', () => { - const engine = HyperFormula.buildFromArray([ - ['13:13', '11:50', '=A1>B1', '=A1=B1', '=A1<=B1'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('D1'))).toBe(false) - expect(engine.getCellValue(adr('E1'))).toBe(true) - expect(engine.getCellValue(adr('F1'))).toBe(false) - }) - - it('compare two time-strings', () => { - const engine = HyperFormula.buildFromArray([ - ['="13:13"', '="11:50"', '=A1>B1', '=A1=B1', '=A1<=B1'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('D1'))).toBe(false) - expect(engine.getCellValue(adr('E1'))).toBe(true) - expect(engine.getCellValue(adr('F1'))).toBe(false) - }) - - it('compare a time value with a time-string, non-equal', () => { - const engine = HyperFormula.buildFromArray([ - ['="13:13"', '11:50', '=A1>B1', '=A1=B1', '=A1<=B1', '=A1=B1', '=A1<>B1'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('D1'))).toBe(false) - expect(engine.getCellValue(adr('E1'))).toBe(true) - expect(engine.getCellValue(adr('F1'))).toBe(false) - expect(engine.getCellValue(adr('G1'))).toBe(false) - expect(engine.getCellValue(adr('H1'))).toBe(true) - }) - - it('compare a time value with a time-string, equal', () => { - const engine = HyperFormula.buildFromArray([ - ['="13:13"', '13:13', '=A1>B1', '=A1=B1', '=A1<=B1', '=A1=B1', '=A1<>B1'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(false) - expect(engine.getCellValue(adr('D1'))).toBe(false) - expect(engine.getCellValue(adr('E1'))).toBe(true) - expect(engine.getCellValue(adr('F1'))).toBe(true) - expect(engine.getCellValue(adr('G1'))).toBe(true) - expect(engine.getCellValue(adr('H1'))).toBe(false) - }) - - it('compare a time-string with a boolean value', () => { - const engine = HyperFormula.buildFromArray([ - ['="13:13"', '=TRUE()', '=A1>B1', '=A1=B1', '=A1<=B1'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(false) - expect(engine.getCellValue(adr('D1'))).toBe(true) - expect(engine.getCellValue(adr('E1'))).toBe(false) - expect(engine.getCellValue(adr('F1'))).toBe(true) - }) - - it('compare a time value with a number', () => { - const engine = HyperFormula.buildFromArray([ - ['13:13', '0.01', '=A1>B1', '=A1=B1', '=A1<=B1'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('D1'))).toBe(false) - expect(engine.getCellValue(adr('E1'))).toBe(true) - expect(engine.getCellValue(adr('F1'))).toBe(false) - }) - - it('sum a time value with a number', () => { - const engine = HyperFormula.buildFromArray([ - ['13:13', '2', '=A1+B1', '=TEXT(A1+B1, "hh:mm")'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBeGreaterThan(2) - expect(engine.getCellValue(adr('D1'))).toBe('13:13') - }) - - it('sum a time value with boolean', () => { - const engine = HyperFormula.buildFromArray([ - ['13:13', '=TRUE()', '=A1+B1', '=TEXT(A1+B1, "hh:mm")'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBeGreaterThan(1) - expect(engine.getCellValue(adr('D1'))).toBe('13:13') - }) - - it('apply a numeric function to a time value', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISODD("13:13")', '=COS("13:13")'], - ], {smartRounding: false}) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('B1'))).toBe(0.8521613392800845) - }) - - it('Don\'t convert string to time value when prepended with apostrophe', () => { - const engine = HyperFormula.buildFromArray([ - ["'13:13"], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('13:13') - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.STRING) - }) - - it('Don\'t convert string to time value when there is no timeFormats configured', () => { - const engine = HyperFormula.buildFromArray([ - ['1:80'], - ], { timeFormats: [] }) - - expect(engine.getCellValue(adr('A1'))).toEqual('1:80') - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.STRING) - }) -}) diff --git a/test/unit/interpreter/error-literals.spec.ts b/test/unit/interpreter/error-literals.spec.ts deleted file mode 100644 index 67f0357032..0000000000 --- a/test/unit/interpreter/error-literals.spec.ts +++ /dev/null @@ -1,92 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Error literals', () => { - it('Errors should be parsed and propagated', () => { - const engine = HyperFormula.buildFromArray([ - ['#DIV/0!', '=A1', '=#DIV/0!'], - ['=ISERROR(A1)', '=ISERROR(B1)', '=ISERROR(C1)', '=ISERROR(#DIV/0!)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqual(true) - expect(engine.getCellValue(adr('B2'))).toEqual(true) - expect(engine.getCellValue(adr('C2'))).toEqual(true) - }) - - it('should return error when unknown error literal in formula', () => { - const engine = HyperFormula.buildFromArray([ - ['#UNKNOWN!', '=#UNKNOWN!'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('#UNKNOWN!') - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - }) - - it('error #N/A! with every combination should be supported by all comparison operators', () => { - const engine = HyperFormula.buildFromArray([ - ['#N/A', 0, '=A1=B1', '=A1>B1', '=A1=B1', '=A1<=B1', '=A1<>B1', '=A1+B1', '=A1-B1', '=A1*B1', '=A1/B1', '=A1^B1', '=A1&B1', '=+A1', '=-A1', '=A1%'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NA)) // EQUAL - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NA)) // GT - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.NA)) // LT - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.NA)) // GTE - expect(engine.getCellValue(adr('G1'))).toEqualError(detailedError(ErrorType.NA)) // LTE - expect(engine.getCellValue(adr('H1'))).toEqualError(detailedError(ErrorType.NA)) // NOT EQUAL - expect(engine.getCellValue(adr('I1'))).toEqualError(detailedError(ErrorType.NA)) // ADD - expect(engine.getCellValue(adr('J1'))).toEqualError(detailedError(ErrorType.NA)) // SUB - expect(engine.getCellValue(adr('K1'))).toEqualError(detailedError(ErrorType.NA)) // MULT - expect(engine.getCellValue(adr('L1'))).toEqualError(detailedError(ErrorType.NA)) // DIV - expect(engine.getCellValue(adr('M1'))).toEqualError(detailedError(ErrorType.NA)) // EXP - expect(engine.getCellValue(adr('N1'))).toEqualError(detailedError(ErrorType.NA)) // CONCAT - expect(engine.getCellValue(adr('O1'))).toEqualError(detailedError(ErrorType.NA)) // UNARY PLUS - expect(engine.getCellValue(adr('P1'))).toEqualError(detailedError(ErrorType.NA)) // UNARY MINUS - expect(engine.getCellValue(adr('Q1'))).toEqualError(detailedError(ErrorType.NA)) // PERCENTAGE - }) - - it('error #DIV/0! with every combination should be supported by all comparison operators', () => { - const engine = HyperFormula.buildFromArray([ - ['#DIV/0!', null, '=A1=B1', '=A1>B1', '=A1=B1', '=A1<=B1', '=A1<>B1', '=A1+B1', '=A1-B1', '=A1*B1', '=A1/B1', '=A1^B1', '=A1&B1', '=+A1', '=-A1', '=A1%'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // EQUAL - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // GT - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // LT - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // GTE - expect(engine.getCellValue(adr('G1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // LTE - expect(engine.getCellValue(adr('H1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // NOT EQUAL - expect(engine.getCellValue(adr('I1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) //ADD - expect(engine.getCellValue(adr('J1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) //SUB - expect(engine.getCellValue(adr('K1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) //MULT - expect(engine.getCellValue(adr('L1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // DIV - expect(engine.getCellValue(adr('M1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // EXP - expect(engine.getCellValue(adr('N1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // CONCAT - expect(engine.getCellValue(adr('O1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // UNARY PLUS - expect(engine.getCellValue(adr('P1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // UNARY MINUS - expect(engine.getCellValue(adr('Q1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // PERCENTAGE - }) - - it('error #CYCLE! with every combination should be supported by all comparison operators', () => { - const engine = HyperFormula.buildFromArray([ - ['#CYCLE!', null, '=A1=B1', '=A1>B1', '=A1=B1', '=A1<=B1', '=A1<>B1', '=A1+B1', '=A1-B1', '=A1*B1', '=A1/B1', '=A1^B1', '=A1&B1', '=+A1', '=-A1', '=A1%'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.CYCLE)) // EQUAL - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.CYCLE)) // GT - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.CYCLE)) // LT - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.CYCLE)) // GTE - expect(engine.getCellValue(adr('G1'))).toEqualError(detailedError(ErrorType.CYCLE)) // LTE - expect(engine.getCellValue(adr('H1'))).toEqualError(detailedError(ErrorType.CYCLE)) // NOT EQUAL - expect(engine.getCellValue(adr('I1'))).toEqualError(detailedError(ErrorType.CYCLE)) //ADD - expect(engine.getCellValue(adr('J1'))).toEqualError(detailedError(ErrorType.CYCLE)) //SUB - expect(engine.getCellValue(adr('K1'))).toEqualError(detailedError(ErrorType.CYCLE)) //MULT - expect(engine.getCellValue(adr('L1'))).toEqualError(detailedError(ErrorType.CYCLE)) // DIV - expect(engine.getCellValue(adr('M1'))).toEqualError(detailedError(ErrorType.CYCLE)) // EXP - expect(engine.getCellValue(adr('N1'))).toEqualError(detailedError(ErrorType.CYCLE)) // CONCAT - expect(engine.getCellValue(adr('O1'))).toEqualError(detailedError(ErrorType.CYCLE)) // UNARY PLUS - expect(engine.getCellValue(adr('P1'))).toEqualError(detailedError(ErrorType.CYCLE)) // UNARY MINUS - expect(engine.getCellValue(adr('Q1'))).toEqualError(detailedError(ErrorType.CYCLE)) // PERCENTAGE - }) -}) diff --git a/test/unit/interpreter/function-abs.spec.ts b/test/unit/interpreter/function-abs.spec.ts deleted file mode 100644 index fb7bf96eb4..0000000000 --- a/test/unit/interpreter/function-abs.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ABS', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([ - ['=ABS(-1)', '=ABS(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - }) - - it('given wrong argument type', () => { - const engine = HyperFormula.buildFromArray([ - ['=ABS("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="2"', '=ABS(A1)'], - ['=TRUE()', '=ABS(A2)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(2) - expect(engine.getCellValue(adr('B2'))).toEqual(1) - }) - - it('given wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ABS()'], - ['=ABS(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=ABS(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-acos.spec.ts b/test/unit/interpreter/function-acos.spec.ts deleted file mode 100644 index 18daa1f1ea..0000000000 --- a/test/unit/interpreter/function-acos.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ACOS', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=ACOS(0.5)']]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.0471975511966) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=ACOS("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('for 1 (edge)', () => { - const engine = HyperFormula.buildFromArray([['=ACOS(1)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - }) - - it('for -1 (edge)', () => { - const engine = HyperFormula.buildFromArray([['=ACOS(-1)']], - {smartRounding: false}) - - expect(engine.getCellValue(adr('A1'))).toEqual(Math.PI) - }) - - it('when value too large', () => { - const engine = HyperFormula.buildFromArray([['=ACOS(1.1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('when value too small', () => { - const engine = HyperFormula.buildFromArray([['=ACOS(-1.1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=ACOS()', '=ACOS(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=ACOS(A1)'], - ['=TRUE()', '=ACOS(A2)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(3.141592654) - expect(engine.getCellValue(adr('B2'))).toBeCloseTo(0) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=ACOS(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-acosh.spec.ts b/test/unit/interpreter/function-acosh.spec.ts deleted file mode 100644 index e3de7d15bf..0000000000 --- a/test/unit/interpreter/function-acosh.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ACOSH', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=ACOSH(1)', '=ACOSH(2)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(1.31695789692482) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=ACOSH("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('too small', () => { - const engine = HyperFormula.buildFromArray([['=ACOSH(0.9)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=ACOSH()', '=ACOSH(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="1"', '=ACOSH(A1)'], - ['=TRUE()', '=ACOSH(A2)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(0) - expect(engine.getCellValue(adr('B2'))).toEqual(0) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=ACOSH(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-acot.spec.ts b/test/unit/interpreter/function-acot.spec.ts deleted file mode 100644 index 3c7a000ce7..0000000000 --- a/test/unit/interpreter/function-acot.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ACOT', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=ACOT(0)', '=ACOT(1)']]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.5707963267949) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.785398163397448) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=ACOT("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=ACOT()', '=ACOT(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=ACOT(A1)'], - ['', '=ACOT(A2)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-0.785398163397448) - expect(engine.getCellValue(adr('B2'))).toBeCloseTo(1.5707963267949, 10) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=ACOT(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-acoth.spec.ts b/test/unit/interpreter/function-acoth.spec.ts deleted file mode 100644 index 1145bfd318..0000000000 --- a/test/unit/interpreter/function-acoth.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ACOTH', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=ACOTH(2)']], {smartRounding: false}) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.5493061443340548) - }) - - it('error for 1', () => { - const engine = HyperFormula.buildFromArray([['=ACOTH(1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('error for -1', () => { - const engine = HyperFormula.buildFromArray([['=ACOTH(-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=ACOTH("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=ACOTH()', '=ACOTH(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="2"', '=ACOTH(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.5493061443340548) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=ACOTH(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-address.spec.ts b/test/unit/interpreter/function-address.spec.ts deleted file mode 100644 index 3391ba5c62..0000000000 --- a/test/unit/interpreter/function-address.spec.ts +++ /dev/null @@ -1,299 +0,0 @@ -import {HyperFormula, ErrorType} from '../../../src' -import {adr, detailedError} from '../testUtils' -import {ErrorMessage} from '../../../src/error-message' - -describe('ADDRESS', () => { - it('with row and col', () => { - const engine = HyperFormula.buildFromArray([ - ['=ADDRESS(1,1)'], - ['=ADDRESS(77,300)'], - ['=ADDRESS(ROW(),300)'], - ['=ADDRESS(45,COLUMN())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('$A$1') - expect(engine.getCellValue(adr('A2'))).toEqual('$KN$77') - expect(engine.getCellValue(adr('A3'))).toEqual('$KN$3') - expect(engine.getCellValue(adr('A4'))).toEqual('$A$45') - }) - - it('with row, col, and abs (A1 Notation)', () => { - const engine = HyperFormula.buildFromArray([ - ['=ADDRESS(1,1,1)'], - ['=ADDRESS(1,1,2)'], - ['=ADDRESS(1,1,3)'], - ['=ADDRESS(1,1,4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('$A$1') - expect(engine.getCellValue(adr('A2'))).toEqual('A$1') - expect(engine.getCellValue(adr('A3'))).toEqual('$A1') - expect(engine.getCellValue(adr('A4'))).toEqual('A1') - }) - - it('with row, col, and abs (R1C1 Notation)', () => { - const engine = HyperFormula.buildFromArray([ - ['=ADDRESS(1,1,1,FALSE())'], - ['=ADDRESS(1,1,2,FALSE())'], - ['=ADDRESS(1,1,3,FALSE())'], - ['=ADDRESS(1,1,4,FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('R1C1') - expect(engine.getCellValue(adr('A2'))).toEqual('R1C[1]') - expect(engine.getCellValue(adr('A3'))).toEqual('R[1]C1') - expect(engine.getCellValue(adr('A4'))).toEqual('R[1]C[1]') - }) - - it('with row, col, abs, and sheetName (A1 Notation)', () => { - const engine = HyperFormula.buildFromArray([ - ['=ADDRESS(1,1,1, TRUE(), "Sheet1")'], - ['=ADDRESS(1,1,2, TRUE(), "Sheet2")'], - ['=ADDRESS(1,1,3, TRUE(), "Sheet3")'], - ['=ADDRESS(1,1,4, TRUE(), "Sheet4")'], - ['=ADDRESS(1,1,4, TRUE(), "")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('Sheet1!$A$1') - expect(engine.getCellValue(adr('A2'))).toEqual('Sheet2!A$1') - expect(engine.getCellValue(adr('A3'))).toEqual('Sheet3!$A1') - expect(engine.getCellValue(adr('A4'))).toEqual('Sheet4!A1') - expect(engine.getCellValue(adr('A5'))).toEqual('!A1') - }) - - it('with row, col, abs, and sheetName (R1C1 Notation)', () => { - const engine = HyperFormula.buildFromArray([ - ['=ADDRESS(1,1,1, FALSE(), "Sheet1")'], - ['=ADDRESS(1,1,2, FALSE(), "Sheet2")'], - ['=ADDRESS(1,1,3, FALSE(), "Sheet3")'], - ['=ADDRESS(1,1,4, FALSE(), "Sheet4")'], - ['=ADDRESS(1,1,4, FALSE(), "")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('Sheet1!R1C1') - expect(engine.getCellValue(adr('A2'))).toEqual('Sheet2!R1C[1]') - expect(engine.getCellValue(adr('A3'))).toEqual('Sheet3!R[1]C1') - expect(engine.getCellValue(adr('A4'))).toEqual('Sheet4!R[1]C[1]') - expect(engine.getCellValue(adr('A5'))).toEqual('!R[1]C[1]') - }) - - it('invalid arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ADDRESS()'], - ['=ADDRESS(1)'], - ['=ADDRESS(0,0)'], - ['=ADDRESS("row1","row2")'], - ['=ADDRESS(1,1,0)'], - ['=ADDRESS(1,1,5)'], - ['=ADDRESS(1,1,1, true, "")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A7'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.NamedExpressionName('true'))) - }) -}) - -describe('ADDRESS - Compatability Checks', () => { - it('row negative - col negative', () => { - const engine = HyperFormula.buildFromArray([ - ['=ADDRESS(-1, -1, 1, FALSE())'], - ['=ADDRESS(-1, -1, 1, TRUE())'], - ['=ADDRESS(-1, -1, 2, FALSE())'], - ['=ADDRESS(-1, -1, 2, TRUE())'], - ['=ADDRESS(-1, -1, 3, FALSE())'], - ['=ADDRESS(-1, -1, 3, TRUE())'], - ['=ADDRESS(-1, -1, 4, FALSE())'], - ['=ADDRESS(-1, -1, 4, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A7'))).toEqual('R[-1]C[-1]') - expect(engine.getCellValue(adr('A8'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - }) - - it('row negative - col zero', () => { - const engine = HyperFormula.buildFromArray([ - ['=ADDRESS(-1, 0, 1, FALSE())'], - ['=ADDRESS(-1, 0, 1, TRUE())'], - ['=ADDRESS(-1, 0, 2, FALSE())'], - ['=ADDRESS(-1, 0, 2, TRUE())'], - ['=ADDRESS(-1, 0, 3, FALSE())'], - ['=ADDRESS(-1, 0, 3, TRUE())'], - ['=ADDRESS(-1, 0, 4, FALSE())'], - ['=ADDRESS(-1, 0, 4, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A7'))).toEqual('R[-1]C') - expect(engine.getCellValue(adr('A8'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - }) - - it('row negative - col one', () => { - const engine = HyperFormula.buildFromArray([ - ['=ADDRESS(-1, 1, 1, FALSE())'], - ['=ADDRESS(-1, 1, 1, TRUE())'], - ['=ADDRESS(-1, 1, 2, FALSE())'], - ['=ADDRESS(-1, 1, 2, TRUE())'], - ['=ADDRESS(-1, 1, 3, FALSE())'], - ['=ADDRESS(-1, 1, 3, TRUE())'], - ['=ADDRESS(-1, 1, 4, FALSE())'], - ['=ADDRESS(-1, 1, 4, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A5'))).toEqual('R[-1]C1') - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A7'))).toEqual('R[-1]C[1]') - expect(engine.getCellValue(adr('A8'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - }) - - it('row zero - col negative', () => { - const engine = HyperFormula.buildFromArray([ - ['=ADDRESS(0, -1, 1, FALSE())'], - ['=ADDRESS(0, -1, 1, TRUE())'], - ['=ADDRESS(0, -1, 2, FALSE())'], - ['=ADDRESS(0, -1, 2, TRUE())'], - ['=ADDRESS(0, -1, 3, FALSE())'], - ['=ADDRESS(0, -1, 3, TRUE())'], - ['=ADDRESS(0, -1, 4, FALSE())'], - ['=ADDRESS(0, -1, 4, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A7'))).toEqual('RC[-1]') - expect(engine.getCellValue(adr('A8'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - }) - - it('row zero - col zero', () => { - const engine = HyperFormula.buildFromArray([ - ['=ADDRESS(0, 0, 1, FALSE())'], - ['=ADDRESS(0, 0, 1, TRUE())'], - ['=ADDRESS(0, 0, 2, FALSE())'], - ['=ADDRESS(0, 0, 2, TRUE())'], - ['=ADDRESS(0, 0, 3, FALSE())'], - ['=ADDRESS(0, 0, 3, TRUE())'], - ['=ADDRESS(0, 0, 4, FALSE())'], - ['=ADDRESS(0, 0, 4, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A7'))).toEqual('RC') - expect(engine.getCellValue(adr('A8'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - }) - - it('row zero - col one', () => { - const engine = HyperFormula.buildFromArray([ - ['=ADDRESS(0, 1, 1, FALSE())'], - ['=ADDRESS(0, 1, 1, TRUE())'], - ['=ADDRESS(0, 1, 2, FALSE())'], - ['=ADDRESS(0, 1, 2, TRUE())'], - ['=ADDRESS(0, 1, 3, FALSE())'], - ['=ADDRESS(0, 1, 3, TRUE())'], - ['=ADDRESS(0, 1, 4, FALSE())'], - ['=ADDRESS(0, 1, 4, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A5'))).toEqual('RC1') - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A7'))).toEqual('RC[1]') - expect(engine.getCellValue(adr('A8'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - }) - - it('row one - col negative', () => { - const engine = HyperFormula.buildFromArray([ - ['=ADDRESS(1, -1, 1, FALSE())'], - ['=ADDRESS(1, -1, 1, TRUE())'], - ['=ADDRESS(1, -1, 2, FALSE())'], - ['=ADDRESS(1, -1, 2, TRUE())'], - ['=ADDRESS(1, -1, 3, FALSE())'], - ['=ADDRESS(1, -1, 3, TRUE())'], - ['=ADDRESS(1, -1, 4, FALSE())'], - ['=ADDRESS(1, -1, 4, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A3'))).toEqual('R1C[-1]') - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A7'))).toEqual('R[1]C[-1]') - expect(engine.getCellValue(adr('A8'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - }) - - it('row one - col zero', () => { - const engine = HyperFormula.buildFromArray([ - ['=ADDRESS(1, 0, 1, FALSE())'], - ['=ADDRESS(1, 0, 1, TRUE())'], - ['=ADDRESS(1, 0, 2, FALSE())'], - ['=ADDRESS(1, 0, 2, TRUE())'], - ['=ADDRESS(1, 0, 3, FALSE())'], - ['=ADDRESS(1, 0, 3, TRUE())'], - ['=ADDRESS(1, 0, 4, FALSE())'], - ['=ADDRESS(1, 0, 4, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A3'))).toEqual('R1C') - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A7'))).toEqual('R[1]C') - expect(engine.getCellValue(adr('A8'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - }) - - it('row one - col one', () => { - const engine = HyperFormula.buildFromArray([ - ['=ADDRESS(1, 1, 1, FALSE())'], - ['=ADDRESS(1, 1, 1, TRUE())'], - ['=ADDRESS(1, 1, 2, FALSE())'], - ['=ADDRESS(1, 1, 2, TRUE())'], - ['=ADDRESS(1, 1, 3, FALSE())'], - ['=ADDRESS(1, 1, 3, TRUE())'], - ['=ADDRESS(1, 1, 4, FALSE())'], - ['=ADDRESS(1, 1, 4, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('R1C1') - expect(engine.getCellValue(adr('A2'))).toEqual('$A$1') - expect(engine.getCellValue(adr('A3'))).toEqual('R1C[1]') - expect(engine.getCellValue(adr('A4'))).toEqual('A$1') - expect(engine.getCellValue(adr('A5'))).toEqual('R[1]C1') - expect(engine.getCellValue(adr('A6'))).toEqual('$A1') - expect(engine.getCellValue(adr('A7'))).toEqual('R[1]C[1]') - expect(engine.getCellValue(adr('A8'))).toEqual('A1') - }) -}) diff --git a/test/unit/interpreter/function-and.spec.ts b/test/unit/interpreter/function-and.spec.ts deleted file mode 100644 index 2d41dded20..0000000000 --- a/test/unit/interpreter/function-and.spec.ts +++ /dev/null @@ -1,95 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function AND', () => { - it('usage', () => { - const engine = HyperFormula.buildFromArray([ - ['=AND(TRUE(), TRUE())', '=AND(TRUE(), FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - }) - - it('with numerical arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=AND(1)', '=AND(0)', '=AND(1, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('C1'))).toBe(true) - }) - - it('use coercion #1', () => { - const engine = HyperFormula.buildFromArray([ - ['=AND("TRUE", 1)'], - ['=AND("foo", TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('use coercion #2', () => { - const engine = HyperFormula.buildFromArray([ - ['=AND(A4:B4)'], - ['=AND(C4:D4)'], - ['=AND(C4:D4, "foo")'], - ['TRUE', 1, 'foo', '=TRUE()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('A2'))).toBe(true) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('if error in range found, returns first one in row-by-row order', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=4/0'], - ['=FOOBAR()', '1'], - ['=AND(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('works with ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1'], - ['1', '1'], - ['=AND(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(true) - }) - - it('takes at least one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=AND()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('is computed eagerly', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '=4/0'], - ['1', '1'], - ['=AND(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('when called with a range, ignores strings other than "true", "false" and ""', () => { - const engine = HyperFormula.buildFromArray([ - ['foo', '=TRUE()', '=AND(A1:B1)'], - ['foo', '=FALSE()', '=AND(A2:B2)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(true) - expect(engine.getCellValue(adr('C2'))).toEqual(false) - }) -}) diff --git a/test/unit/interpreter/function-arabic.spec.ts b/test/unit/interpreter/function-arabic.spec.ts deleted file mode 100644 index 1750abe4c4..0000000000 --- a/test/unit/interpreter/function-arabic.spec.ts +++ /dev/null @@ -1,102 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ARABIC', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ARABIC()', '=ARABIC(1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should properly sanitize input', () => { - const engine = HyperFormula.buildFromArray([ - ['=ARABIC(" XD ")'], - ['=ARABIC("xd")'], - ['=ARABIC(" xD ")'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(490) - expect(engine.getCellValue(adr('A2'))).toEqual(490) - expect(engine.getCellValue(adr('A3'))).toEqual(490) - }) - - it('should detect incorrect numerals', () => { - const engine = HyperFormula.buildFromArray([ - ['=ARABIC("IMM")'], - ['=ARABIC("MMMM")'], - ['=ARABIC("IXC")'], - ['=ARABIC("--I")'], - ['=ARABIC("-")'], - ['=ARABIC("Ma")'], - ['=ARABIC("M M")'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.InvalidRoman)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.InvalidRoman)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.InvalidRoman)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.InvalidRoman)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.InvalidRoman)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.InvalidRoman)) - expect(engine.getCellValue(adr('A7'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.InvalidRoman)) - }) - - it('works for border cases', () => { - const engine = HyperFormula.buildFromArray([ - ['=ARABIC("MMMIMIDCCCICILXXXIXIVIII")'], - ['=ARABIC("-I")'], - ['=ARABIC(" ")'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(4992) - expect(engine.getCellValue(adr('A2'))).toEqual(-1) - expect(engine.getCellValue(adr('A3'))).toEqual(0) - }) - - it('should output correct value for roman numerals from mode 0', () => { - const [input, output] = inputOutput(0) - const engine = HyperFormula.buildFromArray([input]) - expect(engine.getSheetValues(0)).toEqual([output]) - }) - - it('should output correct value for roman numerals from mode 1', () => { - const [input, output] = inputOutput(1) - const engine = HyperFormula.buildFromArray([input]) - expect(engine.getSheetValues(0)).toEqual([output]) - }) - - it('should output correct value for roman numerals from mode 2', () => { - const [input, output] = inputOutput(2) - const engine = HyperFormula.buildFromArray([input]) - expect(engine.getSheetValues(0)).toEqual([output]) - }) - - it('should output correct value for roman numerals from mode 3', () => { - const [input, output] = inputOutput(0) - const engine = HyperFormula.buildFromArray([input]) - expect(engine.getSheetValues(0)).toEqual([output]) - }) - - it('should output correct value for roman numerals from mode 4', () => { - const [input, output] = inputOutput(4) - const engine = HyperFormula.buildFromArray([input]) - expect(engine.getSheetValues(0)).toEqual([output]) - }) -}) - -function inputOutput(mode: number) { - const arr = [mode0, mode1, mode2, mode3, mode4][mode] - const input = [] - const output = [] - for (let i = 0; i < arr.length; i++) { - input.push(`=ARABIC("${arr[i]}")`) - output.push(i + 1) - } - return [input, output] -} - -const mode0 = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX', 'XXI', 'XXII', 'XXIII', 'XXIV', 'XXV', 'XXVI', 'XXVII', 'XXVIII', 'XXIX', 'XXX', 'XXXI', 'XXXII', 'XXXIII', 'XXXIV', 'XXXV', 'XXXVI', 'XXXVII', 'XXXVIII', 'XXXIX', 'XL', 'XLI', 'XLII', 'XLIII', 'XLIV', 'XLV', 'XLVI', 'XLVII', 'XLVIII', 'XLIX', 'L', 'LI', 'LII', 'LIII', 'LIV', 'LV', 'LVI', 'LVII', 'LVIII', 'LIX', 'LX', 'LXI', 'LXII', 'LXIII', 'LXIV', 'LXV', 'LXVI', 'LXVII', 'LXVIII', 'LXIX', 'LXX', 'LXXI', 'LXXII', 'LXXIII', 'LXXIV', 'LXXV', 'LXXVI', 'LXXVII', 'LXXVIII', 'LXXIX', 'LXXX', 'LXXXI', 'LXXXII', 'LXXXIII', 'LXXXIV', 'LXXXV', 'LXXXVI', 'LXXXVII', 'LXXXVIII', 'LXXXIX', 'XC', 'XCI', 'XCII', 'XCIII', 'XCIV', 'XCV', 'XCVI', 'XCVII', 'XCVIII', 'XCIX', 'C', 'CI', 'CII', 'CIII', 'CIV', 'CV', 'CVI', 'CVII', 'CVIII', 'CIX', 'CX', 'CXI', 'CXII', 'CXIII', 'CXIV', 'CXV', 'CXVI', 'CXVII', 'CXVIII', 'CXIX', 'CXX', 'CXXI', 'CXXII', 'CXXIII', 'CXXIV', 'CXXV', 'CXXVI', 'CXXVII', 'CXXVIII', 'CXXIX', 'CXXX', 'CXXXI', 'CXXXII', 'CXXXIII', 'CXXXIV', 'CXXXV', 'CXXXVI', 'CXXXVII', 'CXXXVIII', 'CXXXIX', 'CXL', 'CXLI', 'CXLII', 'CXLIII', 'CXLIV', 'CXLV', 'CXLVI', 'CXLVII', 'CXLVIII', 'CXLIX', 'CL', 'CLI', 'CLII', 'CLIII', 'CLIV', 'CLV', 'CLVI', 'CLVII', 'CLVIII', 'CLIX', 'CLX', 'CLXI', 'CLXII', 'CLXIII', 'CLXIV', 'CLXV', 'CLXVI', 'CLXVII', 'CLXVIII', 'CLXIX', 'CLXX', 'CLXXI', 'CLXXII', 'CLXXIII', 'CLXXIV', 'CLXXV', 'CLXXVI', 'CLXXVII', 'CLXXVIII', 'CLXXIX', 'CLXXX', 'CLXXXI', 'CLXXXII', 'CLXXXIII', 'CLXXXIV', 'CLXXXV', 'CLXXXVI', 'CLXXXVII', 'CLXXXVIII', 'CLXXXIX', 'CXC', 'CXCI', 'CXCII', 'CXCIII', 'CXCIV', 'CXCV', 'CXCVI', 'CXCVII', 'CXCVIII', 'CXCIX', 'CC', 'CCI', 'CCII', 'CCIII', 'CCIV', 'CCV', 'CCVI', 'CCVII', 'CCVIII', 'CCIX', 'CCX', 'CCXI', 'CCXII', 'CCXIII', 'CCXIV', 'CCXV', 'CCXVI', 'CCXVII', 'CCXVIII', 'CCXIX', 'CCXX', 'CCXXI', 'CCXXII', 'CCXXIII', 'CCXXIV', 'CCXXV', 'CCXXVI', 'CCXXVII', 'CCXXVIII', 'CCXXIX', 'CCXXX', 'CCXXXI', 'CCXXXII', 'CCXXXIII', 'CCXXXIV', 'CCXXXV', 'CCXXXVI', 'CCXXXVII', 'CCXXXVIII', 'CCXXXIX', 'CCXL', 'CCXLI', 'CCXLII', 'CCXLIII', 'CCXLIV', 'CCXLV', 'CCXLVI', 'CCXLVII', 'CCXLVIII', 'CCXLIX', 'CCL', 'CCLI', 'CCLII', 'CCLIII', 'CCLIV', 'CCLV', 'CCLVI', 'CCLVII', 'CCLVIII', 'CCLIX', 'CCLX', 'CCLXI', 'CCLXII', 'CCLXIII', 'CCLXIV', 'CCLXV', 'CCLXVI', 'CCLXVII', 'CCLXVIII', 'CCLXIX', 'CCLXX', 'CCLXXI', 'CCLXXII', 'CCLXXIII', 'CCLXXIV', 'CCLXXV', 'CCLXXVI', 'CCLXXVII', 'CCLXXVIII', 'CCLXXIX', 'CCLXXX', 'CCLXXXI', 'CCLXXXII', 'CCLXXXIII', 'CCLXXXIV', 'CCLXXXV', 'CCLXXXVI', 'CCLXXXVII', 'CCLXXXVIII', 'CCLXXXIX', 'CCXC', 'CCXCI', 'CCXCII', 'CCXCIII', 'CCXCIV', 'CCXCV', 'CCXCVI', 'CCXCVII', 'CCXCVIII', 'CCXCIX', 'CCC', 'CCCI', 'CCCII', 'CCCIII', 'CCCIV', 'CCCV', 'CCCVI', 'CCCVII', 'CCCVIII', 'CCCIX', 'CCCX', 'CCCXI', 'CCCXII', 'CCCXIII', 'CCCXIV', 'CCCXV', 'CCCXVI', 'CCCXVII', 'CCCXVIII', 'CCCXIX', 'CCCXX', 'CCCXXI', 'CCCXXII', 'CCCXXIII', 'CCCXXIV', 'CCCXXV', 'CCCXXVI', 'CCCXXVII', 'CCCXXVIII', 'CCCXXIX', 'CCCXXX', 'CCCXXXI', 'CCCXXXII', 'CCCXXXIII', 'CCCXXXIV', 'CCCXXXV', 'CCCXXXVI', 'CCCXXXVII', 'CCCXXXVIII', 'CCCXXXIX', 'CCCXL', 'CCCXLI', 'CCCXLII', 'CCCXLIII', 'CCCXLIV', 'CCCXLV', 'CCCXLVI', 'CCCXLVII', 'CCCXLVIII', 'CCCXLIX', 'CCCL', 'CCCLI', 'CCCLII', 'CCCLIII', 'CCCLIV', 'CCCLV', 'CCCLVI', 'CCCLVII', 'CCCLVIII', 'CCCLIX', 'CCCLX', 'CCCLXI', 'CCCLXII', 'CCCLXIII', 'CCCLXIV', 'CCCLXV', 'CCCLXVI', 'CCCLXVII', 'CCCLXVIII', 'CCCLXIX', 'CCCLXX', 'CCCLXXI', 'CCCLXXII', 'CCCLXXIII', 'CCCLXXIV', 'CCCLXXV', 'CCCLXXVI', 'CCCLXXVII', 'CCCLXXVIII', 'CCCLXXIX', 'CCCLXXX', 'CCCLXXXI', 'CCCLXXXII', 'CCCLXXXIII', 'CCCLXXXIV', 'CCCLXXXV', 'CCCLXXXVI', 'CCCLXXXVII', 'CCCLXXXVIII', 'CCCLXXXIX', 'CCCXC', 'CCCXCI', 'CCCXCII', 'CCCXCIII', 'CCCXCIV', 'CCCXCV', 'CCCXCVI', 'CCCXCVII', 'CCCXCVIII', 'CCCXCIX', 'CD', 'CDI', 'CDII', 'CDIII', 'CDIV', 'CDV', 'CDVI', 'CDVII', 'CDVIII', 'CDIX', 'CDX', 'CDXI', 'CDXII', 'CDXIII', 'CDXIV', 'CDXV', 'CDXVI', 'CDXVII', 'CDXVIII', 'CDXIX', 'CDXX', 'CDXXI', 'CDXXII', 'CDXXIII', 'CDXXIV', 'CDXXV', 'CDXXVI', 'CDXXVII', 'CDXXVIII', 'CDXXIX', 'CDXXX', 'CDXXXI', 'CDXXXII', 'CDXXXIII', 'CDXXXIV', 'CDXXXV', 'CDXXXVI', 'CDXXXVII', 'CDXXXVIII', 'CDXXXIX', 'CDXL', 'CDXLI', 'CDXLII', 'CDXLIII', 'CDXLIV', 'CDXLV', 'CDXLVI', 'CDXLVII', 'CDXLVIII', 'CDXLIX', 'CDL', 'CDLI', 'CDLII', 'CDLIII', 'CDLIV', 'CDLV', 'CDLVI', 'CDLVII', 'CDLVIII', 'CDLIX', 'CDLX', 'CDLXI', 'CDLXII', 'CDLXIII', 'CDLXIV', 'CDLXV', 'CDLXVI', 'CDLXVII', 'CDLXVIII', 'CDLXIX', 'CDLXX', 'CDLXXI', 'CDLXXII', 'CDLXXIII', 'CDLXXIV', 'CDLXXV', 'CDLXXVI', 'CDLXXVII', 'CDLXXVIII', 'CDLXXIX', 'CDLXXX', 'CDLXXXI', 'CDLXXXII', 'CDLXXXIII', 'CDLXXXIV', 'CDLXXXV', 'CDLXXXVI', 'CDLXXXVII', 'CDLXXXVIII', 'CDLXXXIX', 'CDXC', 'CDXCI', 'CDXCII', 'CDXCIII', 'CDXCIV', 'CDXCV', 'CDXCVI', 'CDXCVII', 'CDXCVIII', 'CDXCIX', 'D', 'DI', 'DII', 'DIII', 'DIV', 'DV', 'DVI', 'DVII', 'DVIII', 'DIX', 'DX', 'DXI', 'DXII', 'DXIII', 'DXIV', 'DXV', 'DXVI', 'DXVII', 'DXVIII', 'DXIX', 'DXX', 'DXXI', 'DXXII', 'DXXIII', 'DXXIV', 'DXXV', 'DXXVI', 'DXXVII', 'DXXVIII', 'DXXIX', 'DXXX', 'DXXXI', 'DXXXII', 'DXXXIII', 'DXXXIV', 'DXXXV', 'DXXXVI', 'DXXXVII', 'DXXXVIII', 'DXXXIX', 'DXL', 'DXLI', 'DXLII', 'DXLIII', 'DXLIV', 'DXLV', 'DXLVI', 'DXLVII', 'DXLVIII', 'DXLIX', 'DL', 'DLI', 'DLII', 'DLIII', 'DLIV', 'DLV', 'DLVI', 'DLVII', 'DLVIII', 'DLIX', 'DLX', 'DLXI', 'DLXII', 'DLXIII', 'DLXIV', 'DLXV', 'DLXVI', 'DLXVII', 'DLXVIII', 'DLXIX', 'DLXX', 'DLXXI', 'DLXXII', 'DLXXIII', 'DLXXIV', 'DLXXV', 'DLXXVI', 'DLXXVII', 'DLXXVIII', 'DLXXIX', 'DLXXX', 'DLXXXI', 'DLXXXII', 'DLXXXIII', 'DLXXXIV', 'DLXXXV', 'DLXXXVI', 'DLXXXVII', 'DLXXXVIII', 'DLXXXIX', 'DXC', 'DXCI', 'DXCII', 'DXCIII', 'DXCIV', 'DXCV', 'DXCVI', 'DXCVII', 'DXCVIII', 'DXCIX', 'DC', 'DCI', 'DCII', 'DCIII', 'DCIV', 'DCV', 'DCVI', 'DCVII', 'DCVIII', 'DCIX', 'DCX', 'DCXI', 'DCXII', 'DCXIII', 'DCXIV', 'DCXV', 'DCXVI', 'DCXVII', 'DCXVIII', 'DCXIX', 'DCXX', 'DCXXI', 'DCXXII', 'DCXXIII', 'DCXXIV', 'DCXXV', 'DCXXVI', 'DCXXVII', 'DCXXVIII', 'DCXXIX', 'DCXXX', 'DCXXXI', 'DCXXXII', 'DCXXXIII', 'DCXXXIV', 'DCXXXV', 'DCXXXVI', 'DCXXXVII', 'DCXXXVIII', 'DCXXXIX', 'DCXL', 'DCXLI', 'DCXLII', 'DCXLIII', 'DCXLIV', 'DCXLV', 'DCXLVI', 'DCXLVII', 'DCXLVIII', 'DCXLIX', 'DCL', 'DCLI', 'DCLII', 'DCLIII', 'DCLIV', 'DCLV', 'DCLVI', 'DCLVII', 'DCLVIII', 'DCLIX', 'DCLX', 'DCLXI', 'DCLXII', 'DCLXIII', 'DCLXIV', 'DCLXV', 'DCLXVI', 'DCLXVII', 'DCLXVIII', 'DCLXIX', 'DCLXX', 'DCLXXI', 'DCLXXII', 'DCLXXIII', 'DCLXXIV', 'DCLXXV', 'DCLXXVI', 'DCLXXVII', 'DCLXXVIII', 'DCLXXIX', 'DCLXXX', 'DCLXXXI', 'DCLXXXII', 'DCLXXXIII', 'DCLXXXIV', 'DCLXXXV', 'DCLXXXVI', 'DCLXXXVII', 'DCLXXXVIII', 'DCLXXXIX', 'DCXC', 'DCXCI', 'DCXCII', 'DCXCIII', 'DCXCIV', 'DCXCV', 'DCXCVI', 'DCXCVII', 'DCXCVIII', 'DCXCIX', 'DCC', 'DCCI', 'DCCII', 'DCCIII', 'DCCIV', 'DCCV', 'DCCVI', 'DCCVII', 'DCCVIII', 'DCCIX', 'DCCX', 'DCCXI', 'DCCXII', 'DCCXIII', 'DCCXIV', 'DCCXV', 'DCCXVI', 'DCCXVII', 'DCCXVIII', 'DCCXIX', 'DCCXX', 'DCCXXI', 'DCCXXII', 'DCCXXIII', 'DCCXXIV', 'DCCXXV', 'DCCXXVI', 'DCCXXVII', 'DCCXXVIII', 'DCCXXIX', 'DCCXXX', 'DCCXXXI', 'DCCXXXII', 'DCCXXXIII', 'DCCXXXIV', 'DCCXXXV', 'DCCXXXVI', 'DCCXXXVII', 'DCCXXXVIII', 'DCCXXXIX', 'DCCXL', 'DCCXLI', 'DCCXLII', 'DCCXLIII', 'DCCXLIV', 'DCCXLV', 'DCCXLVI', 'DCCXLVII', 'DCCXLVIII', 'DCCXLIX', 'DCCL', 'DCCLI', 'DCCLII', 'DCCLIII', 'DCCLIV', 'DCCLV', 'DCCLVI', 'DCCLVII', 'DCCLVIII', 'DCCLIX', 'DCCLX', 'DCCLXI', 'DCCLXII', 'DCCLXIII', 'DCCLXIV', 'DCCLXV', 'DCCLXVI', 'DCCLXVII', 'DCCLXVIII', 'DCCLXIX', 'DCCLXX', 'DCCLXXI', 'DCCLXXII', 'DCCLXXIII', 'DCCLXXIV', 'DCCLXXV', 'DCCLXXVI', 'DCCLXXVII', 'DCCLXXVIII', 'DCCLXXIX', 'DCCLXXX', 'DCCLXXXI', 'DCCLXXXII', 'DCCLXXXIII', 'DCCLXXXIV', 'DCCLXXXV', 'DCCLXXXVI', 'DCCLXXXVII', 'DCCLXXXVIII', 'DCCLXXXIX', 'DCCXC', 'DCCXCI', 'DCCXCII', 'DCCXCIII', 'DCCXCIV', 'DCCXCV', 'DCCXCVI', 'DCCXCVII', 'DCCXCVIII', 'DCCXCIX', 'DCCC', 'DCCCI', 'DCCCII', 'DCCCIII', 'DCCCIV', 'DCCCV', 'DCCCVI', 'DCCCVII', 'DCCCVIII', 'DCCCIX', 'DCCCX', 'DCCCXI', 'DCCCXII', 'DCCCXIII', 'DCCCXIV', 'DCCCXV', 'DCCCXVI', 'DCCCXVII', 'DCCCXVIII', 'DCCCXIX', 'DCCCXX', 'DCCCXXI', 'DCCCXXII', 'DCCCXXIII', 'DCCCXXIV', 'DCCCXXV', 'DCCCXXVI', 'DCCCXXVII', 'DCCCXXVIII', 'DCCCXXIX', 'DCCCXXX', 'DCCCXXXI', 'DCCCXXXII', 'DCCCXXXIII', 'DCCCXXXIV', 'DCCCXXXV', 'DCCCXXXVI', 'DCCCXXXVII', 'DCCCXXXVIII', 'DCCCXXXIX', 'DCCCXL', 'DCCCXLI', 'DCCCXLII', 'DCCCXLIII', 'DCCCXLIV', 'DCCCXLV', 'DCCCXLVI', 'DCCCXLVII', 'DCCCXLVIII', 'DCCCXLIX', 'DCCCL', 'DCCCLI', 'DCCCLII', 'DCCCLIII', 'DCCCLIV', 'DCCCLV', 'DCCCLVI', 'DCCCLVII', 'DCCCLVIII', 'DCCCLIX', 'DCCCLX', 'DCCCLXI', 'DCCCLXII', 'DCCCLXIII', 'DCCCLXIV', 'DCCCLXV', 'DCCCLXVI', 'DCCCLXVII', 'DCCCLXVIII', 'DCCCLXIX', 'DCCCLXX', 'DCCCLXXI', 'DCCCLXXII', 'DCCCLXXIII', 'DCCCLXXIV', 'DCCCLXXV', 'DCCCLXXVI', 'DCCCLXXVII', 'DCCCLXXVIII', 'DCCCLXXIX', 'DCCCLXXX', 'DCCCLXXXI', 'DCCCLXXXII', 'DCCCLXXXIII', 'DCCCLXXXIV', 'DCCCLXXXV', 'DCCCLXXXVI', 'DCCCLXXXVII', 'DCCCLXXXVIII', 'DCCCLXXXIX', 'DCCCXC', 'DCCCXCI', 'DCCCXCII', 'DCCCXCIII', 'DCCCXCIV', 'DCCCXCV', 'DCCCXCVI', 'DCCCXCVII', 'DCCCXCVIII', 'DCCCXCIX', 'CM', 'CMI', 'CMII', 'CMIII', 'CMIV', 'CMV', 'CMVI', 'CMVII', 'CMVIII', 'CMIX', 'CMX', 'CMXI', 'CMXII', 'CMXIII', 'CMXIV', 'CMXV', 'CMXVI', 'CMXVII', 'CMXVIII', 'CMXIX', 'CMXX', 'CMXXI', 'CMXXII', 'CMXXIII', 'CMXXIV', 'CMXXV', 'CMXXVI', 'CMXXVII', 'CMXXVIII', 'CMXXIX', 'CMXXX', 'CMXXXI', 'CMXXXII', 'CMXXXIII', 'CMXXXIV', 'CMXXXV', 'CMXXXVI', 'CMXXXVII', 'CMXXXVIII', 'CMXXXIX', 'CMXL', 'CMXLI', 'CMXLII', 'CMXLIII', 'CMXLIV', 'CMXLV', 'CMXLVI', 'CMXLVII', 'CMXLVIII', 'CMXLIX', 'CML', 'CMLI', 'CMLII', 'CMLIII', 'CMLIV', 'CMLV', 'CMLVI', 'CMLVII', 'CMLVIII', 'CMLIX', 'CMLX', 'CMLXI', 'CMLXII', 'CMLXIII', 'CMLXIV', 'CMLXV', 'CMLXVI', 'CMLXVII', 'CMLXVIII', 'CMLXIX', 'CMLXX', 'CMLXXI', 'CMLXXII', 'CMLXXIII', 'CMLXXIV', 'CMLXXV', 'CMLXXVI', 'CMLXXVII', 'CMLXXVIII', 'CMLXXIX', 'CMLXXX', 'CMLXXXI', 'CMLXXXII', 'CMLXXXIII', 'CMLXXXIV', 'CMLXXXV', 'CMLXXXVI', 'CMLXXXVII', 'CMLXXXVIII', 'CMLXXXIX', 'CMXC', 'CMXCI', 'CMXCII', 'CMXCIII', 'CMXCIV', 'CMXCV', 'CMXCVI', 'CMXCVII', 'CMXCVIII', 'CMXCIX', 'M', 'MI', 'MII', 'MIII', 'MIV', 'MV', 'MVI', 'MVII', 'MVIII', 'MIX', 'MX', 'MXI', 'MXII', 'MXIII', 'MXIV', 'MXV', 'MXVI', 'MXVII', 'MXVIII', 'MXIX', 'MXX', 'MXXI', 'MXXII', 'MXXIII', 'MXXIV', 'MXXV', 'MXXVI', 'MXXVII', 'MXXVIII', 'MXXIX', 'MXXX', 'MXXXI', 'MXXXII', 'MXXXIII', 'MXXXIV', 'MXXXV', 'MXXXVI', 'MXXXVII', 'MXXXVIII', 'MXXXIX', 'MXL', 'MXLI', 'MXLII', 'MXLIII', 'MXLIV', 'MXLV', 'MXLVI', 'MXLVII', 'MXLVIII', 'MXLIX', 'ML', 'MLI', 'MLII', 'MLIII', 'MLIV', 'MLV', 'MLVI', 'MLVII', 'MLVIII', 'MLIX', 'MLX', 'MLXI', 'MLXII', 'MLXIII', 'MLXIV', 'MLXV', 'MLXVI', 'MLXVII', 'MLXVIII', 'MLXIX', 'MLXX', 'MLXXI', 'MLXXII', 'MLXXIII', 'MLXXIV', 'MLXXV', 'MLXXVI', 'MLXXVII', 'MLXXVIII', 'MLXXIX', 'MLXXX', 'MLXXXI', 'MLXXXII', 'MLXXXIII', 'MLXXXIV', 'MLXXXV', 'MLXXXVI', 'MLXXXVII', 'MLXXXVIII', 'MLXXXIX', 'MXC', 'MXCI', 'MXCII', 'MXCIII', 'MXCIV', 'MXCV', 'MXCVI', 'MXCVII', 'MXCVIII', 'MXCIX', 'MC', 'MCI', 'MCII', 'MCIII', 'MCIV', 'MCV', 'MCVI', 'MCVII', 'MCVIII', 'MCIX', 'MCX', 'MCXI', 'MCXII', 'MCXIII', 'MCXIV', 'MCXV', 'MCXVI', 'MCXVII', 'MCXVIII', 'MCXIX', 'MCXX', 'MCXXI', 'MCXXII', 'MCXXIII', 'MCXXIV', 'MCXXV', 'MCXXVI', 'MCXXVII', 'MCXXVIII', 'MCXXIX', 'MCXXX', 'MCXXXI', 'MCXXXII', 'MCXXXIII', 'MCXXXIV', 'MCXXXV', 'MCXXXVI', 'MCXXXVII', 'MCXXXVIII', 'MCXXXIX', 'MCXL', 'MCXLI', 'MCXLII', 'MCXLIII', 'MCXLIV', 'MCXLV', 'MCXLVI', 'MCXLVII', 'MCXLVIII', 'MCXLIX', 'MCL', 'MCLI', 'MCLII', 'MCLIII', 'MCLIV', 'MCLV', 'MCLVI', 'MCLVII', 'MCLVIII', 'MCLIX', 'MCLX', 'MCLXI', 'MCLXII', 'MCLXIII', 'MCLXIV', 'MCLXV', 'MCLXVI', 'MCLXVII', 'MCLXVIII', 'MCLXIX', 'MCLXX', 'MCLXXI', 'MCLXXII', 'MCLXXIII', 'MCLXXIV', 'MCLXXV', 'MCLXXVI', 'MCLXXVII', 'MCLXXVIII', 'MCLXXIX', 'MCLXXX', 'MCLXXXI', 'MCLXXXII', 'MCLXXXIII', 'MCLXXXIV', 'MCLXXXV', 'MCLXXXVI', 'MCLXXXVII', 'MCLXXXVIII', 'MCLXXXIX', 'MCXC', 'MCXCI', 'MCXCII', 'MCXCIII', 'MCXCIV', 'MCXCV', 'MCXCVI', 'MCXCVII', 'MCXCVIII', 'MCXCIX', 'MCC', 'MCCI', 'MCCII', 'MCCIII', 'MCCIV', 'MCCV', 'MCCVI', 'MCCVII', 'MCCVIII', 'MCCIX', 'MCCX', 'MCCXI', 'MCCXII', 'MCCXIII', 'MCCXIV', 'MCCXV', 'MCCXVI', 'MCCXVII', 'MCCXVIII', 'MCCXIX', 'MCCXX', 'MCCXXI', 'MCCXXII', 'MCCXXIII', 'MCCXXIV', 'MCCXXV', 'MCCXXVI', 'MCCXXVII', 'MCCXXVIII', 'MCCXXIX', 'MCCXXX', 'MCCXXXI', 'MCCXXXII', 'MCCXXXIII', 'MCCXXXIV', 'MCCXXXV', 'MCCXXXVI', 'MCCXXXVII', 'MCCXXXVIII', 'MCCXXXIX', 'MCCXL', 'MCCXLI', 'MCCXLII', 'MCCXLIII', 'MCCXLIV', 'MCCXLV', 'MCCXLVI', 'MCCXLVII', 'MCCXLVIII', 'MCCXLIX', 'MCCL', 'MCCLI', 'MCCLII', 'MCCLIII', 'MCCLIV', 'MCCLV', 'MCCLVI', 'MCCLVII', 'MCCLVIII', 'MCCLIX', 'MCCLX', 'MCCLXI', 'MCCLXII', 'MCCLXIII', 'MCCLXIV', 'MCCLXV', 'MCCLXVI', 'MCCLXVII', 'MCCLXVIII', 'MCCLXIX', 'MCCLXX', 'MCCLXXI', 'MCCLXXII', 'MCCLXXIII', 'MCCLXXIV', 'MCCLXXV', 'MCCLXXVI', 'MCCLXXVII', 'MCCLXXVIII', 'MCCLXXIX', 'MCCLXXX', 'MCCLXXXI', 'MCCLXXXII', 'MCCLXXXIII', 'MCCLXXXIV', 'MCCLXXXV', 'MCCLXXXVI', 'MCCLXXXVII', 'MCCLXXXVIII', 'MCCLXXXIX', 'MCCXC', 'MCCXCI', 'MCCXCII', 'MCCXCIII', 'MCCXCIV', 'MCCXCV', 'MCCXCVI', 'MCCXCVII', 'MCCXCVIII', 'MCCXCIX', 'MCCC', 'MCCCI', 'MCCCII', 'MCCCIII', 'MCCCIV', 'MCCCV', 'MCCCVI', 'MCCCVII', 'MCCCVIII', 'MCCCIX', 'MCCCX', 'MCCCXI', 'MCCCXII', 'MCCCXIII', 'MCCCXIV', 'MCCCXV', 'MCCCXVI', 'MCCCXVII', 'MCCCXVIII', 'MCCCXIX', 'MCCCXX', 'MCCCXXI', 'MCCCXXII', 'MCCCXXIII', 'MCCCXXIV', 'MCCCXXV', 'MCCCXXVI', 'MCCCXXVII', 'MCCCXXVIII', 'MCCCXXIX', 'MCCCXXX', 'MCCCXXXI', 'MCCCXXXII', 'MCCCXXXIII', 'MCCCXXXIV', 'MCCCXXXV', 'MCCCXXXVI', 'MCCCXXXVII', 'MCCCXXXVIII', 'MCCCXXXIX', 'MCCCXL', 'MCCCXLI', 'MCCCXLII', 'MCCCXLIII', 'MCCCXLIV', 'MCCCXLV', 'MCCCXLVI', 'MCCCXLVII', 'MCCCXLVIII', 'MCCCXLIX', 'MCCCL', 'MCCCLI', 'MCCCLII', 'MCCCLIII', 'MCCCLIV', 'MCCCLV', 'MCCCLVI', 'MCCCLVII', 'MCCCLVIII', 'MCCCLIX', 'MCCCLX', 'MCCCLXI', 'MCCCLXII', 'MCCCLXIII', 'MCCCLXIV', 'MCCCLXV', 'MCCCLXVI', 'MCCCLXVII', 'MCCCLXVIII', 'MCCCLXIX', 'MCCCLXX', 'MCCCLXXI', 'MCCCLXXII', 'MCCCLXXIII', 'MCCCLXXIV', 'MCCCLXXV', 'MCCCLXXVI', 'MCCCLXXVII', 'MCCCLXXVIII', 'MCCCLXXIX', 'MCCCLXXX', 'MCCCLXXXI', 'MCCCLXXXII', 'MCCCLXXXIII', 'MCCCLXXXIV', 'MCCCLXXXV', 'MCCCLXXXVI', 'MCCCLXXXVII', 'MCCCLXXXVIII', 'MCCCLXXXIX', 'MCCCXC', 'MCCCXCI', 'MCCCXCII', 'MCCCXCIII', 'MCCCXCIV', 'MCCCXCV', 'MCCCXCVI', 'MCCCXCVII', 'MCCCXCVIII', 'MCCCXCIX', 'MCD', 'MCDI', 'MCDII', 'MCDIII', 'MCDIV', 'MCDV', 'MCDVI', 'MCDVII', 'MCDVIII', 'MCDIX', 'MCDX', 'MCDXI', 'MCDXII', 'MCDXIII', 'MCDXIV', 'MCDXV', 'MCDXVI', 'MCDXVII', 'MCDXVIII', 'MCDXIX', 'MCDXX', 'MCDXXI', 'MCDXXII', 'MCDXXIII', 'MCDXXIV', 'MCDXXV', 'MCDXXVI', 'MCDXXVII', 'MCDXXVIII', 'MCDXXIX', 'MCDXXX', 'MCDXXXI', 'MCDXXXII', 'MCDXXXIII', 'MCDXXXIV', 'MCDXXXV', 'MCDXXXVI', 'MCDXXXVII', 'MCDXXXVIII', 'MCDXXXIX', 'MCDXL', 'MCDXLI', 'MCDXLII', 'MCDXLIII', 'MCDXLIV', 'MCDXLV', 'MCDXLVI', 'MCDXLVII', 'MCDXLVIII', 'MCDXLIX', 'MCDL', 'MCDLI', 'MCDLII', 'MCDLIII', 'MCDLIV', 'MCDLV', 'MCDLVI', 'MCDLVII', 'MCDLVIII', 'MCDLIX', 'MCDLX', 'MCDLXI', 'MCDLXII', 'MCDLXIII', 'MCDLXIV', 'MCDLXV', 'MCDLXVI', 'MCDLXVII', 'MCDLXVIII', 'MCDLXIX', 'MCDLXX', 'MCDLXXI', 'MCDLXXII', 'MCDLXXIII', 'MCDLXXIV', 'MCDLXXV', 'MCDLXXVI', 'MCDLXXVII', 'MCDLXXVIII', 'MCDLXXIX', 'MCDLXXX', 'MCDLXXXI', 'MCDLXXXII', 'MCDLXXXIII', 'MCDLXXXIV', 'MCDLXXXV', 'MCDLXXXVI', 'MCDLXXXVII', 'MCDLXXXVIII', 'MCDLXXXIX', 'MCDXC', 'MCDXCI', 'MCDXCII', 'MCDXCIII', 'MCDXCIV', 'MCDXCV', 'MCDXCVI', 'MCDXCVII', 'MCDXCVIII', 'MCDXCIX', 'MD', 'MDI', 'MDII', 'MDIII', 'MDIV', 'MDV', 'MDVI', 'MDVII', 'MDVIII', 'MDIX', 'MDX', 'MDXI', 'MDXII', 'MDXIII', 'MDXIV', 'MDXV', 'MDXVI', 'MDXVII', 'MDXVIII', 'MDXIX', 'MDXX', 'MDXXI', 'MDXXII', 'MDXXIII', 'MDXXIV', 'MDXXV', 'MDXXVI', 'MDXXVII', 'MDXXVIII', 'MDXXIX', 'MDXXX', 'MDXXXI', 'MDXXXII', 'MDXXXIII', 'MDXXXIV', 'MDXXXV', 'MDXXXVI', 'MDXXXVII', 'MDXXXVIII', 'MDXXXIX', 'MDXL', 'MDXLI', 'MDXLII', 'MDXLIII', 'MDXLIV', 'MDXLV', 'MDXLVI', 'MDXLVII', 'MDXLVIII', 'MDXLIX', 'MDL', 'MDLI', 'MDLII', 'MDLIII', 'MDLIV', 'MDLV', 'MDLVI', 'MDLVII', 'MDLVIII', 'MDLIX', 'MDLX', 'MDLXI', 'MDLXII', 'MDLXIII', 'MDLXIV', 'MDLXV', 'MDLXVI', 'MDLXVII', 'MDLXVIII', 'MDLXIX', 'MDLXX', 'MDLXXI', 'MDLXXII', 'MDLXXIII', 'MDLXXIV', 'MDLXXV', 'MDLXXVI', 'MDLXXVII', 'MDLXXVIII', 'MDLXXIX', 'MDLXXX', 'MDLXXXI', 'MDLXXXII', 'MDLXXXIII', 'MDLXXXIV', 'MDLXXXV', 'MDLXXXVI', 'MDLXXXVII', 'MDLXXXVIII', 'MDLXXXIX', 'MDXC', 'MDXCI', 'MDXCII', 'MDXCIII', 'MDXCIV', 'MDXCV', 'MDXCVI', 'MDXCVII', 'MDXCVIII', 'MDXCIX', 'MDC', 'MDCI', 'MDCII', 'MDCIII', 'MDCIV', 'MDCV', 'MDCVI', 'MDCVII', 'MDCVIII', 'MDCIX', 'MDCX', 'MDCXI', 'MDCXII', 'MDCXIII', 'MDCXIV', 'MDCXV', 'MDCXVI', 'MDCXVII', 'MDCXVIII', 'MDCXIX', 'MDCXX', 'MDCXXI', 'MDCXXII', 'MDCXXIII', 'MDCXXIV', 'MDCXXV', 'MDCXXVI', 'MDCXXVII', 'MDCXXVIII', 'MDCXXIX', 'MDCXXX', 'MDCXXXI', 'MDCXXXII', 'MDCXXXIII', 'MDCXXXIV', 'MDCXXXV', 'MDCXXXVI', 'MDCXXXVII', 'MDCXXXVIII', 'MDCXXXIX', 'MDCXL', 'MDCXLI', 'MDCXLII', 'MDCXLIII', 'MDCXLIV', 'MDCXLV', 'MDCXLVI', 'MDCXLVII', 'MDCXLVIII', 'MDCXLIX', 'MDCL', 'MDCLI', 'MDCLII', 'MDCLIII', 'MDCLIV', 'MDCLV', 'MDCLVI', 'MDCLVII', 'MDCLVIII', 'MDCLIX', 'MDCLX', 'MDCLXI', 'MDCLXII', 'MDCLXIII', 'MDCLXIV', 'MDCLXV', 'MDCLXVI', 'MDCLXVII', 'MDCLXVIII', 'MDCLXIX', 'MDCLXX', 'MDCLXXI', 'MDCLXXII', 'MDCLXXIII', 'MDCLXXIV', 'MDCLXXV', 'MDCLXXVI', 'MDCLXXVII', 'MDCLXXVIII', 'MDCLXXIX', 'MDCLXXX', 'MDCLXXXI', 'MDCLXXXII', 'MDCLXXXIII', 'MDCLXXXIV', 'MDCLXXXV', 'MDCLXXXVI', 'MDCLXXXVII', 'MDCLXXXVIII', 'MDCLXXXIX', 'MDCXC', 'MDCXCI', 'MDCXCII', 'MDCXCIII', 'MDCXCIV', 'MDCXCV', 'MDCXCVI', 'MDCXCVII', 'MDCXCVIII', 'MDCXCIX', 'MDCC', 'MDCCI', 'MDCCII', 'MDCCIII', 'MDCCIV', 'MDCCV', 'MDCCVI', 'MDCCVII', 'MDCCVIII', 'MDCCIX', 'MDCCX', 'MDCCXI', 'MDCCXII', 'MDCCXIII', 'MDCCXIV', 'MDCCXV', 'MDCCXVI', 'MDCCXVII', 'MDCCXVIII', 'MDCCXIX', 'MDCCXX', 'MDCCXXI', 'MDCCXXII', 'MDCCXXIII', 'MDCCXXIV', 'MDCCXXV', 'MDCCXXVI', 'MDCCXXVII', 'MDCCXXVIII', 'MDCCXXIX', 'MDCCXXX', 'MDCCXXXI', 'MDCCXXXII', 'MDCCXXXIII', 'MDCCXXXIV', 'MDCCXXXV', 'MDCCXXXVI', 'MDCCXXXVII', 'MDCCXXXVIII', 'MDCCXXXIX', 'MDCCXL', 'MDCCXLI', 'MDCCXLII', 'MDCCXLIII', 'MDCCXLIV', 'MDCCXLV', 'MDCCXLVI', 'MDCCXLVII', 'MDCCXLVIII', 'MDCCXLIX', 'MDCCL', 'MDCCLI', 'MDCCLII', 'MDCCLIII', 'MDCCLIV', 'MDCCLV', 'MDCCLVI', 'MDCCLVII', 'MDCCLVIII', 'MDCCLIX', 'MDCCLX', 'MDCCLXI', 'MDCCLXII', 'MDCCLXIII', 'MDCCLXIV', 'MDCCLXV', 'MDCCLXVI', 'MDCCLXVII', 'MDCCLXVIII', 'MDCCLXIX', 'MDCCLXX', 'MDCCLXXI', 'MDCCLXXII', 'MDCCLXXIII', 'MDCCLXXIV', 'MDCCLXXV', 'MDCCLXXVI', 'MDCCLXXVII', 'MDCCLXXVIII', 'MDCCLXXIX', 'MDCCLXXX', 'MDCCLXXXI', 'MDCCLXXXII', 'MDCCLXXXIII', 'MDCCLXXXIV', 'MDCCLXXXV', 'MDCCLXXXVI', 'MDCCLXXXVII', 'MDCCLXXXVIII', 'MDCCLXXXIX', 'MDCCXC', 'MDCCXCI', 'MDCCXCII', 'MDCCXCIII', 'MDCCXCIV', 'MDCCXCV', 'MDCCXCVI', 'MDCCXCVII', 'MDCCXCVIII', 'MDCCXCIX', 'MDCCC', 'MDCCCI', 'MDCCCII', 'MDCCCIII', 'MDCCCIV', 'MDCCCV', 'MDCCCVI', 'MDCCCVII', 'MDCCCVIII', 'MDCCCIX', 'MDCCCX', 'MDCCCXI', 'MDCCCXII', 'MDCCCXIII', 'MDCCCXIV', 'MDCCCXV', 'MDCCCXVI', 'MDCCCXVII', 'MDCCCXVIII', 'MDCCCXIX', 'MDCCCXX', 'MDCCCXXI', 'MDCCCXXII', 'MDCCCXXIII', 'MDCCCXXIV', 'MDCCCXXV', 'MDCCCXXVI', 'MDCCCXXVII', 'MDCCCXXVIII', 'MDCCCXXIX', 'MDCCCXXX', 'MDCCCXXXI', 'MDCCCXXXII', 'MDCCCXXXIII', 'MDCCCXXXIV', 'MDCCCXXXV', 'MDCCCXXXVI', 'MDCCCXXXVII', 'MDCCCXXXVIII', 'MDCCCXXXIX', 'MDCCCXL', 'MDCCCXLI', 'MDCCCXLII', 'MDCCCXLIII', 'MDCCCXLIV', 'MDCCCXLV', 'MDCCCXLVI', 'MDCCCXLVII', 'MDCCCXLVIII', 'MDCCCXLIX', 'MDCCCL', 'MDCCCLI', 'MDCCCLII', 'MDCCCLIII', 'MDCCCLIV', 'MDCCCLV', 'MDCCCLVI', 'MDCCCLVII', 'MDCCCLVIII', 'MDCCCLIX', 'MDCCCLX', 'MDCCCLXI', 'MDCCCLXII', 'MDCCCLXIII', 'MDCCCLXIV', 'MDCCCLXV', 'MDCCCLXVI', 'MDCCCLXVII', 'MDCCCLXVIII', 'MDCCCLXIX', 'MDCCCLXX', 'MDCCCLXXI', 'MDCCCLXXII', 'MDCCCLXXIII', 'MDCCCLXXIV', 'MDCCCLXXV', 'MDCCCLXXVI', 'MDCCCLXXVII', 'MDCCCLXXVIII', 'MDCCCLXXIX', 'MDCCCLXXX', 'MDCCCLXXXI', 'MDCCCLXXXII', 'MDCCCLXXXIII', 'MDCCCLXXXIV', 'MDCCCLXXXV', 'MDCCCLXXXVI', 'MDCCCLXXXVII', 'MDCCCLXXXVIII', 'MDCCCLXXXIX', 'MDCCCXC', 'MDCCCXCI', 'MDCCCXCII', 'MDCCCXCIII', 'MDCCCXCIV', 'MDCCCXCV', 'MDCCCXCVI', 'MDCCCXCVII', 'MDCCCXCVIII', 'MDCCCXCIX', 'MCM', 'MCMI', 'MCMII', 'MCMIII', 'MCMIV', 'MCMV', 'MCMVI', 'MCMVII', 'MCMVIII', 'MCMIX', 'MCMX', 'MCMXI', 'MCMXII', 'MCMXIII', 'MCMXIV', 'MCMXV', 'MCMXVI', 'MCMXVII', 'MCMXVIII', 'MCMXIX', 'MCMXX', 'MCMXXI', 'MCMXXII', 'MCMXXIII', 'MCMXXIV', 'MCMXXV', 'MCMXXVI', 'MCMXXVII', 'MCMXXVIII', 'MCMXXIX', 'MCMXXX', 'MCMXXXI', 'MCMXXXII', 'MCMXXXIII', 'MCMXXXIV', 'MCMXXXV', 'MCMXXXVI', 'MCMXXXVII', 'MCMXXXVIII', 'MCMXXXIX', 'MCMXL', 'MCMXLI', 'MCMXLII', 'MCMXLIII', 'MCMXLIV', 'MCMXLV', 'MCMXLVI', 'MCMXLVII', 'MCMXLVIII', 'MCMXLIX', 'MCML', 'MCMLI', 'MCMLII', 'MCMLIII', 'MCMLIV', 'MCMLV', 'MCMLVI', 'MCMLVII', 'MCMLVIII', 'MCMLIX', 'MCMLX', 'MCMLXI', 'MCMLXII', 'MCMLXIII', 'MCMLXIV', 'MCMLXV', 'MCMLXVI', 'MCMLXVII', 'MCMLXVIII', 'MCMLXIX', 'MCMLXX', 'MCMLXXI', 'MCMLXXII', 'MCMLXXIII', 'MCMLXXIV', 'MCMLXXV', 'MCMLXXVI', 'MCMLXXVII', 'MCMLXXVIII', 'MCMLXXIX', 'MCMLXXX', 'MCMLXXXI', 'MCMLXXXII', 'MCMLXXXIII', 'MCMLXXXIV', 'MCMLXXXV', 'MCMLXXXVI', 'MCMLXXXVII', 'MCMLXXXVIII', 'MCMLXXXIX', 'MCMXC', 'MCMXCI', 'MCMXCII', 'MCMXCIII', 'MCMXCIV', 'MCMXCV', 'MCMXCVI', 'MCMXCVII', 'MCMXCVIII', 'MCMXCIX', 'MM', 'MMI', 'MMII', 'MMIII', 'MMIV', 'MMV', 'MMVI', 'MMVII', 'MMVIII', 'MMIX', 'MMX', 'MMXI', 'MMXII', 'MMXIII', 'MMXIV', 'MMXV', 'MMXVI', 'MMXVII', 'MMXVIII', 'MMXIX', 'MMXX', 'MMXXI', 'MMXXII', 'MMXXIII', 'MMXXIV', 'MMXXV', 'MMXXVI', 'MMXXVII', 'MMXXVIII', 'MMXXIX', 'MMXXX', 'MMXXXI', 'MMXXXII', 'MMXXXIII', 'MMXXXIV', 'MMXXXV', 'MMXXXVI', 'MMXXXVII', 'MMXXXVIII', 'MMXXXIX', 'MMXL', 'MMXLI', 'MMXLII', 'MMXLIII', 'MMXLIV', 'MMXLV', 'MMXLVI', 'MMXLVII', 'MMXLVIII', 'MMXLIX', 'MML', 'MMLI', 'MMLII', 'MMLIII', 'MMLIV', 'MMLV', 'MMLVI', 'MMLVII', 'MMLVIII', 'MMLIX', 'MMLX', 'MMLXI', 'MMLXII', 'MMLXIII', 'MMLXIV', 'MMLXV', 'MMLXVI', 'MMLXVII', 'MMLXVIII', 'MMLXIX', 'MMLXX', 'MMLXXI', 'MMLXXII', 'MMLXXIII', 'MMLXXIV', 'MMLXXV', 'MMLXXVI', 'MMLXXVII', 'MMLXXVIII', 'MMLXXIX', 'MMLXXX', 'MMLXXXI', 'MMLXXXII', 'MMLXXXIII', 'MMLXXXIV', 'MMLXXXV', 'MMLXXXVI', 'MMLXXXVII', 'MMLXXXVIII', 'MMLXXXIX', 'MMXC', 'MMXCI', 'MMXCII', 'MMXCIII', 'MMXCIV', 'MMXCV', 'MMXCVI', 'MMXCVII', 'MMXCVIII', 'MMXCIX', 'MMC', 'MMCI', 'MMCII', 'MMCIII', 'MMCIV', 'MMCV', 'MMCVI', 'MMCVII', 'MMCVIII', 'MMCIX', 'MMCX', 'MMCXI', 'MMCXII', 'MMCXIII', 'MMCXIV', 'MMCXV', 'MMCXVI', 'MMCXVII', 'MMCXVIII', 'MMCXIX', 'MMCXX', 'MMCXXI', 'MMCXXII', 'MMCXXIII', 'MMCXXIV', 'MMCXXV', 'MMCXXVI', 'MMCXXVII', 'MMCXXVIII', 'MMCXXIX', 'MMCXXX', 'MMCXXXI', 'MMCXXXII', 'MMCXXXIII', 'MMCXXXIV', 'MMCXXXV', 'MMCXXXVI', 'MMCXXXVII', 'MMCXXXVIII', 'MMCXXXIX', 'MMCXL', 'MMCXLI', 'MMCXLII', 'MMCXLIII', 'MMCXLIV', 'MMCXLV', 'MMCXLVI', 'MMCXLVII', 'MMCXLVIII', 'MMCXLIX', 'MMCL', 'MMCLI', 'MMCLII', 'MMCLIII', 'MMCLIV', 'MMCLV', 'MMCLVI', 'MMCLVII', 'MMCLVIII', 'MMCLIX', 'MMCLX', 'MMCLXI', 'MMCLXII', 'MMCLXIII', 'MMCLXIV', 'MMCLXV', 'MMCLXVI', 'MMCLXVII', 'MMCLXVIII', 'MMCLXIX', 'MMCLXX', 'MMCLXXI', 'MMCLXXII', 'MMCLXXIII', 'MMCLXXIV', 'MMCLXXV', 'MMCLXXVI', 'MMCLXXVII', 'MMCLXXVIII', 'MMCLXXIX', 'MMCLXXX', 'MMCLXXXI', 'MMCLXXXII', 'MMCLXXXIII', 'MMCLXXXIV', 'MMCLXXXV', 'MMCLXXXVI', 'MMCLXXXVII', 'MMCLXXXVIII', 'MMCLXXXIX', 'MMCXC', 'MMCXCI', 'MMCXCII', 'MMCXCIII', 'MMCXCIV', 'MMCXCV', 'MMCXCVI', 'MMCXCVII', 'MMCXCVIII', 'MMCXCIX', 'MMCC', 'MMCCI', 'MMCCII', 'MMCCIII', 'MMCCIV', 'MMCCV', 'MMCCVI', 'MMCCVII', 'MMCCVIII', 'MMCCIX', 'MMCCX', 'MMCCXI', 'MMCCXII', 'MMCCXIII', 'MMCCXIV', 'MMCCXV', 'MMCCXVI', 'MMCCXVII', 'MMCCXVIII', 'MMCCXIX', 'MMCCXX', 'MMCCXXI', 'MMCCXXII', 'MMCCXXIII', 'MMCCXXIV', 'MMCCXXV', 'MMCCXXVI', 'MMCCXXVII', 'MMCCXXVIII', 'MMCCXXIX', 'MMCCXXX', 'MMCCXXXI', 'MMCCXXXII', 'MMCCXXXIII', 'MMCCXXXIV', 'MMCCXXXV', 'MMCCXXXVI', 'MMCCXXXVII', 'MMCCXXXVIII', 'MMCCXXXIX', 'MMCCXL', 'MMCCXLI', 'MMCCXLII', 'MMCCXLIII', 'MMCCXLIV', 'MMCCXLV', 'MMCCXLVI', 'MMCCXLVII', 'MMCCXLVIII', 'MMCCXLIX', 'MMCCL', 'MMCCLI', 'MMCCLII', 'MMCCLIII', 'MMCCLIV', 'MMCCLV', 'MMCCLVI', 'MMCCLVII', 'MMCCLVIII', 'MMCCLIX', 'MMCCLX', 'MMCCLXI', 'MMCCLXII', 'MMCCLXIII', 'MMCCLXIV', 'MMCCLXV', 'MMCCLXVI', 'MMCCLXVII', 'MMCCLXVIII', 'MMCCLXIX', 'MMCCLXX', 'MMCCLXXI', 'MMCCLXXII', 'MMCCLXXIII', 'MMCCLXXIV', 'MMCCLXXV', 'MMCCLXXVI', 'MMCCLXXVII', 'MMCCLXXVIII', 'MMCCLXXIX', 'MMCCLXXX', 'MMCCLXXXI', 'MMCCLXXXII', 'MMCCLXXXIII', 'MMCCLXXXIV', 'MMCCLXXXV', 'MMCCLXXXVI', 'MMCCLXXXVII', 'MMCCLXXXVIII', 'MMCCLXXXIX', 'MMCCXC', 'MMCCXCI', 'MMCCXCII', 'MMCCXCIII', 'MMCCXCIV', 'MMCCXCV', 'MMCCXCVI', 'MMCCXCVII', 'MMCCXCVIII', 'MMCCXCIX', 'MMCCC', 'MMCCCI', 'MMCCCII', 'MMCCCIII', 'MMCCCIV', 'MMCCCV', 'MMCCCVI', 'MMCCCVII', 'MMCCCVIII', 'MMCCCIX', 'MMCCCX', 'MMCCCXI', 'MMCCCXII', 'MMCCCXIII', 'MMCCCXIV', 'MMCCCXV', 'MMCCCXVI', 'MMCCCXVII', 'MMCCCXVIII', 'MMCCCXIX', 'MMCCCXX', 'MMCCCXXI', 'MMCCCXXII', 'MMCCCXXIII', 'MMCCCXXIV', 'MMCCCXXV', 'MMCCCXXVI', 'MMCCCXXVII', 'MMCCCXXVIII', 'MMCCCXXIX', 'MMCCCXXX', 'MMCCCXXXI', 'MMCCCXXXII', 'MMCCCXXXIII', 'MMCCCXXXIV', 'MMCCCXXXV', 'MMCCCXXXVI', 'MMCCCXXXVII', 'MMCCCXXXVIII', 'MMCCCXXXIX', 'MMCCCXL', 'MMCCCXLI', 'MMCCCXLII', 'MMCCCXLIII', 'MMCCCXLIV', 'MMCCCXLV', 'MMCCCXLVI', 'MMCCCXLVII', 'MMCCCXLVIII', 'MMCCCXLIX', 'MMCCCL', 'MMCCCLI', 'MMCCCLII', 'MMCCCLIII', 'MMCCCLIV', 'MMCCCLV', 'MMCCCLVI', 'MMCCCLVII', 'MMCCCLVIII', 'MMCCCLIX', 'MMCCCLX', 'MMCCCLXI', 'MMCCCLXII', 'MMCCCLXIII', 'MMCCCLXIV', 'MMCCCLXV', 'MMCCCLXVI', 'MMCCCLXVII', 'MMCCCLXVIII', 'MMCCCLXIX', 'MMCCCLXX', 'MMCCCLXXI', 'MMCCCLXXII', 'MMCCCLXXIII', 'MMCCCLXXIV', 'MMCCCLXXV', 'MMCCCLXXVI', 'MMCCCLXXVII', 'MMCCCLXXVIII', 'MMCCCLXXIX', 'MMCCCLXXX', 'MMCCCLXXXI', 'MMCCCLXXXII', 'MMCCCLXXXIII', 'MMCCCLXXXIV', 'MMCCCLXXXV', 'MMCCCLXXXVI', 'MMCCCLXXXVII', 'MMCCCLXXXVIII', 'MMCCCLXXXIX', 'MMCCCXC', 'MMCCCXCI', 'MMCCCXCII', 'MMCCCXCIII', 'MMCCCXCIV', 'MMCCCXCV', 'MMCCCXCVI', 'MMCCCXCVII', 'MMCCCXCVIII', 'MMCCCXCIX', 'MMCD', 'MMCDI', 'MMCDII', 'MMCDIII', 'MMCDIV', 'MMCDV', 'MMCDVI', 'MMCDVII', 'MMCDVIII', 'MMCDIX', 'MMCDX', 'MMCDXI', 'MMCDXII', 'MMCDXIII', 'MMCDXIV', 'MMCDXV', 'MMCDXVI', 'MMCDXVII', 'MMCDXVIII', 'MMCDXIX', 'MMCDXX', 'MMCDXXI', 'MMCDXXII', 'MMCDXXIII', 'MMCDXXIV', 'MMCDXXV', 'MMCDXXVI', 'MMCDXXVII', 'MMCDXXVIII', 'MMCDXXIX', 'MMCDXXX', 'MMCDXXXI', 'MMCDXXXII', 'MMCDXXXIII', 'MMCDXXXIV', 'MMCDXXXV', 'MMCDXXXVI', 'MMCDXXXVII', 'MMCDXXXVIII', 'MMCDXXXIX', 'MMCDXL', 'MMCDXLI', 'MMCDXLII', 'MMCDXLIII', 'MMCDXLIV', 'MMCDXLV', 'MMCDXLVI', 'MMCDXLVII', 'MMCDXLVIII', 'MMCDXLIX', 'MMCDL', 'MMCDLI', 'MMCDLII', 'MMCDLIII', 'MMCDLIV', 'MMCDLV', 'MMCDLVI', 'MMCDLVII', 'MMCDLVIII', 'MMCDLIX', 'MMCDLX', 'MMCDLXI', 'MMCDLXII', 'MMCDLXIII', 'MMCDLXIV', 'MMCDLXV', 'MMCDLXVI', 'MMCDLXVII', 'MMCDLXVIII', 'MMCDLXIX', 'MMCDLXX', 'MMCDLXXI', 'MMCDLXXII', 'MMCDLXXIII', 'MMCDLXXIV', 'MMCDLXXV', 'MMCDLXXVI', 'MMCDLXXVII', 'MMCDLXXVIII', 'MMCDLXXIX', 'MMCDLXXX', 'MMCDLXXXI', 'MMCDLXXXII', 'MMCDLXXXIII', 'MMCDLXXXIV', 'MMCDLXXXV', 'MMCDLXXXVI', 'MMCDLXXXVII', 'MMCDLXXXVIII', 'MMCDLXXXIX', 'MMCDXC', 'MMCDXCI', 'MMCDXCII', 'MMCDXCIII', 'MMCDXCIV', 'MMCDXCV', 'MMCDXCVI', 'MMCDXCVII', 'MMCDXCVIII', 'MMCDXCIX', 'MMD', 'MMDI', 'MMDII', 'MMDIII', 'MMDIV', 'MMDV', 'MMDVI', 'MMDVII', 'MMDVIII', 'MMDIX', 'MMDX', 'MMDXI', 'MMDXII', 'MMDXIII', 'MMDXIV', 'MMDXV', 'MMDXVI', 'MMDXVII', 'MMDXVIII', 'MMDXIX', 'MMDXX', 'MMDXXI', 'MMDXXII', 'MMDXXIII', 'MMDXXIV', 'MMDXXV', 'MMDXXVI', 'MMDXXVII', 'MMDXXVIII', 'MMDXXIX', 'MMDXXX', 'MMDXXXI', 'MMDXXXII', 'MMDXXXIII', 'MMDXXXIV', 'MMDXXXV', 'MMDXXXVI', 'MMDXXXVII', 'MMDXXXVIII', 'MMDXXXIX', 'MMDXL', 'MMDXLI', 'MMDXLII', 'MMDXLIII', 'MMDXLIV', 'MMDXLV', 'MMDXLVI', 'MMDXLVII', 'MMDXLVIII', 'MMDXLIX', 'MMDL', 'MMDLI', 'MMDLII', 'MMDLIII', 'MMDLIV', 'MMDLV', 'MMDLVI', 'MMDLVII', 'MMDLVIII', 'MMDLIX', 'MMDLX', 'MMDLXI', 'MMDLXII', 'MMDLXIII', 'MMDLXIV', 'MMDLXV', 'MMDLXVI', 'MMDLXVII', 'MMDLXVIII', 'MMDLXIX', 'MMDLXX', 'MMDLXXI', 'MMDLXXII', 'MMDLXXIII', 'MMDLXXIV', 'MMDLXXV', 'MMDLXXVI', 'MMDLXXVII', 'MMDLXXVIII', 'MMDLXXIX', 'MMDLXXX', 'MMDLXXXI', 'MMDLXXXII', 'MMDLXXXIII', 'MMDLXXXIV', 'MMDLXXXV', 'MMDLXXXVI', 'MMDLXXXVII', 'MMDLXXXVIII', 'MMDLXXXIX', 'MMDXC', 'MMDXCI', 'MMDXCII', 'MMDXCIII', 'MMDXCIV', 'MMDXCV', 'MMDXCVI', 'MMDXCVII', 'MMDXCVIII', 'MMDXCIX', 'MMDC', 'MMDCI', 'MMDCII', 'MMDCIII', 'MMDCIV', 'MMDCV', 'MMDCVI', 'MMDCVII', 'MMDCVIII', 'MMDCIX', 'MMDCX', 'MMDCXI', 'MMDCXII', 'MMDCXIII', 'MMDCXIV', 'MMDCXV', 'MMDCXVI', 'MMDCXVII', 'MMDCXVIII', 'MMDCXIX', 'MMDCXX', 'MMDCXXI', 'MMDCXXII', 'MMDCXXIII', 'MMDCXXIV', 'MMDCXXV', 'MMDCXXVI', 'MMDCXXVII', 'MMDCXXVIII', 'MMDCXXIX', 'MMDCXXX', 'MMDCXXXI', 'MMDCXXXII', 'MMDCXXXIII', 'MMDCXXXIV', 'MMDCXXXV', 'MMDCXXXVI', 'MMDCXXXVII', 'MMDCXXXVIII', 'MMDCXXXIX', 'MMDCXL', 'MMDCXLI', 'MMDCXLII', 'MMDCXLIII', 'MMDCXLIV', 'MMDCXLV', 'MMDCXLVI', 'MMDCXLVII', 'MMDCXLVIII', 'MMDCXLIX', 'MMDCL', 'MMDCLI', 'MMDCLII', 'MMDCLIII', 'MMDCLIV', 'MMDCLV', 'MMDCLVI', 'MMDCLVII', 'MMDCLVIII', 'MMDCLIX', 'MMDCLX', 'MMDCLXI', 'MMDCLXII', 'MMDCLXIII', 'MMDCLXIV', 'MMDCLXV', 'MMDCLXVI', 'MMDCLXVII', 'MMDCLXVIII', 'MMDCLXIX', 'MMDCLXX', 'MMDCLXXI', 'MMDCLXXII', 'MMDCLXXIII', 'MMDCLXXIV', 'MMDCLXXV', 'MMDCLXXVI', 'MMDCLXXVII', 'MMDCLXXVIII', 'MMDCLXXIX', 'MMDCLXXX', 'MMDCLXXXI', 'MMDCLXXXII', 'MMDCLXXXIII', 'MMDCLXXXIV', 'MMDCLXXXV', 'MMDCLXXXVI', 'MMDCLXXXVII', 'MMDCLXXXVIII', 'MMDCLXXXIX', 'MMDCXC', 'MMDCXCI', 'MMDCXCII', 'MMDCXCIII', 'MMDCXCIV', 'MMDCXCV', 'MMDCXCVI', 'MMDCXCVII', 'MMDCXCVIII', 'MMDCXCIX', 'MMDCC', 'MMDCCI', 'MMDCCII', 'MMDCCIII', 'MMDCCIV', 'MMDCCV', 'MMDCCVI', 'MMDCCVII', 'MMDCCVIII', 'MMDCCIX', 'MMDCCX', 'MMDCCXI', 'MMDCCXII', 'MMDCCXIII', 'MMDCCXIV', 'MMDCCXV', 'MMDCCXVI', 'MMDCCXVII', 'MMDCCXVIII', 'MMDCCXIX', 'MMDCCXX', 'MMDCCXXI', 'MMDCCXXII', 'MMDCCXXIII', 'MMDCCXXIV', 'MMDCCXXV', 'MMDCCXXVI', 'MMDCCXXVII', 'MMDCCXXVIII', 'MMDCCXXIX', 'MMDCCXXX', 'MMDCCXXXI', 'MMDCCXXXII', 'MMDCCXXXIII', 'MMDCCXXXIV', 'MMDCCXXXV', 'MMDCCXXXVI', 'MMDCCXXXVII', 'MMDCCXXXVIII', 'MMDCCXXXIX', 'MMDCCXL', 'MMDCCXLI', 'MMDCCXLII', 'MMDCCXLIII', 'MMDCCXLIV', 'MMDCCXLV', 'MMDCCXLVI', 'MMDCCXLVII', 'MMDCCXLVIII', 'MMDCCXLIX', 'MMDCCL', 'MMDCCLI', 'MMDCCLII', 'MMDCCLIII', 'MMDCCLIV', 'MMDCCLV', 'MMDCCLVI', 'MMDCCLVII', 'MMDCCLVIII', 'MMDCCLIX', 'MMDCCLX', 'MMDCCLXI', 'MMDCCLXII', 'MMDCCLXIII', 'MMDCCLXIV', 'MMDCCLXV', 'MMDCCLXVI', 'MMDCCLXVII', 'MMDCCLXVIII', 'MMDCCLXIX', 'MMDCCLXX', 'MMDCCLXXI', 'MMDCCLXXII', 'MMDCCLXXIII', 'MMDCCLXXIV', 'MMDCCLXXV', 'MMDCCLXXVI', 'MMDCCLXXVII', 'MMDCCLXXVIII', 'MMDCCLXXIX', 'MMDCCLXXX', 'MMDCCLXXXI', 'MMDCCLXXXII', 'MMDCCLXXXIII', 'MMDCCLXXXIV', 'MMDCCLXXXV', 'MMDCCLXXXVI', 'MMDCCLXXXVII', 'MMDCCLXXXVIII', 'MMDCCLXXXIX', 'MMDCCXC', 'MMDCCXCI', 'MMDCCXCII', 'MMDCCXCIII', 'MMDCCXCIV', 'MMDCCXCV', 'MMDCCXCVI', 'MMDCCXCVII', 'MMDCCXCVIII', 'MMDCCXCIX', 'MMDCCC', 'MMDCCCI', 'MMDCCCII', 'MMDCCCIII', 'MMDCCCIV', 'MMDCCCV', 'MMDCCCVI', 'MMDCCCVII', 'MMDCCCVIII', 'MMDCCCIX', 'MMDCCCX', 'MMDCCCXI', 'MMDCCCXII', 'MMDCCCXIII', 'MMDCCCXIV', 'MMDCCCXV', 'MMDCCCXVI', 'MMDCCCXVII', 'MMDCCCXVIII', 'MMDCCCXIX', 'MMDCCCXX', 'MMDCCCXXI', 'MMDCCCXXII', 'MMDCCCXXIII', 'MMDCCCXXIV', 'MMDCCCXXV', 'MMDCCCXXVI', 'MMDCCCXXVII', 'MMDCCCXXVIII', 'MMDCCCXXIX', 'MMDCCCXXX', 'MMDCCCXXXI', 'MMDCCCXXXII', 'MMDCCCXXXIII', 'MMDCCCXXXIV', 'MMDCCCXXXV', 'MMDCCCXXXVI', 'MMDCCCXXXVII', 'MMDCCCXXXVIII', 'MMDCCCXXXIX', 'MMDCCCXL', 'MMDCCCXLI', 'MMDCCCXLII', 'MMDCCCXLIII', 'MMDCCCXLIV', 'MMDCCCXLV', 'MMDCCCXLVI', 'MMDCCCXLVII', 'MMDCCCXLVIII', 'MMDCCCXLIX', 'MMDCCCL', 'MMDCCCLI', 'MMDCCCLII', 'MMDCCCLIII', 'MMDCCCLIV', 'MMDCCCLV', 'MMDCCCLVI', 'MMDCCCLVII', 'MMDCCCLVIII', 'MMDCCCLIX', 'MMDCCCLX', 'MMDCCCLXI', 'MMDCCCLXII', 'MMDCCCLXIII', 'MMDCCCLXIV', 'MMDCCCLXV', 'MMDCCCLXVI', 'MMDCCCLXVII', 'MMDCCCLXVIII', 'MMDCCCLXIX', 'MMDCCCLXX', 'MMDCCCLXXI', 'MMDCCCLXXII', 'MMDCCCLXXIII', 'MMDCCCLXXIV', 'MMDCCCLXXV', 'MMDCCCLXXVI', 'MMDCCCLXXVII', 'MMDCCCLXXVIII', 'MMDCCCLXXIX', 'MMDCCCLXXX', 'MMDCCCLXXXI', 'MMDCCCLXXXII', 'MMDCCCLXXXIII', 'MMDCCCLXXXIV', 'MMDCCCLXXXV', 'MMDCCCLXXXVI', 'MMDCCCLXXXVII', 'MMDCCCLXXXVIII', 'MMDCCCLXXXIX', 'MMDCCCXC', 'MMDCCCXCI', 'MMDCCCXCII', 'MMDCCCXCIII', 'MMDCCCXCIV', 'MMDCCCXCV', 'MMDCCCXCVI', 'MMDCCCXCVII', 'MMDCCCXCVIII', 'MMDCCCXCIX', 'MMCM', 'MMCMI', 'MMCMII', 'MMCMIII', 'MMCMIV', 'MMCMV', 'MMCMVI', 'MMCMVII', 'MMCMVIII', 'MMCMIX', 'MMCMX', 'MMCMXI', 'MMCMXII', 'MMCMXIII', 'MMCMXIV', 'MMCMXV', 'MMCMXVI', 'MMCMXVII', 'MMCMXVIII', 'MMCMXIX', 'MMCMXX', 'MMCMXXI', 'MMCMXXII', 'MMCMXXIII', 'MMCMXXIV', 'MMCMXXV', 'MMCMXXVI', 'MMCMXXVII', 'MMCMXXVIII', 'MMCMXXIX', 'MMCMXXX', 'MMCMXXXI', 'MMCMXXXII', 'MMCMXXXIII', 'MMCMXXXIV', 'MMCMXXXV', 'MMCMXXXVI', 'MMCMXXXVII', 'MMCMXXXVIII', 'MMCMXXXIX', 'MMCMXL', 'MMCMXLI', 'MMCMXLII', 'MMCMXLIII', 'MMCMXLIV', 'MMCMXLV', 'MMCMXLVI', 'MMCMXLVII', 'MMCMXLVIII', 'MMCMXLIX', 'MMCML', 'MMCMLI', 'MMCMLII', 'MMCMLIII', 'MMCMLIV', 'MMCMLV', 'MMCMLVI', 'MMCMLVII', 'MMCMLVIII', 'MMCMLIX', 'MMCMLX', 'MMCMLXI', 'MMCMLXII', 'MMCMLXIII', 'MMCMLXIV', 'MMCMLXV', 'MMCMLXVI', 'MMCMLXVII', 'MMCMLXVIII', 'MMCMLXIX', 'MMCMLXX', 'MMCMLXXI', 'MMCMLXXII', 'MMCMLXXIII', 'MMCMLXXIV', 'MMCMLXXV', 'MMCMLXXVI', 'MMCMLXXVII', 'MMCMLXXVIII', 'MMCMLXXIX', 'MMCMLXXX', 'MMCMLXXXI', 'MMCMLXXXII', 'MMCMLXXXIII', 'MMCMLXXXIV', 'MMCMLXXXV', 'MMCMLXXXVI', 'MMCMLXXXVII', 'MMCMLXXXVIII', 'MMCMLXXXIX', 'MMCMXC', 'MMCMXCI', 'MMCMXCII', 'MMCMXCIII', 'MMCMXCIV', 'MMCMXCV', 'MMCMXCVI', 'MMCMXCVII', 'MMCMXCVIII', 'MMCMXCIX', 'MMM', 'MMMI', 'MMMII', 'MMMIII', 'MMMIV', 'MMMV', 'MMMVI', 'MMMVII', 'MMMVIII', 'MMMIX', 'MMMX', 'MMMXI', 'MMMXII', 'MMMXIII', 'MMMXIV', 'MMMXV', 'MMMXVI', 'MMMXVII', 'MMMXVIII', 'MMMXIX', 'MMMXX', 'MMMXXI', 'MMMXXII', 'MMMXXIII', 'MMMXXIV', 'MMMXXV', 'MMMXXVI', 'MMMXXVII', 'MMMXXVIII', 'MMMXXIX', 'MMMXXX', 'MMMXXXI', 'MMMXXXII', 'MMMXXXIII', 'MMMXXXIV', 'MMMXXXV', 'MMMXXXVI', 'MMMXXXVII', 'MMMXXXVIII', 'MMMXXXIX', 'MMMXL', 'MMMXLI', 'MMMXLII', 'MMMXLIII', 'MMMXLIV', 'MMMXLV', 'MMMXLVI', 'MMMXLVII', 'MMMXLVIII', 'MMMXLIX', 'MMML', 'MMMLI', 'MMMLII', 'MMMLIII', 'MMMLIV', 'MMMLV', 'MMMLVI', 'MMMLVII', 'MMMLVIII', 'MMMLIX', 'MMMLX', 'MMMLXI', 'MMMLXII', 'MMMLXIII', 'MMMLXIV', 'MMMLXV', 'MMMLXVI', 'MMMLXVII', 'MMMLXVIII', 'MMMLXIX', 'MMMLXX', 'MMMLXXI', 'MMMLXXII', 'MMMLXXIII', 'MMMLXXIV', 'MMMLXXV', 'MMMLXXVI', 'MMMLXXVII', 'MMMLXXVIII', 'MMMLXXIX', 'MMMLXXX', 'MMMLXXXI', 'MMMLXXXII', 'MMMLXXXIII', 'MMMLXXXIV', 'MMMLXXXV', 'MMMLXXXVI', 'MMMLXXXVII', 'MMMLXXXVIII', 'MMMLXXXIX', 'MMMXC', 'MMMXCI', 'MMMXCII', 'MMMXCIII', 'MMMXCIV', 'MMMXCV', 'MMMXCVI', 'MMMXCVII', 'MMMXCVIII', 'MMMXCIX', 'MMMC', 'MMMCI', 'MMMCII', 'MMMCIII', 'MMMCIV', 'MMMCV', 'MMMCVI', 'MMMCVII', 'MMMCVIII', 'MMMCIX', 'MMMCX', 'MMMCXI', 'MMMCXII', 'MMMCXIII', 'MMMCXIV', 'MMMCXV', 'MMMCXVI', 'MMMCXVII', 'MMMCXVIII', 'MMMCXIX', 'MMMCXX', 'MMMCXXI', 'MMMCXXII', 'MMMCXXIII', 'MMMCXXIV', 'MMMCXXV', 'MMMCXXVI', 'MMMCXXVII', 'MMMCXXVIII', 'MMMCXXIX', 'MMMCXXX', 'MMMCXXXI', 'MMMCXXXII', 'MMMCXXXIII', 'MMMCXXXIV', 'MMMCXXXV', 'MMMCXXXVI', 'MMMCXXXVII', 'MMMCXXXVIII', 'MMMCXXXIX', 'MMMCXL', 'MMMCXLI', 'MMMCXLII', 'MMMCXLIII', 'MMMCXLIV', 'MMMCXLV', 'MMMCXLVI', 'MMMCXLVII', 'MMMCXLVIII', 'MMMCXLIX', 'MMMCL', 'MMMCLI', 'MMMCLII', 'MMMCLIII', 'MMMCLIV', 'MMMCLV', 'MMMCLVI', 'MMMCLVII', 'MMMCLVIII', 'MMMCLIX', 'MMMCLX', 'MMMCLXI', 'MMMCLXII', 'MMMCLXIII', 'MMMCLXIV', 'MMMCLXV', 'MMMCLXVI', 'MMMCLXVII', 'MMMCLXVIII', 'MMMCLXIX', 'MMMCLXX', 'MMMCLXXI', 'MMMCLXXII', 'MMMCLXXIII', 'MMMCLXXIV', 'MMMCLXXV', 'MMMCLXXVI', 'MMMCLXXVII', 'MMMCLXXVIII', 'MMMCLXXIX', 'MMMCLXXX', 'MMMCLXXXI', 'MMMCLXXXII', 'MMMCLXXXIII', 'MMMCLXXXIV', 'MMMCLXXXV', 'MMMCLXXXVI', 'MMMCLXXXVII', 'MMMCLXXXVIII', 'MMMCLXXXIX', 'MMMCXC', 'MMMCXCI', 'MMMCXCII', 'MMMCXCIII', 'MMMCXCIV', 'MMMCXCV', 'MMMCXCVI', 'MMMCXCVII', 'MMMCXCVIII', 'MMMCXCIX', 'MMMCC', 'MMMCCI', 'MMMCCII', 'MMMCCIII', 'MMMCCIV', 'MMMCCV', 'MMMCCVI', 'MMMCCVII', 'MMMCCVIII', 'MMMCCIX', 'MMMCCX', 'MMMCCXI', 'MMMCCXII', 'MMMCCXIII', 'MMMCCXIV', 'MMMCCXV', 'MMMCCXVI', 'MMMCCXVII', 'MMMCCXVIII', 'MMMCCXIX', 'MMMCCXX', 'MMMCCXXI', 'MMMCCXXII', 'MMMCCXXIII', 'MMMCCXXIV', 'MMMCCXXV', 'MMMCCXXVI', 'MMMCCXXVII', 'MMMCCXXVIII', 'MMMCCXXIX', 'MMMCCXXX', 'MMMCCXXXI', 'MMMCCXXXII', 'MMMCCXXXIII', 'MMMCCXXXIV', 'MMMCCXXXV', 'MMMCCXXXVI', 'MMMCCXXXVII', 'MMMCCXXXVIII', 'MMMCCXXXIX', 'MMMCCXL', 'MMMCCXLI', 'MMMCCXLII', 'MMMCCXLIII', 'MMMCCXLIV', 'MMMCCXLV', 'MMMCCXLVI', 'MMMCCXLVII', 'MMMCCXLVIII', 'MMMCCXLIX', 'MMMCCL', 'MMMCCLI', 'MMMCCLII', 'MMMCCLIII', 'MMMCCLIV', 'MMMCCLV', 'MMMCCLVI', 'MMMCCLVII', 'MMMCCLVIII', 'MMMCCLIX', 'MMMCCLX', 'MMMCCLXI', 'MMMCCLXII', 'MMMCCLXIII', 'MMMCCLXIV', 'MMMCCLXV', 'MMMCCLXVI', 'MMMCCLXVII', 'MMMCCLXVIII', 'MMMCCLXIX', 'MMMCCLXX', 'MMMCCLXXI', 'MMMCCLXXII', 'MMMCCLXXIII', 'MMMCCLXXIV', 'MMMCCLXXV', 'MMMCCLXXVI', 'MMMCCLXXVII', 'MMMCCLXXVIII', 'MMMCCLXXIX', 'MMMCCLXXX', 'MMMCCLXXXI', 'MMMCCLXXXII', 'MMMCCLXXXIII', 'MMMCCLXXXIV', 'MMMCCLXXXV', 'MMMCCLXXXVI', 'MMMCCLXXXVII', 'MMMCCLXXXVIII', 'MMMCCLXXXIX', 'MMMCCXC', 'MMMCCXCI', 'MMMCCXCII', 'MMMCCXCIII', 'MMMCCXCIV', 'MMMCCXCV', 'MMMCCXCVI', 'MMMCCXCVII', 'MMMCCXCVIII', 'MMMCCXCIX', 'MMMCCC', 'MMMCCCI', 'MMMCCCII', 'MMMCCCIII', 'MMMCCCIV', 'MMMCCCV', 'MMMCCCVI', 'MMMCCCVII', 'MMMCCCVIII', 'MMMCCCIX', 'MMMCCCX', 'MMMCCCXI', 'MMMCCCXII', 'MMMCCCXIII', 'MMMCCCXIV', 'MMMCCCXV', 'MMMCCCXVI', 'MMMCCCXVII', 'MMMCCCXVIII', 'MMMCCCXIX', 'MMMCCCXX', 'MMMCCCXXI', 'MMMCCCXXII', 'MMMCCCXXIII', 'MMMCCCXXIV', 'MMMCCCXXV', 'MMMCCCXXVI', 'MMMCCCXXVII', 'MMMCCCXXVIII', 'MMMCCCXXIX', 'MMMCCCXXX', 'MMMCCCXXXI', 'MMMCCCXXXII', 'MMMCCCXXXIII', 'MMMCCCXXXIV', 'MMMCCCXXXV', 'MMMCCCXXXVI', 'MMMCCCXXXVII', 'MMMCCCXXXVIII', 'MMMCCCXXXIX', 'MMMCCCXL', 'MMMCCCXLI', 'MMMCCCXLII', 'MMMCCCXLIII', 'MMMCCCXLIV', 'MMMCCCXLV', 'MMMCCCXLVI', 'MMMCCCXLVII', 'MMMCCCXLVIII', 'MMMCCCXLIX', 'MMMCCCL', 'MMMCCCLI', 'MMMCCCLII', 'MMMCCCLIII', 'MMMCCCLIV', 'MMMCCCLV', 'MMMCCCLVI', 'MMMCCCLVII', 'MMMCCCLVIII', 'MMMCCCLIX', 'MMMCCCLX', 'MMMCCCLXI', 'MMMCCCLXII', 'MMMCCCLXIII', 'MMMCCCLXIV', 'MMMCCCLXV', 'MMMCCCLXVI', 'MMMCCCLXVII', 'MMMCCCLXVIII', 'MMMCCCLXIX', 'MMMCCCLXX', 'MMMCCCLXXI', 'MMMCCCLXXII', 'MMMCCCLXXIII', 'MMMCCCLXXIV', 'MMMCCCLXXV', 'MMMCCCLXXVI', 'MMMCCCLXXVII', 'MMMCCCLXXVIII', 'MMMCCCLXXIX', 'MMMCCCLXXX', 'MMMCCCLXXXI', 'MMMCCCLXXXII', 'MMMCCCLXXXIII', 'MMMCCCLXXXIV', 'MMMCCCLXXXV', 'MMMCCCLXXXVI', 'MMMCCCLXXXVII', 'MMMCCCLXXXVIII', 'MMMCCCLXXXIX', 'MMMCCCXC', 'MMMCCCXCI', 'MMMCCCXCII', 'MMMCCCXCIII', 'MMMCCCXCIV', 'MMMCCCXCV', 'MMMCCCXCVI', 'MMMCCCXCVII', 'MMMCCCXCVIII', 'MMMCCCXCIX', 'MMMCD', 'MMMCDI', 'MMMCDII', 'MMMCDIII', 'MMMCDIV', 'MMMCDV', 'MMMCDVI', 'MMMCDVII', 'MMMCDVIII', 'MMMCDIX', 'MMMCDX', 'MMMCDXI', 'MMMCDXII', 'MMMCDXIII', 'MMMCDXIV', 'MMMCDXV', 'MMMCDXVI', 'MMMCDXVII', 'MMMCDXVIII', 'MMMCDXIX', 'MMMCDXX', 'MMMCDXXI', 'MMMCDXXII', 'MMMCDXXIII', 'MMMCDXXIV', 'MMMCDXXV', 'MMMCDXXVI', 'MMMCDXXVII', 'MMMCDXXVIII', 'MMMCDXXIX', 'MMMCDXXX', 'MMMCDXXXI', 'MMMCDXXXII', 'MMMCDXXXIII', 'MMMCDXXXIV', 'MMMCDXXXV', 'MMMCDXXXVI', 'MMMCDXXXVII', 'MMMCDXXXVIII', 'MMMCDXXXIX', 'MMMCDXL', 'MMMCDXLI', 'MMMCDXLII', 'MMMCDXLIII', 'MMMCDXLIV', 'MMMCDXLV', 'MMMCDXLVI', 'MMMCDXLVII', 'MMMCDXLVIII', 'MMMCDXLIX', 'MMMCDL', 'MMMCDLI', 'MMMCDLII', 'MMMCDLIII', 'MMMCDLIV', 'MMMCDLV', 'MMMCDLVI', 'MMMCDLVII', 'MMMCDLVIII', 'MMMCDLIX', 'MMMCDLX', 'MMMCDLXI', 'MMMCDLXII', 'MMMCDLXIII', 'MMMCDLXIV', 'MMMCDLXV', 'MMMCDLXVI', 'MMMCDLXVII', 'MMMCDLXVIII', 'MMMCDLXIX', 'MMMCDLXX', 'MMMCDLXXI', 'MMMCDLXXII', 'MMMCDLXXIII', 'MMMCDLXXIV', 'MMMCDLXXV', 'MMMCDLXXVI', 'MMMCDLXXVII', 'MMMCDLXXVIII', 'MMMCDLXXIX', 'MMMCDLXXX', 'MMMCDLXXXI', 'MMMCDLXXXII', 'MMMCDLXXXIII', 'MMMCDLXXXIV', 'MMMCDLXXXV', 'MMMCDLXXXVI', 'MMMCDLXXXVII', 'MMMCDLXXXVIII', 'MMMCDLXXXIX', 'MMMCDXC', 'MMMCDXCI', 'MMMCDXCII', 'MMMCDXCIII', 'MMMCDXCIV', 'MMMCDXCV', 'MMMCDXCVI', 'MMMCDXCVII', 'MMMCDXCVIII', 'MMMCDXCIX', 'MMMD', 'MMMDI', 'MMMDII', 'MMMDIII', 'MMMDIV', 'MMMDV', 'MMMDVI', 'MMMDVII', 'MMMDVIII', 'MMMDIX', 'MMMDX', 'MMMDXI', 'MMMDXII', 'MMMDXIII', 'MMMDXIV', 'MMMDXV', 'MMMDXVI', 'MMMDXVII', 'MMMDXVIII', 'MMMDXIX', 'MMMDXX', 'MMMDXXI', 'MMMDXXII', 'MMMDXXIII', 'MMMDXXIV', 'MMMDXXV', 'MMMDXXVI', 'MMMDXXVII', 'MMMDXXVIII', 'MMMDXXIX', 'MMMDXXX', 'MMMDXXXI', 'MMMDXXXII', 'MMMDXXXIII', 'MMMDXXXIV', 'MMMDXXXV', 'MMMDXXXVI', 'MMMDXXXVII', 'MMMDXXXVIII', 'MMMDXXXIX', 'MMMDXL', 'MMMDXLI', 'MMMDXLII', 'MMMDXLIII', 'MMMDXLIV', 'MMMDXLV', 'MMMDXLVI', 'MMMDXLVII', 'MMMDXLVIII', 'MMMDXLIX', 'MMMDL', 'MMMDLI', 'MMMDLII', 'MMMDLIII', 'MMMDLIV', 'MMMDLV', 'MMMDLVI', 'MMMDLVII', 'MMMDLVIII', 'MMMDLIX', 'MMMDLX', 'MMMDLXI', 'MMMDLXII', 'MMMDLXIII', 'MMMDLXIV', 'MMMDLXV', 'MMMDLXVI', 'MMMDLXVII', 'MMMDLXVIII', 'MMMDLXIX', 'MMMDLXX', 'MMMDLXXI', 'MMMDLXXII', 'MMMDLXXIII', 'MMMDLXXIV', 'MMMDLXXV', 'MMMDLXXVI', 'MMMDLXXVII', 'MMMDLXXVIII', 'MMMDLXXIX', 'MMMDLXXX', 'MMMDLXXXI', 'MMMDLXXXII', 'MMMDLXXXIII', 'MMMDLXXXIV', 'MMMDLXXXV', 'MMMDLXXXVI', 'MMMDLXXXVII', 'MMMDLXXXVIII', 'MMMDLXXXIX', 'MMMDXC', 'MMMDXCI', 'MMMDXCII', 'MMMDXCIII', 'MMMDXCIV', 'MMMDXCV', 'MMMDXCVI', 'MMMDXCVII', 'MMMDXCVIII', 'MMMDXCIX', 'MMMDC', 'MMMDCI', 'MMMDCII', 'MMMDCIII', 'MMMDCIV', 'MMMDCV', 'MMMDCVI', 'MMMDCVII', 'MMMDCVIII', 'MMMDCIX', 'MMMDCX', 'MMMDCXI', 'MMMDCXII', 'MMMDCXIII', 'MMMDCXIV', 'MMMDCXV', 'MMMDCXVI', 'MMMDCXVII', 'MMMDCXVIII', 'MMMDCXIX', 'MMMDCXX', 'MMMDCXXI', 'MMMDCXXII', 'MMMDCXXIII', 'MMMDCXXIV', 'MMMDCXXV', 'MMMDCXXVI', 'MMMDCXXVII', 'MMMDCXXVIII', 'MMMDCXXIX', 'MMMDCXXX', 'MMMDCXXXI', 'MMMDCXXXII', 'MMMDCXXXIII', 'MMMDCXXXIV', 'MMMDCXXXV', 'MMMDCXXXVI', 'MMMDCXXXVII', 'MMMDCXXXVIII', 'MMMDCXXXIX', 'MMMDCXL', 'MMMDCXLI', 'MMMDCXLII', 'MMMDCXLIII', 'MMMDCXLIV', 'MMMDCXLV', 'MMMDCXLVI', 'MMMDCXLVII', 'MMMDCXLVIII', 'MMMDCXLIX', 'MMMDCL', 'MMMDCLI', 'MMMDCLII', 'MMMDCLIII', 'MMMDCLIV', 'MMMDCLV', 'MMMDCLVI', 'MMMDCLVII', 'MMMDCLVIII', 'MMMDCLIX', 'MMMDCLX', 'MMMDCLXI', 'MMMDCLXII', 'MMMDCLXIII', 'MMMDCLXIV', 'MMMDCLXV', 'MMMDCLXVI', 'MMMDCLXVII', 'MMMDCLXVIII', 'MMMDCLXIX', 'MMMDCLXX', 'MMMDCLXXI', 'MMMDCLXXII', 'MMMDCLXXIII', 'MMMDCLXXIV', 'MMMDCLXXV', 'MMMDCLXXVI', 'MMMDCLXXVII', 'MMMDCLXXVIII', 'MMMDCLXXIX', 'MMMDCLXXX', 'MMMDCLXXXI', 'MMMDCLXXXII', 'MMMDCLXXXIII', 'MMMDCLXXXIV', 'MMMDCLXXXV', 'MMMDCLXXXVI', 'MMMDCLXXXVII', 'MMMDCLXXXVIII', 'MMMDCLXXXIX', 'MMMDCXC', 'MMMDCXCI', 'MMMDCXCII', 'MMMDCXCIII', 'MMMDCXCIV', 'MMMDCXCV', 'MMMDCXCVI', 'MMMDCXCVII', 'MMMDCXCVIII', 'MMMDCXCIX', 'MMMDCC', 'MMMDCCI', 'MMMDCCII', 'MMMDCCIII', 'MMMDCCIV', 'MMMDCCV', 'MMMDCCVI', 'MMMDCCVII', 'MMMDCCVIII', 'MMMDCCIX', 'MMMDCCX', 'MMMDCCXI', 'MMMDCCXII', 'MMMDCCXIII', 'MMMDCCXIV', 'MMMDCCXV', 'MMMDCCXVI', 'MMMDCCXVII', 'MMMDCCXVIII', 'MMMDCCXIX', 'MMMDCCXX', 'MMMDCCXXI', 'MMMDCCXXII', 'MMMDCCXXIII', 'MMMDCCXXIV', 'MMMDCCXXV', 'MMMDCCXXVI', 'MMMDCCXXVII', 'MMMDCCXXVIII', 'MMMDCCXXIX', 'MMMDCCXXX', 'MMMDCCXXXI', 'MMMDCCXXXII', 'MMMDCCXXXIII', 'MMMDCCXXXIV', 'MMMDCCXXXV', 'MMMDCCXXXVI', 'MMMDCCXXXVII', 'MMMDCCXXXVIII', 'MMMDCCXXXIX', 'MMMDCCXL', 'MMMDCCXLI', 'MMMDCCXLII', 'MMMDCCXLIII', 'MMMDCCXLIV', 'MMMDCCXLV', 'MMMDCCXLVI', 'MMMDCCXLVII', 'MMMDCCXLVIII', 'MMMDCCXLIX', 'MMMDCCL', 'MMMDCCLI', 'MMMDCCLII', 'MMMDCCLIII', 'MMMDCCLIV', 'MMMDCCLV', 'MMMDCCLVI', 'MMMDCCLVII', 'MMMDCCLVIII', 'MMMDCCLIX', 'MMMDCCLX', 'MMMDCCLXI', 'MMMDCCLXII', 'MMMDCCLXIII', 'MMMDCCLXIV', 'MMMDCCLXV', 'MMMDCCLXVI', 'MMMDCCLXVII', 'MMMDCCLXVIII', 'MMMDCCLXIX', 'MMMDCCLXX', 'MMMDCCLXXI', 'MMMDCCLXXII', 'MMMDCCLXXIII', 'MMMDCCLXXIV', 'MMMDCCLXXV', 'MMMDCCLXXVI', 'MMMDCCLXXVII', 'MMMDCCLXXVIII', 'MMMDCCLXXIX', 'MMMDCCLXXX', 'MMMDCCLXXXI', 'MMMDCCLXXXII', 'MMMDCCLXXXIII', 'MMMDCCLXXXIV', 'MMMDCCLXXXV', 'MMMDCCLXXXVI', 'MMMDCCLXXXVII', 'MMMDCCLXXXVIII', 'MMMDCCLXXXIX', 'MMMDCCXC', 'MMMDCCXCI', 'MMMDCCXCII', 'MMMDCCXCIII', 'MMMDCCXCIV', 'MMMDCCXCV', 'MMMDCCXCVI', 'MMMDCCXCVII', 'MMMDCCXCVIII', 'MMMDCCXCIX', 'MMMDCCC', 'MMMDCCCI', 'MMMDCCCII', 'MMMDCCCIII', 'MMMDCCCIV', 'MMMDCCCV', 'MMMDCCCVI', 'MMMDCCCVII', 'MMMDCCCVIII', 'MMMDCCCIX', 'MMMDCCCX', 'MMMDCCCXI', 'MMMDCCCXII', 'MMMDCCCXIII', 'MMMDCCCXIV', 'MMMDCCCXV', 'MMMDCCCXVI', 'MMMDCCCXVII', 'MMMDCCCXVIII', 'MMMDCCCXIX', 'MMMDCCCXX', 'MMMDCCCXXI', 'MMMDCCCXXII', 'MMMDCCCXXIII', 'MMMDCCCXXIV', 'MMMDCCCXXV', 'MMMDCCCXXVI', 'MMMDCCCXXVII', 'MMMDCCCXXVIII', 'MMMDCCCXXIX', 'MMMDCCCXXX', 'MMMDCCCXXXI', 'MMMDCCCXXXII', 'MMMDCCCXXXIII', 'MMMDCCCXXXIV', 'MMMDCCCXXXV', 'MMMDCCCXXXVI', 'MMMDCCCXXXVII', 'MMMDCCCXXXVIII', 'MMMDCCCXXXIX', 'MMMDCCCXL', 'MMMDCCCXLI', 'MMMDCCCXLII', 'MMMDCCCXLIII', 'MMMDCCCXLIV', 'MMMDCCCXLV', 'MMMDCCCXLVI', 'MMMDCCCXLVII', 'MMMDCCCXLVIII', 'MMMDCCCXLIX', 'MMMDCCCL', 'MMMDCCCLI', 'MMMDCCCLII', 'MMMDCCCLIII', 'MMMDCCCLIV', 'MMMDCCCLV', 'MMMDCCCLVI', 'MMMDCCCLVII', 'MMMDCCCLVIII', 'MMMDCCCLIX', 'MMMDCCCLX', 'MMMDCCCLXI', 'MMMDCCCLXII', 'MMMDCCCLXIII', 'MMMDCCCLXIV', 'MMMDCCCLXV', 'MMMDCCCLXVI', 'MMMDCCCLXVII', 'MMMDCCCLXVIII', 'MMMDCCCLXIX', 'MMMDCCCLXX', 'MMMDCCCLXXI', 'MMMDCCCLXXII', 'MMMDCCCLXXIII', 'MMMDCCCLXXIV', 'MMMDCCCLXXV', 'MMMDCCCLXXVI', 'MMMDCCCLXXVII', 'MMMDCCCLXXVIII', 'MMMDCCCLXXIX', 'MMMDCCCLXXX', 'MMMDCCCLXXXI', 'MMMDCCCLXXXII', 'MMMDCCCLXXXIII', 'MMMDCCCLXXXIV', 'MMMDCCCLXXXV', 'MMMDCCCLXXXVI', 'MMMDCCCLXXXVII', 'MMMDCCCLXXXVIII', 'MMMDCCCLXXXIX', 'MMMDCCCXC', 'MMMDCCCXCI', 'MMMDCCCXCII', 'MMMDCCCXCIII', 'MMMDCCCXCIV', 'MMMDCCCXCV', 'MMMDCCCXCVI', 'MMMDCCCXCVII', 'MMMDCCCXCVIII', 'MMMDCCCXCIX', 'MMMCM', 'MMMCMI', 'MMMCMII', 'MMMCMIII', 'MMMCMIV', 'MMMCMV', 'MMMCMVI', 'MMMCMVII', 'MMMCMVIII', 'MMMCMIX', 'MMMCMX', 'MMMCMXI', 'MMMCMXII', 'MMMCMXIII', 'MMMCMXIV', 'MMMCMXV', 'MMMCMXVI', 'MMMCMXVII', 'MMMCMXVIII', 'MMMCMXIX', 'MMMCMXX', 'MMMCMXXI', 'MMMCMXXII', 'MMMCMXXIII', 'MMMCMXXIV', 'MMMCMXXV', 'MMMCMXXVI', 'MMMCMXXVII', 'MMMCMXXVIII', 'MMMCMXXIX', 'MMMCMXXX', 'MMMCMXXXI', 'MMMCMXXXII', 'MMMCMXXXIII', 'MMMCMXXXIV', 'MMMCMXXXV', 'MMMCMXXXVI', 'MMMCMXXXVII', 'MMMCMXXXVIII', 'MMMCMXXXIX', 'MMMCMXL', 'MMMCMXLI', 'MMMCMXLII', 'MMMCMXLIII', 'MMMCMXLIV', 'MMMCMXLV', 'MMMCMXLVI', 'MMMCMXLVII', 'MMMCMXLVIII', 'MMMCMXLIX', 'MMMCML', 'MMMCMLI', 'MMMCMLII', 'MMMCMLIII', 'MMMCMLIV', 'MMMCMLV', 'MMMCMLVI', 'MMMCMLVII', 'MMMCMLVIII', 'MMMCMLIX', 'MMMCMLX', 'MMMCMLXI', 'MMMCMLXII', 'MMMCMLXIII', 'MMMCMLXIV', 'MMMCMLXV', 'MMMCMLXVI', 'MMMCMLXVII', 'MMMCMLXVIII', 'MMMCMLXIX', 'MMMCMLXX', 'MMMCMLXXI', 'MMMCMLXXII', 'MMMCMLXXIII', 'MMMCMLXXIV', 'MMMCMLXXV', 'MMMCMLXXVI', 'MMMCMLXXVII', 'MMMCMLXXVIII', 'MMMCMLXXIX', 'MMMCMLXXX', 'MMMCMLXXXI', 'MMMCMLXXXII', 'MMMCMLXXXIII', 'MMMCMLXXXIV', 'MMMCMLXXXV', 'MMMCMLXXXVI', 'MMMCMLXXXVII', 'MMMCMLXXXVIII', 'MMMCMLXXXIX', 'MMMCMXC', 'MMMCMXCI', 'MMMCMXCII', 'MMMCMXCIII', 'MMMCMXCIV', 'MMMCMXCV', 'MMMCMXCVI', 'MMMCMXCVII', 'MMMCMXCVIII', 'MMMCMXCIX', ] -const mode1 = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX', 'XXI', 'XXII', 'XXIII', 'XXIV', 'XXV', 'XXVI', 'XXVII', 'XXVIII', 'XXIX', 'XXX', 'XXXI', 'XXXII', 'XXXIII', 'XXXIV', 'XXXV', 'XXXVI', 'XXXVII', 'XXXVIII', 'XXXIX', 'XL', 'XLI', 'XLII', 'XLIII', 'XLIV', 'VL', 'VLI', 'VLII', 'VLIII', 'VLIV', 'L', 'LI', 'LII', 'LIII', 'LIV', 'LV', 'LVI', 'LVII', 'LVIII', 'LIX', 'LX', 'LXI', 'LXII', 'LXIII', 'LXIV', 'LXV', 'LXVI', 'LXVII', 'LXVIII', 'LXIX', 'LXX', 'LXXI', 'LXXII', 'LXXIII', 'LXXIV', 'LXXV', 'LXXVI', 'LXXVII', 'LXXVIII', 'LXXIX', 'LXXX', 'LXXXI', 'LXXXII', 'LXXXIII', 'LXXXIV', 'LXXXV', 'LXXXVI', 'LXXXVII', 'LXXXVIII', 'LXXXIX', 'XC', 'XCI', 'XCII', 'XCIII', 'XCIV', 'VC', 'VCI', 'VCII', 'VCIII', 'VCIV', 'C', 'CI', 'CII', 'CIII', 'CIV', 'CV', 'CVI', 'CVII', 'CVIII', 'CIX', 'CX', 'CXI', 'CXII', 'CXIII', 'CXIV', 'CXV', 'CXVI', 'CXVII', 'CXVIII', 'CXIX', 'CXX', 'CXXI', 'CXXII', 'CXXIII', 'CXXIV', 'CXXV', 'CXXVI', 'CXXVII', 'CXXVIII', 'CXXIX', 'CXXX', 'CXXXI', 'CXXXII', 'CXXXIII', 'CXXXIV', 'CXXXV', 'CXXXVI', 'CXXXVII', 'CXXXVIII', 'CXXXIX', 'CXL', 'CXLI', 'CXLII', 'CXLIII', 'CXLIV', 'CVL', 'CVLI', 'CVLII', 'CVLIII', 'CVLIV', 'CL', 'CLI', 'CLII', 'CLIII', 'CLIV', 'CLV', 'CLVI', 'CLVII', 'CLVIII', 'CLIX', 'CLX', 'CLXI', 'CLXII', 'CLXIII', 'CLXIV', 'CLXV', 'CLXVI', 'CLXVII', 'CLXVIII', 'CLXIX', 'CLXX', 'CLXXI', 'CLXXII', 'CLXXIII', 'CLXXIV', 'CLXXV', 'CLXXVI', 'CLXXVII', 'CLXXVIII', 'CLXXIX', 'CLXXX', 'CLXXXI', 'CLXXXII', 'CLXXXIII', 'CLXXXIV', 'CLXXXV', 'CLXXXVI', 'CLXXXVII', 'CLXXXVIII', 'CLXXXIX', 'CXC', 'CXCI', 'CXCII', 'CXCIII', 'CXCIV', 'CVC', 'CVCI', 'CVCII', 'CVCIII', 'CVCIV', 'CC', 'CCI', 'CCII', 'CCIII', 'CCIV', 'CCV', 'CCVI', 'CCVII', 'CCVIII', 'CCIX', 'CCX', 'CCXI', 'CCXII', 'CCXIII', 'CCXIV', 'CCXV', 'CCXVI', 'CCXVII', 'CCXVIII', 'CCXIX', 'CCXX', 'CCXXI', 'CCXXII', 'CCXXIII', 'CCXXIV', 'CCXXV', 'CCXXVI', 'CCXXVII', 'CCXXVIII', 'CCXXIX', 'CCXXX', 'CCXXXI', 'CCXXXII', 'CCXXXIII', 'CCXXXIV', 'CCXXXV', 'CCXXXVI', 'CCXXXVII', 'CCXXXVIII', 'CCXXXIX', 'CCXL', 'CCXLI', 'CCXLII', 'CCXLIII', 'CCXLIV', 'CCVL', 'CCVLI', 'CCVLII', 'CCVLIII', 'CCVLIV', 'CCL', 'CCLI', 'CCLII', 'CCLIII', 'CCLIV', 'CCLV', 'CCLVI', 'CCLVII', 'CCLVIII', 'CCLIX', 'CCLX', 'CCLXI', 'CCLXII', 'CCLXIII', 'CCLXIV', 'CCLXV', 'CCLXVI', 'CCLXVII', 'CCLXVIII', 'CCLXIX', 'CCLXX', 'CCLXXI', 'CCLXXII', 'CCLXXIII', 'CCLXXIV', 'CCLXXV', 'CCLXXVI', 'CCLXXVII', 'CCLXXVIII', 'CCLXXIX', 'CCLXXX', 'CCLXXXI', 'CCLXXXII', 'CCLXXXIII', 'CCLXXXIV', 'CCLXXXV', 'CCLXXXVI', 'CCLXXXVII', 'CCLXXXVIII', 'CCLXXXIX', 'CCXC', 'CCXCI', 'CCXCII', 'CCXCIII', 'CCXCIV', 'CCVC', 'CCVCI', 'CCVCII', 'CCVCIII', 'CCVCIV', 'CCC', 'CCCI', 'CCCII', 'CCCIII', 'CCCIV', 'CCCV', 'CCCVI', 'CCCVII', 'CCCVIII', 'CCCIX', 'CCCX', 'CCCXI', 'CCCXII', 'CCCXIII', 'CCCXIV', 'CCCXV', 'CCCXVI', 'CCCXVII', 'CCCXVIII', 'CCCXIX', 'CCCXX', 'CCCXXI', 'CCCXXII', 'CCCXXIII', 'CCCXXIV', 'CCCXXV', 'CCCXXVI', 'CCCXXVII', 'CCCXXVIII', 'CCCXXIX', 'CCCXXX', 'CCCXXXI', 'CCCXXXII', 'CCCXXXIII', 'CCCXXXIV', 'CCCXXXV', 'CCCXXXVI', 'CCCXXXVII', 'CCCXXXVIII', 'CCCXXXIX', 'CCCXL', 'CCCXLI', 'CCCXLII', 'CCCXLIII', 'CCCXLIV', 'CCCVL', 'CCCVLI', 'CCCVLII', 'CCCVLIII', 'CCCVLIV', 'CCCL', 'CCCLI', 'CCCLII', 'CCCLIII', 'CCCLIV', 'CCCLV', 'CCCLVI', 'CCCLVII', 'CCCLVIII', 'CCCLIX', 'CCCLX', 'CCCLXI', 'CCCLXII', 'CCCLXIII', 'CCCLXIV', 'CCCLXV', 'CCCLXVI', 'CCCLXVII', 'CCCLXVIII', 'CCCLXIX', 'CCCLXX', 'CCCLXXI', 'CCCLXXII', 'CCCLXXIII', 'CCCLXXIV', 'CCCLXXV', 'CCCLXXVI', 'CCCLXXVII', 'CCCLXXVIII', 'CCCLXXIX', 'CCCLXXX', 'CCCLXXXI', 'CCCLXXXII', 'CCCLXXXIII', 'CCCLXXXIV', 'CCCLXXXV', 'CCCLXXXVI', 'CCCLXXXVII', 'CCCLXXXVIII', 'CCCLXXXIX', 'CCCXC', 'CCCXCI', 'CCCXCII', 'CCCXCIII', 'CCCXCIV', 'CCCVC', 'CCCVCI', 'CCCVCII', 'CCCVCIII', 'CCCVCIV', 'CD', 'CDI', 'CDII', 'CDIII', 'CDIV', 'CDV', 'CDVI', 'CDVII', 'CDVIII', 'CDIX', 'CDX', 'CDXI', 'CDXII', 'CDXIII', 'CDXIV', 'CDXV', 'CDXVI', 'CDXVII', 'CDXVIII', 'CDXIX', 'CDXX', 'CDXXI', 'CDXXII', 'CDXXIII', 'CDXXIV', 'CDXXV', 'CDXXVI', 'CDXXVII', 'CDXXVIII', 'CDXXIX', 'CDXXX', 'CDXXXI', 'CDXXXII', 'CDXXXIII', 'CDXXXIV', 'CDXXXV', 'CDXXXVI', 'CDXXXVII', 'CDXXXVIII', 'CDXXXIX', 'CDXL', 'CDXLI', 'CDXLII', 'CDXLIII', 'CDXLIV', 'CDVL', 'CDVLI', 'CDVLII', 'CDVLIII', 'CDVLIV', 'LD', 'LDI', 'LDII', 'LDIII', 'LDIV', 'LDV', 'LDVI', 'LDVII', 'LDVIII', 'LDIX', 'LDX', 'LDXI', 'LDXII', 'LDXIII', 'LDXIV', 'LDXV', 'LDXVI', 'LDXVII', 'LDXVIII', 'LDXIX', 'LDXX', 'LDXXI', 'LDXXII', 'LDXXIII', 'LDXXIV', 'LDXXV', 'LDXXVI', 'LDXXVII', 'LDXXVIII', 'LDXXIX', 'LDXXX', 'LDXXXI', 'LDXXXII', 'LDXXXIII', 'LDXXXIV', 'LDXXXV', 'LDXXXVI', 'LDXXXVII', 'LDXXXVIII', 'LDXXXIX', 'LDXL', 'LDXLI', 'LDXLII', 'LDXLIII', 'LDXLIV', 'LDVL', 'LDVLI', 'LDVLII', 'LDVLIII', 'LDVLIV', 'D', 'DI', 'DII', 'DIII', 'DIV', 'DV', 'DVI', 'DVII', 'DVIII', 'DIX', 'DX', 'DXI', 'DXII', 'DXIII', 'DXIV', 'DXV', 'DXVI', 'DXVII', 'DXVIII', 'DXIX', 'DXX', 'DXXI', 'DXXII', 'DXXIII', 'DXXIV', 'DXXV', 'DXXVI', 'DXXVII', 'DXXVIII', 'DXXIX', 'DXXX', 'DXXXI', 'DXXXII', 'DXXXIII', 'DXXXIV', 'DXXXV', 'DXXXVI', 'DXXXVII', 'DXXXVIII', 'DXXXIX', 'DXL', 'DXLI', 'DXLII', 'DXLIII', 'DXLIV', 'DVL', 'DVLI', 'DVLII', 'DVLIII', 'DVLIV', 'DL', 'DLI', 'DLII', 'DLIII', 'DLIV', 'DLV', 'DLVI', 'DLVII', 'DLVIII', 'DLIX', 'DLX', 'DLXI', 'DLXII', 'DLXIII', 'DLXIV', 'DLXV', 'DLXVI', 'DLXVII', 'DLXVIII', 'DLXIX', 'DLXX', 'DLXXI', 'DLXXII', 'DLXXIII', 'DLXXIV', 'DLXXV', 'DLXXVI', 'DLXXVII', 'DLXXVIII', 'DLXXIX', 'DLXXX', 'DLXXXI', 'DLXXXII', 'DLXXXIII', 'DLXXXIV', 'DLXXXV', 'DLXXXVI', 'DLXXXVII', 'DLXXXVIII', 'DLXXXIX', 'DXC', 'DXCI', 'DXCII', 'DXCIII', 'DXCIV', 'DVC', 'DVCI', 'DVCII', 'DVCIII', 'DVCIV', 'DC', 'DCI', 'DCII', 'DCIII', 'DCIV', 'DCV', 'DCVI', 'DCVII', 'DCVIII', 'DCIX', 'DCX', 'DCXI', 'DCXII', 'DCXIII', 'DCXIV', 'DCXV', 'DCXVI', 'DCXVII', 'DCXVIII', 'DCXIX', 'DCXX', 'DCXXI', 'DCXXII', 'DCXXIII', 'DCXXIV', 'DCXXV', 'DCXXVI', 'DCXXVII', 'DCXXVIII', 'DCXXIX', 'DCXXX', 'DCXXXI', 'DCXXXII', 'DCXXXIII', 'DCXXXIV', 'DCXXXV', 'DCXXXVI', 'DCXXXVII', 'DCXXXVIII', 'DCXXXIX', 'DCXL', 'DCXLI', 'DCXLII', 'DCXLIII', 'DCXLIV', 'DCVL', 'DCVLI', 'DCVLII', 'DCVLIII', 'DCVLIV', 'DCL', 'DCLI', 'DCLII', 'DCLIII', 'DCLIV', 'DCLV', 'DCLVI', 'DCLVII', 'DCLVIII', 'DCLIX', 'DCLX', 'DCLXI', 'DCLXII', 'DCLXIII', 'DCLXIV', 'DCLXV', 'DCLXVI', 'DCLXVII', 'DCLXVIII', 'DCLXIX', 'DCLXX', 'DCLXXI', 'DCLXXII', 'DCLXXIII', 'DCLXXIV', 'DCLXXV', 'DCLXXVI', 'DCLXXVII', 'DCLXXVIII', 'DCLXXIX', 'DCLXXX', 'DCLXXXI', 'DCLXXXII', 'DCLXXXIII', 'DCLXXXIV', 'DCLXXXV', 'DCLXXXVI', 'DCLXXXVII', 'DCLXXXVIII', 'DCLXXXIX', 'DCXC', 'DCXCI', 'DCXCII', 'DCXCIII', 'DCXCIV', 'DCVC', 'DCVCI', 'DCVCII', 'DCVCIII', 'DCVCIV', 'DCC', 'DCCI', 'DCCII', 'DCCIII', 'DCCIV', 'DCCV', 'DCCVI', 'DCCVII', 'DCCVIII', 'DCCIX', 'DCCX', 'DCCXI', 'DCCXII', 'DCCXIII', 'DCCXIV', 'DCCXV', 'DCCXVI', 'DCCXVII', 'DCCXVIII', 'DCCXIX', 'DCCXX', 'DCCXXI', 'DCCXXII', 'DCCXXIII', 'DCCXXIV', 'DCCXXV', 'DCCXXVI', 'DCCXXVII', 'DCCXXVIII', 'DCCXXIX', 'DCCXXX', 'DCCXXXI', 'DCCXXXII', 'DCCXXXIII', 'DCCXXXIV', 'DCCXXXV', 'DCCXXXVI', 'DCCXXXVII', 'DCCXXXVIII', 'DCCXXXIX', 'DCCXL', 'DCCXLI', 'DCCXLII', 'DCCXLIII', 'DCCXLIV', 'DCCVL', 'DCCVLI', 'DCCVLII', 'DCCVLIII', 'DCCVLIV', 'DCCL', 'DCCLI', 'DCCLII', 'DCCLIII', 'DCCLIV', 'DCCLV', 'DCCLVI', 'DCCLVII', 'DCCLVIII', 'DCCLIX', 'DCCLX', 'DCCLXI', 'DCCLXII', 'DCCLXIII', 'DCCLXIV', 'DCCLXV', 'DCCLXVI', 'DCCLXVII', 'DCCLXVIII', 'DCCLXIX', 'DCCLXX', 'DCCLXXI', 'DCCLXXII', 'DCCLXXIII', 'DCCLXXIV', 'DCCLXXV', 'DCCLXXVI', 'DCCLXXVII', 'DCCLXXVIII', 'DCCLXXIX', 'DCCLXXX', 'DCCLXXXI', 'DCCLXXXII', 'DCCLXXXIII', 'DCCLXXXIV', 'DCCLXXXV', 'DCCLXXXVI', 'DCCLXXXVII', 'DCCLXXXVIII', 'DCCLXXXIX', 'DCCXC', 'DCCXCI', 'DCCXCII', 'DCCXCIII', 'DCCXCIV', 'DCCVC', 'DCCVCI', 'DCCVCII', 'DCCVCIII', 'DCCVCIV', 'DCCC', 'DCCCI', 'DCCCII', 'DCCCIII', 'DCCCIV', 'DCCCV', 'DCCCVI', 'DCCCVII', 'DCCCVIII', 'DCCCIX', 'DCCCX', 'DCCCXI', 'DCCCXII', 'DCCCXIII', 'DCCCXIV', 'DCCCXV', 'DCCCXVI', 'DCCCXVII', 'DCCCXVIII', 'DCCCXIX', 'DCCCXX', 'DCCCXXI', 'DCCCXXII', 'DCCCXXIII', 'DCCCXXIV', 'DCCCXXV', 'DCCCXXVI', 'DCCCXXVII', 'DCCCXXVIII', 'DCCCXXIX', 'DCCCXXX', 'DCCCXXXI', 'DCCCXXXII', 'DCCCXXXIII', 'DCCCXXXIV', 'DCCCXXXV', 'DCCCXXXVI', 'DCCCXXXVII', 'DCCCXXXVIII', 'DCCCXXXIX', 'DCCCXL', 'DCCCXLI', 'DCCCXLII', 'DCCCXLIII', 'DCCCXLIV', 'DCCCVL', 'DCCCVLI', 'DCCCVLII', 'DCCCVLIII', 'DCCCVLIV', 'DCCCL', 'DCCCLI', 'DCCCLII', 'DCCCLIII', 'DCCCLIV', 'DCCCLV', 'DCCCLVI', 'DCCCLVII', 'DCCCLVIII', 'DCCCLIX', 'DCCCLX', 'DCCCLXI', 'DCCCLXII', 'DCCCLXIII', 'DCCCLXIV', 'DCCCLXV', 'DCCCLXVI', 'DCCCLXVII', 'DCCCLXVIII', 'DCCCLXIX', 'DCCCLXX', 'DCCCLXXI', 'DCCCLXXII', 'DCCCLXXIII', 'DCCCLXXIV', 'DCCCLXXV', 'DCCCLXXVI', 'DCCCLXXVII', 'DCCCLXXVIII', 'DCCCLXXIX', 'DCCCLXXX', 'DCCCLXXXI', 'DCCCLXXXII', 'DCCCLXXXIII', 'DCCCLXXXIV', 'DCCCLXXXV', 'DCCCLXXXVI', 'DCCCLXXXVII', 'DCCCLXXXVIII', 'DCCCLXXXIX', 'DCCCXC', 'DCCCXCI', 'DCCCXCII', 'DCCCXCIII', 'DCCCXCIV', 'DCCCVC', 'DCCCVCI', 'DCCCVCII', 'DCCCVCIII', 'DCCCVCIV', 'CM', 'CMI', 'CMII', 'CMIII', 'CMIV', 'CMV', 'CMVI', 'CMVII', 'CMVIII', 'CMIX', 'CMX', 'CMXI', 'CMXII', 'CMXIII', 'CMXIV', 'CMXV', 'CMXVI', 'CMXVII', 'CMXVIII', 'CMXIX', 'CMXX', 'CMXXI', 'CMXXII', 'CMXXIII', 'CMXXIV', 'CMXXV', 'CMXXVI', 'CMXXVII', 'CMXXVIII', 'CMXXIX', 'CMXXX', 'CMXXXI', 'CMXXXII', 'CMXXXIII', 'CMXXXIV', 'CMXXXV', 'CMXXXVI', 'CMXXXVII', 'CMXXXVIII', 'CMXXXIX', 'CMXL', 'CMXLI', 'CMXLII', 'CMXLIII', 'CMXLIV', 'CMVL', 'CMVLI', 'CMVLII', 'CMVLIII', 'CMVLIV', 'LM', 'LMI', 'LMII', 'LMIII', 'LMIV', 'LMV', 'LMVI', 'LMVII', 'LMVIII', 'LMIX', 'LMX', 'LMXI', 'LMXII', 'LMXIII', 'LMXIV', 'LMXV', 'LMXVI', 'LMXVII', 'LMXVIII', 'LMXIX', 'LMXX', 'LMXXI', 'LMXXII', 'LMXXIII', 'LMXXIV', 'LMXXV', 'LMXXVI', 'LMXXVII', 'LMXXVIII', 'LMXXIX', 'LMXXX', 'LMXXXI', 'LMXXXII', 'LMXXXIII', 'LMXXXIV', 'LMXXXV', 'LMXXXVI', 'LMXXXVII', 'LMXXXVIII', 'LMXXXIX', 'LMXL', 'LMXLI', 'LMXLII', 'LMXLIII', 'LMXLIV', 'LMVL', 'LMVLI', 'LMVLII', 'LMVLIII', 'LMVLIV', 'M', 'MI', 'MII', 'MIII', 'MIV', 'MV', 'MVI', 'MVII', 'MVIII', 'MIX', 'MX', 'MXI', 'MXII', 'MXIII', 'MXIV', 'MXV', 'MXVI', 'MXVII', 'MXVIII', 'MXIX', 'MXX', 'MXXI', 'MXXII', 'MXXIII', 'MXXIV', 'MXXV', 'MXXVI', 'MXXVII', 'MXXVIII', 'MXXIX', 'MXXX', 'MXXXI', 'MXXXII', 'MXXXIII', 'MXXXIV', 'MXXXV', 'MXXXVI', 'MXXXVII', 'MXXXVIII', 'MXXXIX', 'MXL', 'MXLI', 'MXLII', 'MXLIII', 'MXLIV', 'MVL', 'MVLI', 'MVLII', 'MVLIII', 'MVLIV', 'ML', 'MLI', 'MLII', 'MLIII', 'MLIV', 'MLV', 'MLVI', 'MLVII', 'MLVIII', 'MLIX', 'MLX', 'MLXI', 'MLXII', 'MLXIII', 'MLXIV', 'MLXV', 'MLXVI', 'MLXVII', 'MLXVIII', 'MLXIX', 'MLXX', 'MLXXI', 'MLXXII', 'MLXXIII', 'MLXXIV', 'MLXXV', 'MLXXVI', 'MLXXVII', 'MLXXVIII', 'MLXXIX', 'MLXXX', 'MLXXXI', 'MLXXXII', 'MLXXXIII', 'MLXXXIV', 'MLXXXV', 'MLXXXVI', 'MLXXXVII', 'MLXXXVIII', 'MLXXXIX', 'MXC', 'MXCI', 'MXCII', 'MXCIII', 'MXCIV', 'MVC', 'MVCI', 'MVCII', 'MVCIII', 'MVCIV', 'MC', 'MCI', 'MCII', 'MCIII', 'MCIV', 'MCV', 'MCVI', 'MCVII', 'MCVIII', 'MCIX', 'MCX', 'MCXI', 'MCXII', 'MCXIII', 'MCXIV', 'MCXV', 'MCXVI', 'MCXVII', 'MCXVIII', 'MCXIX', 'MCXX', 'MCXXI', 'MCXXII', 'MCXXIII', 'MCXXIV', 'MCXXV', 'MCXXVI', 'MCXXVII', 'MCXXVIII', 'MCXXIX', 'MCXXX', 'MCXXXI', 'MCXXXII', 'MCXXXIII', 'MCXXXIV', 'MCXXXV', 'MCXXXVI', 'MCXXXVII', 'MCXXXVIII', 'MCXXXIX', 'MCXL', 'MCXLI', 'MCXLII', 'MCXLIII', 'MCXLIV', 'MCVL', 'MCVLI', 'MCVLII', 'MCVLIII', 'MCVLIV', 'MCL', 'MCLI', 'MCLII', 'MCLIII', 'MCLIV', 'MCLV', 'MCLVI', 'MCLVII', 'MCLVIII', 'MCLIX', 'MCLX', 'MCLXI', 'MCLXII', 'MCLXIII', 'MCLXIV', 'MCLXV', 'MCLXVI', 'MCLXVII', 'MCLXVIII', 'MCLXIX', 'MCLXX', 'MCLXXI', 'MCLXXII', 'MCLXXIII', 'MCLXXIV', 'MCLXXV', 'MCLXXVI', 'MCLXXVII', 'MCLXXVIII', 'MCLXXIX', 'MCLXXX', 'MCLXXXI', 'MCLXXXII', 'MCLXXXIII', 'MCLXXXIV', 'MCLXXXV', 'MCLXXXVI', 'MCLXXXVII', 'MCLXXXVIII', 'MCLXXXIX', 'MCXC', 'MCXCI', 'MCXCII', 'MCXCIII', 'MCXCIV', 'MCVC', 'MCVCI', 'MCVCII', 'MCVCIII', 'MCVCIV', 'MCC', 'MCCI', 'MCCII', 'MCCIII', 'MCCIV', 'MCCV', 'MCCVI', 'MCCVII', 'MCCVIII', 'MCCIX', 'MCCX', 'MCCXI', 'MCCXII', 'MCCXIII', 'MCCXIV', 'MCCXV', 'MCCXVI', 'MCCXVII', 'MCCXVIII', 'MCCXIX', 'MCCXX', 'MCCXXI', 'MCCXXII', 'MCCXXIII', 'MCCXXIV', 'MCCXXV', 'MCCXXVI', 'MCCXXVII', 'MCCXXVIII', 'MCCXXIX', 'MCCXXX', 'MCCXXXI', 'MCCXXXII', 'MCCXXXIII', 'MCCXXXIV', 'MCCXXXV', 'MCCXXXVI', 'MCCXXXVII', 'MCCXXXVIII', 'MCCXXXIX', 'MCCXL', 'MCCXLI', 'MCCXLII', 'MCCXLIII', 'MCCXLIV', 'MCCVL', 'MCCVLI', 'MCCVLII', 'MCCVLIII', 'MCCVLIV', 'MCCL', 'MCCLI', 'MCCLII', 'MCCLIII', 'MCCLIV', 'MCCLV', 'MCCLVI', 'MCCLVII', 'MCCLVIII', 'MCCLIX', 'MCCLX', 'MCCLXI', 'MCCLXII', 'MCCLXIII', 'MCCLXIV', 'MCCLXV', 'MCCLXVI', 'MCCLXVII', 'MCCLXVIII', 'MCCLXIX', 'MCCLXX', 'MCCLXXI', 'MCCLXXII', 'MCCLXXIII', 'MCCLXXIV', 'MCCLXXV', 'MCCLXXVI', 'MCCLXXVII', 'MCCLXXVIII', 'MCCLXXIX', 'MCCLXXX', 'MCCLXXXI', 'MCCLXXXII', 'MCCLXXXIII', 'MCCLXXXIV', 'MCCLXXXV', 'MCCLXXXVI', 'MCCLXXXVII', 'MCCLXXXVIII', 'MCCLXXXIX', 'MCCXC', 'MCCXCI', 'MCCXCII', 'MCCXCIII', 'MCCXCIV', 'MCCVC', 'MCCVCI', 'MCCVCII', 'MCCVCIII', 'MCCVCIV', 'MCCC', 'MCCCI', 'MCCCII', 'MCCCIII', 'MCCCIV', 'MCCCV', 'MCCCVI', 'MCCCVII', 'MCCCVIII', 'MCCCIX', 'MCCCX', 'MCCCXI', 'MCCCXII', 'MCCCXIII', 'MCCCXIV', 'MCCCXV', 'MCCCXVI', 'MCCCXVII', 'MCCCXVIII', 'MCCCXIX', 'MCCCXX', 'MCCCXXI', 'MCCCXXII', 'MCCCXXIII', 'MCCCXXIV', 'MCCCXXV', 'MCCCXXVI', 'MCCCXXVII', 'MCCCXXVIII', 'MCCCXXIX', 'MCCCXXX', 'MCCCXXXI', 'MCCCXXXII', 'MCCCXXXIII', 'MCCCXXXIV', 'MCCCXXXV', 'MCCCXXXVI', 'MCCCXXXVII', 'MCCCXXXVIII', 'MCCCXXXIX', 'MCCCXL', 'MCCCXLI', 'MCCCXLII', 'MCCCXLIII', 'MCCCXLIV', 'MCCCVL', 'MCCCVLI', 'MCCCVLII', 'MCCCVLIII', 'MCCCVLIV', 'MCCCL', 'MCCCLI', 'MCCCLII', 'MCCCLIII', 'MCCCLIV', 'MCCCLV', 'MCCCLVI', 'MCCCLVII', 'MCCCLVIII', 'MCCCLIX', 'MCCCLX', 'MCCCLXI', 'MCCCLXII', 'MCCCLXIII', 'MCCCLXIV', 'MCCCLXV', 'MCCCLXVI', 'MCCCLXVII', 'MCCCLXVIII', 'MCCCLXIX', 'MCCCLXX', 'MCCCLXXI', 'MCCCLXXII', 'MCCCLXXIII', 'MCCCLXXIV', 'MCCCLXXV', 'MCCCLXXVI', 'MCCCLXXVII', 'MCCCLXXVIII', 'MCCCLXXIX', 'MCCCLXXX', 'MCCCLXXXI', 'MCCCLXXXII', 'MCCCLXXXIII', 'MCCCLXXXIV', 'MCCCLXXXV', 'MCCCLXXXVI', 'MCCCLXXXVII', 'MCCCLXXXVIII', 'MCCCLXXXIX', 'MCCCXC', 'MCCCXCI', 'MCCCXCII', 'MCCCXCIII', 'MCCCXCIV', 'MCCCVC', 'MCCCVCI', 'MCCCVCII', 'MCCCVCIII', 'MCCCVCIV', 'MCD', 'MCDI', 'MCDII', 'MCDIII', 'MCDIV', 'MCDV', 'MCDVI', 'MCDVII', 'MCDVIII', 'MCDIX', 'MCDX', 'MCDXI', 'MCDXII', 'MCDXIII', 'MCDXIV', 'MCDXV', 'MCDXVI', 'MCDXVII', 'MCDXVIII', 'MCDXIX', 'MCDXX', 'MCDXXI', 'MCDXXII', 'MCDXXIII', 'MCDXXIV', 'MCDXXV', 'MCDXXVI', 'MCDXXVII', 'MCDXXVIII', 'MCDXXIX', 'MCDXXX', 'MCDXXXI', 'MCDXXXII', 'MCDXXXIII', 'MCDXXXIV', 'MCDXXXV', 'MCDXXXVI', 'MCDXXXVII', 'MCDXXXVIII', 'MCDXXXIX', 'MCDXL', 'MCDXLI', 'MCDXLII', 'MCDXLIII', 'MCDXLIV', 'MCDVL', 'MCDVLI', 'MCDVLII', 'MCDVLIII', 'MCDVLIV', 'MLD', 'MLDI', 'MLDII', 'MLDIII', 'MLDIV', 'MLDV', 'MLDVI', 'MLDVII', 'MLDVIII', 'MLDIX', 'MLDX', 'MLDXI', 'MLDXII', 'MLDXIII', 'MLDXIV', 'MLDXV', 'MLDXVI', 'MLDXVII', 'MLDXVIII', 'MLDXIX', 'MLDXX', 'MLDXXI', 'MLDXXII', 'MLDXXIII', 'MLDXXIV', 'MLDXXV', 'MLDXXVI', 'MLDXXVII', 'MLDXXVIII', 'MLDXXIX', 'MLDXXX', 'MLDXXXI', 'MLDXXXII', 'MLDXXXIII', 'MLDXXXIV', 'MLDXXXV', 'MLDXXXVI', 'MLDXXXVII', 'MLDXXXVIII', 'MLDXXXIX', 'MLDXL', 'MLDXLI', 'MLDXLII', 'MLDXLIII', 'MLDXLIV', 'MLDVL', 'MLDVLI', 'MLDVLII', 'MLDVLIII', 'MLDVLIV', 'MD', 'MDI', 'MDII', 'MDIII', 'MDIV', 'MDV', 'MDVI', 'MDVII', 'MDVIII', 'MDIX', 'MDX', 'MDXI', 'MDXII', 'MDXIII', 'MDXIV', 'MDXV', 'MDXVI', 'MDXVII', 'MDXVIII', 'MDXIX', 'MDXX', 'MDXXI', 'MDXXII', 'MDXXIII', 'MDXXIV', 'MDXXV', 'MDXXVI', 'MDXXVII', 'MDXXVIII', 'MDXXIX', 'MDXXX', 'MDXXXI', 'MDXXXII', 'MDXXXIII', 'MDXXXIV', 'MDXXXV', 'MDXXXVI', 'MDXXXVII', 'MDXXXVIII', 'MDXXXIX', 'MDXL', 'MDXLI', 'MDXLII', 'MDXLIII', 'MDXLIV', 'MDVL', 'MDVLI', 'MDVLII', 'MDVLIII', 'MDVLIV', 'MDL', 'MDLI', 'MDLII', 'MDLIII', 'MDLIV', 'MDLV', 'MDLVI', 'MDLVII', 'MDLVIII', 'MDLIX', 'MDLX', 'MDLXI', 'MDLXII', 'MDLXIII', 'MDLXIV', 'MDLXV', 'MDLXVI', 'MDLXVII', 'MDLXVIII', 'MDLXIX', 'MDLXX', 'MDLXXI', 'MDLXXII', 'MDLXXIII', 'MDLXXIV', 'MDLXXV', 'MDLXXVI', 'MDLXXVII', 'MDLXXVIII', 'MDLXXIX', 'MDLXXX', 'MDLXXXI', 'MDLXXXII', 'MDLXXXIII', 'MDLXXXIV', 'MDLXXXV', 'MDLXXXVI', 'MDLXXXVII', 'MDLXXXVIII', 'MDLXXXIX', 'MDXC', 'MDXCI', 'MDXCII', 'MDXCIII', 'MDXCIV', 'MDVC', 'MDVCI', 'MDVCII', 'MDVCIII', 'MDVCIV', 'MDC', 'MDCI', 'MDCII', 'MDCIII', 'MDCIV', 'MDCV', 'MDCVI', 'MDCVII', 'MDCVIII', 'MDCIX', 'MDCX', 'MDCXI', 'MDCXII', 'MDCXIII', 'MDCXIV', 'MDCXV', 'MDCXVI', 'MDCXVII', 'MDCXVIII', 'MDCXIX', 'MDCXX', 'MDCXXI', 'MDCXXII', 'MDCXXIII', 'MDCXXIV', 'MDCXXV', 'MDCXXVI', 'MDCXXVII', 'MDCXXVIII', 'MDCXXIX', 'MDCXXX', 'MDCXXXI', 'MDCXXXII', 'MDCXXXIII', 'MDCXXXIV', 'MDCXXXV', 'MDCXXXVI', 'MDCXXXVII', 'MDCXXXVIII', 'MDCXXXIX', 'MDCXL', 'MDCXLI', 'MDCXLII', 'MDCXLIII', 'MDCXLIV', 'MDCVL', 'MDCVLI', 'MDCVLII', 'MDCVLIII', 'MDCVLIV', 'MDCL', 'MDCLI', 'MDCLII', 'MDCLIII', 'MDCLIV', 'MDCLV', 'MDCLVI', 'MDCLVII', 'MDCLVIII', 'MDCLIX', 'MDCLX', 'MDCLXI', 'MDCLXII', 'MDCLXIII', 'MDCLXIV', 'MDCLXV', 'MDCLXVI', 'MDCLXVII', 'MDCLXVIII', 'MDCLXIX', 'MDCLXX', 'MDCLXXI', 'MDCLXXII', 'MDCLXXIII', 'MDCLXXIV', 'MDCLXXV', 'MDCLXXVI', 'MDCLXXVII', 'MDCLXXVIII', 'MDCLXXIX', 'MDCLXXX', 'MDCLXXXI', 'MDCLXXXII', 'MDCLXXXIII', 'MDCLXXXIV', 'MDCLXXXV', 'MDCLXXXVI', 'MDCLXXXVII', 'MDCLXXXVIII', 'MDCLXXXIX', 'MDCXC', 'MDCXCI', 'MDCXCII', 'MDCXCIII', 'MDCXCIV', 'MDCVC', 'MDCVCI', 'MDCVCII', 'MDCVCIII', 'MDCVCIV', 'MDCC', 'MDCCI', 'MDCCII', 'MDCCIII', 'MDCCIV', 'MDCCV', 'MDCCVI', 'MDCCVII', 'MDCCVIII', 'MDCCIX', 'MDCCX', 'MDCCXI', 'MDCCXII', 'MDCCXIII', 'MDCCXIV', 'MDCCXV', 'MDCCXVI', 'MDCCXVII', 'MDCCXVIII', 'MDCCXIX', 'MDCCXX', 'MDCCXXI', 'MDCCXXII', 'MDCCXXIII', 'MDCCXXIV', 'MDCCXXV', 'MDCCXXVI', 'MDCCXXVII', 'MDCCXXVIII', 'MDCCXXIX', 'MDCCXXX', 'MDCCXXXI', 'MDCCXXXII', 'MDCCXXXIII', 'MDCCXXXIV', 'MDCCXXXV', 'MDCCXXXVI', 'MDCCXXXVII', 'MDCCXXXVIII', 'MDCCXXXIX', 'MDCCXL', 'MDCCXLI', 'MDCCXLII', 'MDCCXLIII', 'MDCCXLIV', 'MDCCVL', 'MDCCVLI', 'MDCCVLII', 'MDCCVLIII', 'MDCCVLIV', 'MDCCL', 'MDCCLI', 'MDCCLII', 'MDCCLIII', 'MDCCLIV', 'MDCCLV', 'MDCCLVI', 'MDCCLVII', 'MDCCLVIII', 'MDCCLIX', 'MDCCLX', 'MDCCLXI', 'MDCCLXII', 'MDCCLXIII', 'MDCCLXIV', 'MDCCLXV', 'MDCCLXVI', 'MDCCLXVII', 'MDCCLXVIII', 'MDCCLXIX', 'MDCCLXX', 'MDCCLXXI', 'MDCCLXXII', 'MDCCLXXIII', 'MDCCLXXIV', 'MDCCLXXV', 'MDCCLXXVI', 'MDCCLXXVII', 'MDCCLXXVIII', 'MDCCLXXIX', 'MDCCLXXX', 'MDCCLXXXI', 'MDCCLXXXII', 'MDCCLXXXIII', 'MDCCLXXXIV', 'MDCCLXXXV', 'MDCCLXXXVI', 'MDCCLXXXVII', 'MDCCLXXXVIII', 'MDCCLXXXIX', 'MDCCXC', 'MDCCXCI', 'MDCCXCII', 'MDCCXCIII', 'MDCCXCIV', 'MDCCVC', 'MDCCVCI', 'MDCCVCII', 'MDCCVCIII', 'MDCCVCIV', 'MDCCC', 'MDCCCI', 'MDCCCII', 'MDCCCIII', 'MDCCCIV', 'MDCCCV', 'MDCCCVI', 'MDCCCVII', 'MDCCCVIII', 'MDCCCIX', 'MDCCCX', 'MDCCCXI', 'MDCCCXII', 'MDCCCXIII', 'MDCCCXIV', 'MDCCCXV', 'MDCCCXVI', 'MDCCCXVII', 'MDCCCXVIII', 'MDCCCXIX', 'MDCCCXX', 'MDCCCXXI', 'MDCCCXXII', 'MDCCCXXIII', 'MDCCCXXIV', 'MDCCCXXV', 'MDCCCXXVI', 'MDCCCXXVII', 'MDCCCXXVIII', 'MDCCCXXIX', 'MDCCCXXX', 'MDCCCXXXI', 'MDCCCXXXII', 'MDCCCXXXIII', 'MDCCCXXXIV', 'MDCCCXXXV', 'MDCCCXXXVI', 'MDCCCXXXVII', 'MDCCCXXXVIII', 'MDCCCXXXIX', 'MDCCCXL', 'MDCCCXLI', 'MDCCCXLII', 'MDCCCXLIII', 'MDCCCXLIV', 'MDCCCVL', 'MDCCCVLI', 'MDCCCVLII', 'MDCCCVLIII', 'MDCCCVLIV', 'MDCCCL', 'MDCCCLI', 'MDCCCLII', 'MDCCCLIII', 'MDCCCLIV', 'MDCCCLV', 'MDCCCLVI', 'MDCCCLVII', 'MDCCCLVIII', 'MDCCCLIX', 'MDCCCLX', 'MDCCCLXI', 'MDCCCLXII', 'MDCCCLXIII', 'MDCCCLXIV', 'MDCCCLXV', 'MDCCCLXVI', 'MDCCCLXVII', 'MDCCCLXVIII', 'MDCCCLXIX', 'MDCCCLXX', 'MDCCCLXXI', 'MDCCCLXXII', 'MDCCCLXXIII', 'MDCCCLXXIV', 'MDCCCLXXV', 'MDCCCLXXVI', 'MDCCCLXXVII', 'MDCCCLXXVIII', 'MDCCCLXXIX', 'MDCCCLXXX', 'MDCCCLXXXI', 'MDCCCLXXXII', 'MDCCCLXXXIII', 'MDCCCLXXXIV', 'MDCCCLXXXV', 'MDCCCLXXXVI', 'MDCCCLXXXVII', 'MDCCCLXXXVIII', 'MDCCCLXXXIX', 'MDCCCXC', 'MDCCCXCI', 'MDCCCXCII', 'MDCCCXCIII', 'MDCCCXCIV', 'MDCCCVC', 'MDCCCVCI', 'MDCCCVCII', 'MDCCCVCIII', 'MDCCCVCIV', 'MCM', 'MCMI', 'MCMII', 'MCMIII', 'MCMIV', 'MCMV', 'MCMVI', 'MCMVII', 'MCMVIII', 'MCMIX', 'MCMX', 'MCMXI', 'MCMXII', 'MCMXIII', 'MCMXIV', 'MCMXV', 'MCMXVI', 'MCMXVII', 'MCMXVIII', 'MCMXIX', 'MCMXX', 'MCMXXI', 'MCMXXII', 'MCMXXIII', 'MCMXXIV', 'MCMXXV', 'MCMXXVI', 'MCMXXVII', 'MCMXXVIII', 'MCMXXIX', 'MCMXXX', 'MCMXXXI', 'MCMXXXII', 'MCMXXXIII', 'MCMXXXIV', 'MCMXXXV', 'MCMXXXVI', 'MCMXXXVII', 'MCMXXXVIII', 'MCMXXXIX', 'MCMXL', 'MCMXLI', 'MCMXLII', 'MCMXLIII', 'MCMXLIV', 'MCMVL', 'MCMVLI', 'MCMVLII', 'MCMVLIII', 'MCMVLIV', 'MLM', 'MLMI', 'MLMII', 'MLMIII', 'MLMIV', 'MLMV', 'MLMVI', 'MLMVII', 'MLMVIII', 'MLMIX', 'MLMX', 'MLMXI', 'MLMXII', 'MLMXIII', 'MLMXIV', 'MLMXV', 'MLMXVI', 'MLMXVII', 'MLMXVIII', 'MLMXIX', 'MLMXX', 'MLMXXI', 'MLMXXII', 'MLMXXIII', 'MLMXXIV', 'MLMXXV', 'MLMXXVI', 'MLMXXVII', 'MLMXXVIII', 'MLMXXIX', 'MLMXXX', 'MLMXXXI', 'MLMXXXII', 'MLMXXXIII', 'MLMXXXIV', 'MLMXXXV', 'MLMXXXVI', 'MLMXXXVII', 'MLMXXXVIII', 'MLMXXXIX', 'MLMXL', 'MLMXLI', 'MLMXLII', 'MLMXLIII', 'MLMXLIV', 'MLMVL', 'MLMVLI', 'MLMVLII', 'MLMVLIII', 'MLMVLIV', 'MM', 'MMI', 'MMII', 'MMIII', 'MMIV', 'MMV', 'MMVI', 'MMVII', 'MMVIII', 'MMIX', 'MMX', 'MMXI', 'MMXII', 'MMXIII', 'MMXIV', 'MMXV', 'MMXVI', 'MMXVII', 'MMXVIII', 'MMXIX', 'MMXX', 'MMXXI', 'MMXXII', 'MMXXIII', 'MMXXIV', 'MMXXV', 'MMXXVI', 'MMXXVII', 'MMXXVIII', 'MMXXIX', 'MMXXX', 'MMXXXI', 'MMXXXII', 'MMXXXIII', 'MMXXXIV', 'MMXXXV', 'MMXXXVI', 'MMXXXVII', 'MMXXXVIII', 'MMXXXIX', 'MMXL', 'MMXLI', 'MMXLII', 'MMXLIII', 'MMXLIV', 'MMVL', 'MMVLI', 'MMVLII', 'MMVLIII', 'MMVLIV', 'MML', 'MMLI', 'MMLII', 'MMLIII', 'MMLIV', 'MMLV', 'MMLVI', 'MMLVII', 'MMLVIII', 'MMLIX', 'MMLX', 'MMLXI', 'MMLXII', 'MMLXIII', 'MMLXIV', 'MMLXV', 'MMLXVI', 'MMLXVII', 'MMLXVIII', 'MMLXIX', 'MMLXX', 'MMLXXI', 'MMLXXII', 'MMLXXIII', 'MMLXXIV', 'MMLXXV', 'MMLXXVI', 'MMLXXVII', 'MMLXXVIII', 'MMLXXIX', 'MMLXXX', 'MMLXXXI', 'MMLXXXII', 'MMLXXXIII', 'MMLXXXIV', 'MMLXXXV', 'MMLXXXVI', 'MMLXXXVII', 'MMLXXXVIII', 'MMLXXXIX', 'MMXC', 'MMXCI', 'MMXCII', 'MMXCIII', 'MMXCIV', 'MMVC', 'MMVCI', 'MMVCII', 'MMVCIII', 'MMVCIV', 'MMC', 'MMCI', 'MMCII', 'MMCIII', 'MMCIV', 'MMCV', 'MMCVI', 'MMCVII', 'MMCVIII', 'MMCIX', 'MMCX', 'MMCXI', 'MMCXII', 'MMCXIII', 'MMCXIV', 'MMCXV', 'MMCXVI', 'MMCXVII', 'MMCXVIII', 'MMCXIX', 'MMCXX', 'MMCXXI', 'MMCXXII', 'MMCXXIII', 'MMCXXIV', 'MMCXXV', 'MMCXXVI', 'MMCXXVII', 'MMCXXVIII', 'MMCXXIX', 'MMCXXX', 'MMCXXXI', 'MMCXXXII', 'MMCXXXIII', 'MMCXXXIV', 'MMCXXXV', 'MMCXXXVI', 'MMCXXXVII', 'MMCXXXVIII', 'MMCXXXIX', 'MMCXL', 'MMCXLI', 'MMCXLII', 'MMCXLIII', 'MMCXLIV', 'MMCVL', 'MMCVLI', 'MMCVLII', 'MMCVLIII', 'MMCVLIV', 'MMCL', 'MMCLI', 'MMCLII', 'MMCLIII', 'MMCLIV', 'MMCLV', 'MMCLVI', 'MMCLVII', 'MMCLVIII', 'MMCLIX', 'MMCLX', 'MMCLXI', 'MMCLXII', 'MMCLXIII', 'MMCLXIV', 'MMCLXV', 'MMCLXVI', 'MMCLXVII', 'MMCLXVIII', 'MMCLXIX', 'MMCLXX', 'MMCLXXI', 'MMCLXXII', 'MMCLXXIII', 'MMCLXXIV', 'MMCLXXV', 'MMCLXXVI', 'MMCLXXVII', 'MMCLXXVIII', 'MMCLXXIX', 'MMCLXXX', 'MMCLXXXI', 'MMCLXXXII', 'MMCLXXXIII', 'MMCLXXXIV', 'MMCLXXXV', 'MMCLXXXVI', 'MMCLXXXVII', 'MMCLXXXVIII', 'MMCLXXXIX', 'MMCXC', 'MMCXCI', 'MMCXCII', 'MMCXCIII', 'MMCXCIV', 'MMCVC', 'MMCVCI', 'MMCVCII', 'MMCVCIII', 'MMCVCIV', 'MMCC', 'MMCCI', 'MMCCII', 'MMCCIII', 'MMCCIV', 'MMCCV', 'MMCCVI', 'MMCCVII', 'MMCCVIII', 'MMCCIX', 'MMCCX', 'MMCCXI', 'MMCCXII', 'MMCCXIII', 'MMCCXIV', 'MMCCXV', 'MMCCXVI', 'MMCCXVII', 'MMCCXVIII', 'MMCCXIX', 'MMCCXX', 'MMCCXXI', 'MMCCXXII', 'MMCCXXIII', 'MMCCXXIV', 'MMCCXXV', 'MMCCXXVI', 'MMCCXXVII', 'MMCCXXVIII', 'MMCCXXIX', 'MMCCXXX', 'MMCCXXXI', 'MMCCXXXII', 'MMCCXXXIII', 'MMCCXXXIV', 'MMCCXXXV', 'MMCCXXXVI', 'MMCCXXXVII', 'MMCCXXXVIII', 'MMCCXXXIX', 'MMCCXL', 'MMCCXLI', 'MMCCXLII', 'MMCCXLIII', 'MMCCXLIV', 'MMCCVL', 'MMCCVLI', 'MMCCVLII', 'MMCCVLIII', 'MMCCVLIV', 'MMCCL', 'MMCCLI', 'MMCCLII', 'MMCCLIII', 'MMCCLIV', 'MMCCLV', 'MMCCLVI', 'MMCCLVII', 'MMCCLVIII', 'MMCCLIX', 'MMCCLX', 'MMCCLXI', 'MMCCLXII', 'MMCCLXIII', 'MMCCLXIV', 'MMCCLXV', 'MMCCLXVI', 'MMCCLXVII', 'MMCCLXVIII', 'MMCCLXIX', 'MMCCLXX', 'MMCCLXXI', 'MMCCLXXII', 'MMCCLXXIII', 'MMCCLXXIV', 'MMCCLXXV', 'MMCCLXXVI', 'MMCCLXXVII', 'MMCCLXXVIII', 'MMCCLXXIX', 'MMCCLXXX', 'MMCCLXXXI', 'MMCCLXXXII', 'MMCCLXXXIII', 'MMCCLXXXIV', 'MMCCLXXXV', 'MMCCLXXXVI', 'MMCCLXXXVII', 'MMCCLXXXVIII', 'MMCCLXXXIX', 'MMCCXC', 'MMCCXCI', 'MMCCXCII', 'MMCCXCIII', 'MMCCXCIV', 'MMCCVC', 'MMCCVCI', 'MMCCVCII', 'MMCCVCIII', 'MMCCVCIV', 'MMCCC', 'MMCCCI', 'MMCCCII', 'MMCCCIII', 'MMCCCIV', 'MMCCCV', 'MMCCCVI', 'MMCCCVII', 'MMCCCVIII', 'MMCCCIX', 'MMCCCX', 'MMCCCXI', 'MMCCCXII', 'MMCCCXIII', 'MMCCCXIV', 'MMCCCXV', 'MMCCCXVI', 'MMCCCXVII', 'MMCCCXVIII', 'MMCCCXIX', 'MMCCCXX', 'MMCCCXXI', 'MMCCCXXII', 'MMCCCXXIII', 'MMCCCXXIV', 'MMCCCXXV', 'MMCCCXXVI', 'MMCCCXXVII', 'MMCCCXXVIII', 'MMCCCXXIX', 'MMCCCXXX', 'MMCCCXXXI', 'MMCCCXXXII', 'MMCCCXXXIII', 'MMCCCXXXIV', 'MMCCCXXXV', 'MMCCCXXXVI', 'MMCCCXXXVII', 'MMCCCXXXVIII', 'MMCCCXXXIX', 'MMCCCXL', 'MMCCCXLI', 'MMCCCXLII', 'MMCCCXLIII', 'MMCCCXLIV', 'MMCCCVL', 'MMCCCVLI', 'MMCCCVLII', 'MMCCCVLIII', 'MMCCCVLIV', 'MMCCCL', 'MMCCCLI', 'MMCCCLII', 'MMCCCLIII', 'MMCCCLIV', 'MMCCCLV', 'MMCCCLVI', 'MMCCCLVII', 'MMCCCLVIII', 'MMCCCLIX', 'MMCCCLX', 'MMCCCLXI', 'MMCCCLXII', 'MMCCCLXIII', 'MMCCCLXIV', 'MMCCCLXV', 'MMCCCLXVI', 'MMCCCLXVII', 'MMCCCLXVIII', 'MMCCCLXIX', 'MMCCCLXX', 'MMCCCLXXI', 'MMCCCLXXII', 'MMCCCLXXIII', 'MMCCCLXXIV', 'MMCCCLXXV', 'MMCCCLXXVI', 'MMCCCLXXVII', 'MMCCCLXXVIII', 'MMCCCLXXIX', 'MMCCCLXXX', 'MMCCCLXXXI', 'MMCCCLXXXII', 'MMCCCLXXXIII', 'MMCCCLXXXIV', 'MMCCCLXXXV', 'MMCCCLXXXVI', 'MMCCCLXXXVII', 'MMCCCLXXXVIII', 'MMCCCLXXXIX', 'MMCCCXC', 'MMCCCXCI', 'MMCCCXCII', 'MMCCCXCIII', 'MMCCCXCIV', 'MMCCCVC', 'MMCCCVCI', 'MMCCCVCII', 'MMCCCVCIII', 'MMCCCVCIV', 'MMCD', 'MMCDI', 'MMCDII', 'MMCDIII', 'MMCDIV', 'MMCDV', 'MMCDVI', 'MMCDVII', 'MMCDVIII', 'MMCDIX', 'MMCDX', 'MMCDXI', 'MMCDXII', 'MMCDXIII', 'MMCDXIV', 'MMCDXV', 'MMCDXVI', 'MMCDXVII', 'MMCDXVIII', 'MMCDXIX', 'MMCDXX', 'MMCDXXI', 'MMCDXXII', 'MMCDXXIII', 'MMCDXXIV', 'MMCDXXV', 'MMCDXXVI', 'MMCDXXVII', 'MMCDXXVIII', 'MMCDXXIX', 'MMCDXXX', 'MMCDXXXI', 'MMCDXXXII', 'MMCDXXXIII', 'MMCDXXXIV', 'MMCDXXXV', 'MMCDXXXVI', 'MMCDXXXVII', 'MMCDXXXVIII', 'MMCDXXXIX', 'MMCDXL', 'MMCDXLI', 'MMCDXLII', 'MMCDXLIII', 'MMCDXLIV', 'MMCDVL', 'MMCDVLI', 'MMCDVLII', 'MMCDVLIII', 'MMCDVLIV', 'MMLD', 'MMLDI', 'MMLDII', 'MMLDIII', 'MMLDIV', 'MMLDV', 'MMLDVI', 'MMLDVII', 'MMLDVIII', 'MMLDIX', 'MMLDX', 'MMLDXI', 'MMLDXII', 'MMLDXIII', 'MMLDXIV', 'MMLDXV', 'MMLDXVI', 'MMLDXVII', 'MMLDXVIII', 'MMLDXIX', 'MMLDXX', 'MMLDXXI', 'MMLDXXII', 'MMLDXXIII', 'MMLDXXIV', 'MMLDXXV', 'MMLDXXVI', 'MMLDXXVII', 'MMLDXXVIII', 'MMLDXXIX', 'MMLDXXX', 'MMLDXXXI', 'MMLDXXXII', 'MMLDXXXIII', 'MMLDXXXIV', 'MMLDXXXV', 'MMLDXXXVI', 'MMLDXXXVII', 'MMLDXXXVIII', 'MMLDXXXIX', 'MMLDXL', 'MMLDXLI', 'MMLDXLII', 'MMLDXLIII', 'MMLDXLIV', 'MMLDVL', 'MMLDVLI', 'MMLDVLII', 'MMLDVLIII', 'MMLDVLIV', 'MMD', 'MMDI', 'MMDII', 'MMDIII', 'MMDIV', 'MMDV', 'MMDVI', 'MMDVII', 'MMDVIII', 'MMDIX', 'MMDX', 'MMDXI', 'MMDXII', 'MMDXIII', 'MMDXIV', 'MMDXV', 'MMDXVI', 'MMDXVII', 'MMDXVIII', 'MMDXIX', 'MMDXX', 'MMDXXI', 'MMDXXII', 'MMDXXIII', 'MMDXXIV', 'MMDXXV', 'MMDXXVI', 'MMDXXVII', 'MMDXXVIII', 'MMDXXIX', 'MMDXXX', 'MMDXXXI', 'MMDXXXII', 'MMDXXXIII', 'MMDXXXIV', 'MMDXXXV', 'MMDXXXVI', 'MMDXXXVII', 'MMDXXXVIII', 'MMDXXXIX', 'MMDXL', 'MMDXLI', 'MMDXLII', 'MMDXLIII', 'MMDXLIV', 'MMDVL', 'MMDVLI', 'MMDVLII', 'MMDVLIII', 'MMDVLIV', 'MMDL', 'MMDLI', 'MMDLII', 'MMDLIII', 'MMDLIV', 'MMDLV', 'MMDLVI', 'MMDLVII', 'MMDLVIII', 'MMDLIX', 'MMDLX', 'MMDLXI', 'MMDLXII', 'MMDLXIII', 'MMDLXIV', 'MMDLXV', 'MMDLXVI', 'MMDLXVII', 'MMDLXVIII', 'MMDLXIX', 'MMDLXX', 'MMDLXXI', 'MMDLXXII', 'MMDLXXIII', 'MMDLXXIV', 'MMDLXXV', 'MMDLXXVI', 'MMDLXXVII', 'MMDLXXVIII', 'MMDLXXIX', 'MMDLXXX', 'MMDLXXXI', 'MMDLXXXII', 'MMDLXXXIII', 'MMDLXXXIV', 'MMDLXXXV', 'MMDLXXXVI', 'MMDLXXXVII', 'MMDLXXXVIII', 'MMDLXXXIX', 'MMDXC', 'MMDXCI', 'MMDXCII', 'MMDXCIII', 'MMDXCIV', 'MMDVC', 'MMDVCI', 'MMDVCII', 'MMDVCIII', 'MMDVCIV', 'MMDC', 'MMDCI', 'MMDCII', 'MMDCIII', 'MMDCIV', 'MMDCV', 'MMDCVI', 'MMDCVII', 'MMDCVIII', 'MMDCIX', 'MMDCX', 'MMDCXI', 'MMDCXII', 'MMDCXIII', 'MMDCXIV', 'MMDCXV', 'MMDCXVI', 'MMDCXVII', 'MMDCXVIII', 'MMDCXIX', 'MMDCXX', 'MMDCXXI', 'MMDCXXII', 'MMDCXXIII', 'MMDCXXIV', 'MMDCXXV', 'MMDCXXVI', 'MMDCXXVII', 'MMDCXXVIII', 'MMDCXXIX', 'MMDCXXX', 'MMDCXXXI', 'MMDCXXXII', 'MMDCXXXIII', 'MMDCXXXIV', 'MMDCXXXV', 'MMDCXXXVI', 'MMDCXXXVII', 'MMDCXXXVIII', 'MMDCXXXIX', 'MMDCXL', 'MMDCXLI', 'MMDCXLII', 'MMDCXLIII', 'MMDCXLIV', 'MMDCVL', 'MMDCVLI', 'MMDCVLII', 'MMDCVLIII', 'MMDCVLIV', 'MMDCL', 'MMDCLI', 'MMDCLII', 'MMDCLIII', 'MMDCLIV', 'MMDCLV', 'MMDCLVI', 'MMDCLVII', 'MMDCLVIII', 'MMDCLIX', 'MMDCLX', 'MMDCLXI', 'MMDCLXII', 'MMDCLXIII', 'MMDCLXIV', 'MMDCLXV', 'MMDCLXVI', 'MMDCLXVII', 'MMDCLXVIII', 'MMDCLXIX', 'MMDCLXX', 'MMDCLXXI', 'MMDCLXXII', 'MMDCLXXIII', 'MMDCLXXIV', 'MMDCLXXV', 'MMDCLXXVI', 'MMDCLXXVII', 'MMDCLXXVIII', 'MMDCLXXIX', 'MMDCLXXX', 'MMDCLXXXI', 'MMDCLXXXII', 'MMDCLXXXIII', 'MMDCLXXXIV', 'MMDCLXXXV', 'MMDCLXXXVI', 'MMDCLXXXVII', 'MMDCLXXXVIII', 'MMDCLXXXIX', 'MMDCXC', 'MMDCXCI', 'MMDCXCII', 'MMDCXCIII', 'MMDCXCIV', 'MMDCVC', 'MMDCVCI', 'MMDCVCII', 'MMDCVCIII', 'MMDCVCIV', 'MMDCC', 'MMDCCI', 'MMDCCII', 'MMDCCIII', 'MMDCCIV', 'MMDCCV', 'MMDCCVI', 'MMDCCVII', 'MMDCCVIII', 'MMDCCIX', 'MMDCCX', 'MMDCCXI', 'MMDCCXII', 'MMDCCXIII', 'MMDCCXIV', 'MMDCCXV', 'MMDCCXVI', 'MMDCCXVII', 'MMDCCXVIII', 'MMDCCXIX', 'MMDCCXX', 'MMDCCXXI', 'MMDCCXXII', 'MMDCCXXIII', 'MMDCCXXIV', 'MMDCCXXV', 'MMDCCXXVI', 'MMDCCXXVII', 'MMDCCXXVIII', 'MMDCCXXIX', 'MMDCCXXX', 'MMDCCXXXI', 'MMDCCXXXII', 'MMDCCXXXIII', 'MMDCCXXXIV', 'MMDCCXXXV', 'MMDCCXXXVI', 'MMDCCXXXVII', 'MMDCCXXXVIII', 'MMDCCXXXIX', 'MMDCCXL', 'MMDCCXLI', 'MMDCCXLII', 'MMDCCXLIII', 'MMDCCXLIV', 'MMDCCVL', 'MMDCCVLI', 'MMDCCVLII', 'MMDCCVLIII', 'MMDCCVLIV', 'MMDCCL', 'MMDCCLI', 'MMDCCLII', 'MMDCCLIII', 'MMDCCLIV', 'MMDCCLV', 'MMDCCLVI', 'MMDCCLVII', 'MMDCCLVIII', 'MMDCCLIX', 'MMDCCLX', 'MMDCCLXI', 'MMDCCLXII', 'MMDCCLXIII', 'MMDCCLXIV', 'MMDCCLXV', 'MMDCCLXVI', 'MMDCCLXVII', 'MMDCCLXVIII', 'MMDCCLXIX', 'MMDCCLXX', 'MMDCCLXXI', 'MMDCCLXXII', 'MMDCCLXXIII', 'MMDCCLXXIV', 'MMDCCLXXV', 'MMDCCLXXVI', 'MMDCCLXXVII', 'MMDCCLXXVIII', 'MMDCCLXXIX', 'MMDCCLXXX', 'MMDCCLXXXI', 'MMDCCLXXXII', 'MMDCCLXXXIII', 'MMDCCLXXXIV', 'MMDCCLXXXV', 'MMDCCLXXXVI', 'MMDCCLXXXVII', 'MMDCCLXXXVIII', 'MMDCCLXXXIX', 'MMDCCXC', 'MMDCCXCI', 'MMDCCXCII', 'MMDCCXCIII', 'MMDCCXCIV', 'MMDCCVC', 'MMDCCVCI', 'MMDCCVCII', 'MMDCCVCIII', 'MMDCCVCIV', 'MMDCCC', 'MMDCCCI', 'MMDCCCII', 'MMDCCCIII', 'MMDCCCIV', 'MMDCCCV', 'MMDCCCVI', 'MMDCCCVII', 'MMDCCCVIII', 'MMDCCCIX', 'MMDCCCX', 'MMDCCCXI', 'MMDCCCXII', 'MMDCCCXIII', 'MMDCCCXIV', 'MMDCCCXV', 'MMDCCCXVI', 'MMDCCCXVII', 'MMDCCCXVIII', 'MMDCCCXIX', 'MMDCCCXX', 'MMDCCCXXI', 'MMDCCCXXII', 'MMDCCCXXIII', 'MMDCCCXXIV', 'MMDCCCXXV', 'MMDCCCXXVI', 'MMDCCCXXVII', 'MMDCCCXXVIII', 'MMDCCCXXIX', 'MMDCCCXXX', 'MMDCCCXXXI', 'MMDCCCXXXII', 'MMDCCCXXXIII', 'MMDCCCXXXIV', 'MMDCCCXXXV', 'MMDCCCXXXVI', 'MMDCCCXXXVII', 'MMDCCCXXXVIII', 'MMDCCCXXXIX', 'MMDCCCXL', 'MMDCCCXLI', 'MMDCCCXLII', 'MMDCCCXLIII', 'MMDCCCXLIV', 'MMDCCCVL', 'MMDCCCVLI', 'MMDCCCVLII', 'MMDCCCVLIII', 'MMDCCCVLIV', 'MMDCCCL', 'MMDCCCLI', 'MMDCCCLII', 'MMDCCCLIII', 'MMDCCCLIV', 'MMDCCCLV', 'MMDCCCLVI', 'MMDCCCLVII', 'MMDCCCLVIII', 'MMDCCCLIX', 'MMDCCCLX', 'MMDCCCLXI', 'MMDCCCLXII', 'MMDCCCLXIII', 'MMDCCCLXIV', 'MMDCCCLXV', 'MMDCCCLXVI', 'MMDCCCLXVII', 'MMDCCCLXVIII', 'MMDCCCLXIX', 'MMDCCCLXX', 'MMDCCCLXXI', 'MMDCCCLXXII', 'MMDCCCLXXIII', 'MMDCCCLXXIV', 'MMDCCCLXXV', 'MMDCCCLXXVI', 'MMDCCCLXXVII', 'MMDCCCLXXVIII', 'MMDCCCLXXIX', 'MMDCCCLXXX', 'MMDCCCLXXXI', 'MMDCCCLXXXII', 'MMDCCCLXXXIII', 'MMDCCCLXXXIV', 'MMDCCCLXXXV', 'MMDCCCLXXXVI', 'MMDCCCLXXXVII', 'MMDCCCLXXXVIII', 'MMDCCCLXXXIX', 'MMDCCCXC', 'MMDCCCXCI', 'MMDCCCXCII', 'MMDCCCXCIII', 'MMDCCCXCIV', 'MMDCCCVC', 'MMDCCCVCI', 'MMDCCCVCII', 'MMDCCCVCIII', 'MMDCCCVCIV', 'MMCM', 'MMCMI', 'MMCMII', 'MMCMIII', 'MMCMIV', 'MMCMV', 'MMCMVI', 'MMCMVII', 'MMCMVIII', 'MMCMIX', 'MMCMX', 'MMCMXI', 'MMCMXII', 'MMCMXIII', 'MMCMXIV', 'MMCMXV', 'MMCMXVI', 'MMCMXVII', 'MMCMXVIII', 'MMCMXIX', 'MMCMXX', 'MMCMXXI', 'MMCMXXII', 'MMCMXXIII', 'MMCMXXIV', 'MMCMXXV', 'MMCMXXVI', 'MMCMXXVII', 'MMCMXXVIII', 'MMCMXXIX', 'MMCMXXX', 'MMCMXXXI', 'MMCMXXXII', 'MMCMXXXIII', 'MMCMXXXIV', 'MMCMXXXV', 'MMCMXXXVI', 'MMCMXXXVII', 'MMCMXXXVIII', 'MMCMXXXIX', 'MMCMXL', 'MMCMXLI', 'MMCMXLII', 'MMCMXLIII', 'MMCMXLIV', 'MMCMVL', 'MMCMVLI', 'MMCMVLII', 'MMCMVLIII', 'MMCMVLIV', 'MMLM', 'MMLMI', 'MMLMII', 'MMLMIII', 'MMLMIV', 'MMLMV', 'MMLMVI', 'MMLMVII', 'MMLMVIII', 'MMLMIX', 'MMLMX', 'MMLMXI', 'MMLMXII', 'MMLMXIII', 'MMLMXIV', 'MMLMXV', 'MMLMXVI', 'MMLMXVII', 'MMLMXVIII', 'MMLMXIX', 'MMLMXX', 'MMLMXXI', 'MMLMXXII', 'MMLMXXIII', 'MMLMXXIV', 'MMLMXXV', 'MMLMXXVI', 'MMLMXXVII', 'MMLMXXVIII', 'MMLMXXIX', 'MMLMXXX', 'MMLMXXXI', 'MMLMXXXII', 'MMLMXXXIII', 'MMLMXXXIV', 'MMLMXXXV', 'MMLMXXXVI', 'MMLMXXXVII', 'MMLMXXXVIII', 'MMLMXXXIX', 'MMLMXL', 'MMLMXLI', 'MMLMXLII', 'MMLMXLIII', 'MMLMXLIV', 'MMLMVL', 'MMLMVLI', 'MMLMVLII', 'MMLMVLIII', 'MMLMVLIV', 'MMM', 'MMMI', 'MMMII', 'MMMIII', 'MMMIV', 'MMMV', 'MMMVI', 'MMMVII', 'MMMVIII', 'MMMIX', 'MMMX', 'MMMXI', 'MMMXII', 'MMMXIII', 'MMMXIV', 'MMMXV', 'MMMXVI', 'MMMXVII', 'MMMXVIII', 'MMMXIX', 'MMMXX', 'MMMXXI', 'MMMXXII', 'MMMXXIII', 'MMMXXIV', 'MMMXXV', 'MMMXXVI', 'MMMXXVII', 'MMMXXVIII', 'MMMXXIX', 'MMMXXX', 'MMMXXXI', 'MMMXXXII', 'MMMXXXIII', 'MMMXXXIV', 'MMMXXXV', 'MMMXXXVI', 'MMMXXXVII', 'MMMXXXVIII', 'MMMXXXIX', 'MMMXL', 'MMMXLI', 'MMMXLII', 'MMMXLIII', 'MMMXLIV', 'MMMVL', 'MMMVLI', 'MMMVLII', 'MMMVLIII', 'MMMVLIV', 'MMML', 'MMMLI', 'MMMLII', 'MMMLIII', 'MMMLIV', 'MMMLV', 'MMMLVI', 'MMMLVII', 'MMMLVIII', 'MMMLIX', 'MMMLX', 'MMMLXI', 'MMMLXII', 'MMMLXIII', 'MMMLXIV', 'MMMLXV', 'MMMLXVI', 'MMMLXVII', 'MMMLXVIII', 'MMMLXIX', 'MMMLXX', 'MMMLXXI', 'MMMLXXII', 'MMMLXXIII', 'MMMLXXIV', 'MMMLXXV', 'MMMLXXVI', 'MMMLXXVII', 'MMMLXXVIII', 'MMMLXXIX', 'MMMLXXX', 'MMMLXXXI', 'MMMLXXXII', 'MMMLXXXIII', 'MMMLXXXIV', 'MMMLXXXV', 'MMMLXXXVI', 'MMMLXXXVII', 'MMMLXXXVIII', 'MMMLXXXIX', 'MMMXC', 'MMMXCI', 'MMMXCII', 'MMMXCIII', 'MMMXCIV', 'MMMVC', 'MMMVCI', 'MMMVCII', 'MMMVCIII', 'MMMVCIV', 'MMMC', 'MMMCI', 'MMMCII', 'MMMCIII', 'MMMCIV', 'MMMCV', 'MMMCVI', 'MMMCVII', 'MMMCVIII', 'MMMCIX', 'MMMCX', 'MMMCXI', 'MMMCXII', 'MMMCXIII', 'MMMCXIV', 'MMMCXV', 'MMMCXVI', 'MMMCXVII', 'MMMCXVIII', 'MMMCXIX', 'MMMCXX', 'MMMCXXI', 'MMMCXXII', 'MMMCXXIII', 'MMMCXXIV', 'MMMCXXV', 'MMMCXXVI', 'MMMCXXVII', 'MMMCXXVIII', 'MMMCXXIX', 'MMMCXXX', 'MMMCXXXI', 'MMMCXXXII', 'MMMCXXXIII', 'MMMCXXXIV', 'MMMCXXXV', 'MMMCXXXVI', 'MMMCXXXVII', 'MMMCXXXVIII', 'MMMCXXXIX', 'MMMCXL', 'MMMCXLI', 'MMMCXLII', 'MMMCXLIII', 'MMMCXLIV', 'MMMCVL', 'MMMCVLI', 'MMMCVLII', 'MMMCVLIII', 'MMMCVLIV', 'MMMCL', 'MMMCLI', 'MMMCLII', 'MMMCLIII', 'MMMCLIV', 'MMMCLV', 'MMMCLVI', 'MMMCLVII', 'MMMCLVIII', 'MMMCLIX', 'MMMCLX', 'MMMCLXI', 'MMMCLXII', 'MMMCLXIII', 'MMMCLXIV', 'MMMCLXV', 'MMMCLXVI', 'MMMCLXVII', 'MMMCLXVIII', 'MMMCLXIX', 'MMMCLXX', 'MMMCLXXI', 'MMMCLXXII', 'MMMCLXXIII', 'MMMCLXXIV', 'MMMCLXXV', 'MMMCLXXVI', 'MMMCLXXVII', 'MMMCLXXVIII', 'MMMCLXXIX', 'MMMCLXXX', 'MMMCLXXXI', 'MMMCLXXXII', 'MMMCLXXXIII', 'MMMCLXXXIV', 'MMMCLXXXV', 'MMMCLXXXVI', 'MMMCLXXXVII', 'MMMCLXXXVIII', 'MMMCLXXXIX', 'MMMCXC', 'MMMCXCI', 'MMMCXCII', 'MMMCXCIII', 'MMMCXCIV', 'MMMCVC', 'MMMCVCI', 'MMMCVCII', 'MMMCVCIII', 'MMMCVCIV', 'MMMCC', 'MMMCCI', 'MMMCCII', 'MMMCCIII', 'MMMCCIV', 'MMMCCV', 'MMMCCVI', 'MMMCCVII', 'MMMCCVIII', 'MMMCCIX', 'MMMCCX', 'MMMCCXI', 'MMMCCXII', 'MMMCCXIII', 'MMMCCXIV', 'MMMCCXV', 'MMMCCXVI', 'MMMCCXVII', 'MMMCCXVIII', 'MMMCCXIX', 'MMMCCXX', 'MMMCCXXI', 'MMMCCXXII', 'MMMCCXXIII', 'MMMCCXXIV', 'MMMCCXXV', 'MMMCCXXVI', 'MMMCCXXVII', 'MMMCCXXVIII', 'MMMCCXXIX', 'MMMCCXXX', 'MMMCCXXXI', 'MMMCCXXXII', 'MMMCCXXXIII', 'MMMCCXXXIV', 'MMMCCXXXV', 'MMMCCXXXVI', 'MMMCCXXXVII', 'MMMCCXXXVIII', 'MMMCCXXXIX', 'MMMCCXL', 'MMMCCXLI', 'MMMCCXLII', 'MMMCCXLIII', 'MMMCCXLIV', 'MMMCCVL', 'MMMCCVLI', 'MMMCCVLII', 'MMMCCVLIII', 'MMMCCVLIV', 'MMMCCL', 'MMMCCLI', 'MMMCCLII', 'MMMCCLIII', 'MMMCCLIV', 'MMMCCLV', 'MMMCCLVI', 'MMMCCLVII', 'MMMCCLVIII', 'MMMCCLIX', 'MMMCCLX', 'MMMCCLXI', 'MMMCCLXII', 'MMMCCLXIII', 'MMMCCLXIV', 'MMMCCLXV', 'MMMCCLXVI', 'MMMCCLXVII', 'MMMCCLXVIII', 'MMMCCLXIX', 'MMMCCLXX', 'MMMCCLXXI', 'MMMCCLXXII', 'MMMCCLXXIII', 'MMMCCLXXIV', 'MMMCCLXXV', 'MMMCCLXXVI', 'MMMCCLXXVII', 'MMMCCLXXVIII', 'MMMCCLXXIX', 'MMMCCLXXX', 'MMMCCLXXXI', 'MMMCCLXXXII', 'MMMCCLXXXIII', 'MMMCCLXXXIV', 'MMMCCLXXXV', 'MMMCCLXXXVI', 'MMMCCLXXXVII', 'MMMCCLXXXVIII', 'MMMCCLXXXIX', 'MMMCCXC', 'MMMCCXCI', 'MMMCCXCII', 'MMMCCXCIII', 'MMMCCXCIV', 'MMMCCVC', 'MMMCCVCI', 'MMMCCVCII', 'MMMCCVCIII', 'MMMCCVCIV', 'MMMCCC', 'MMMCCCI', 'MMMCCCII', 'MMMCCCIII', 'MMMCCCIV', 'MMMCCCV', 'MMMCCCVI', 'MMMCCCVII', 'MMMCCCVIII', 'MMMCCCIX', 'MMMCCCX', 'MMMCCCXI', 'MMMCCCXII', 'MMMCCCXIII', 'MMMCCCXIV', 'MMMCCCXV', 'MMMCCCXVI', 'MMMCCCXVII', 'MMMCCCXVIII', 'MMMCCCXIX', 'MMMCCCXX', 'MMMCCCXXI', 'MMMCCCXXII', 'MMMCCCXXIII', 'MMMCCCXXIV', 'MMMCCCXXV', 'MMMCCCXXVI', 'MMMCCCXXVII', 'MMMCCCXXVIII', 'MMMCCCXXIX', 'MMMCCCXXX', 'MMMCCCXXXI', 'MMMCCCXXXII', 'MMMCCCXXXIII', 'MMMCCCXXXIV', 'MMMCCCXXXV', 'MMMCCCXXXVI', 'MMMCCCXXXVII', 'MMMCCCXXXVIII', 'MMMCCCXXXIX', 'MMMCCCXL', 'MMMCCCXLI', 'MMMCCCXLII', 'MMMCCCXLIII', 'MMMCCCXLIV', 'MMMCCCVL', 'MMMCCCVLI', 'MMMCCCVLII', 'MMMCCCVLIII', 'MMMCCCVLIV', 'MMMCCCL', 'MMMCCCLI', 'MMMCCCLII', 'MMMCCCLIII', 'MMMCCCLIV', 'MMMCCCLV', 'MMMCCCLVI', 'MMMCCCLVII', 'MMMCCCLVIII', 'MMMCCCLIX', 'MMMCCCLX', 'MMMCCCLXI', 'MMMCCCLXII', 'MMMCCCLXIII', 'MMMCCCLXIV', 'MMMCCCLXV', 'MMMCCCLXVI', 'MMMCCCLXVII', 'MMMCCCLXVIII', 'MMMCCCLXIX', 'MMMCCCLXX', 'MMMCCCLXXI', 'MMMCCCLXXII', 'MMMCCCLXXIII', 'MMMCCCLXXIV', 'MMMCCCLXXV', 'MMMCCCLXXVI', 'MMMCCCLXXVII', 'MMMCCCLXXVIII', 'MMMCCCLXXIX', 'MMMCCCLXXX', 'MMMCCCLXXXI', 'MMMCCCLXXXII', 'MMMCCCLXXXIII', 'MMMCCCLXXXIV', 'MMMCCCLXXXV', 'MMMCCCLXXXVI', 'MMMCCCLXXXVII', 'MMMCCCLXXXVIII', 'MMMCCCLXXXIX', 'MMMCCCXC', 'MMMCCCXCI', 'MMMCCCXCII', 'MMMCCCXCIII', 'MMMCCCXCIV', 'MMMCCCVC', 'MMMCCCVCI', 'MMMCCCVCII', 'MMMCCCVCIII', 'MMMCCCVCIV', 'MMMCD', 'MMMCDI', 'MMMCDII', 'MMMCDIII', 'MMMCDIV', 'MMMCDV', 'MMMCDVI', 'MMMCDVII', 'MMMCDVIII', 'MMMCDIX', 'MMMCDX', 'MMMCDXI', 'MMMCDXII', 'MMMCDXIII', 'MMMCDXIV', 'MMMCDXV', 'MMMCDXVI', 'MMMCDXVII', 'MMMCDXVIII', 'MMMCDXIX', 'MMMCDXX', 'MMMCDXXI', 'MMMCDXXII', 'MMMCDXXIII', 'MMMCDXXIV', 'MMMCDXXV', 'MMMCDXXVI', 'MMMCDXXVII', 'MMMCDXXVIII', 'MMMCDXXIX', 'MMMCDXXX', 'MMMCDXXXI', 'MMMCDXXXII', 'MMMCDXXXIII', 'MMMCDXXXIV', 'MMMCDXXXV', 'MMMCDXXXVI', 'MMMCDXXXVII', 'MMMCDXXXVIII', 'MMMCDXXXIX', 'MMMCDXL', 'MMMCDXLI', 'MMMCDXLII', 'MMMCDXLIII', 'MMMCDXLIV', 'MMMCDVL', 'MMMCDVLI', 'MMMCDVLII', 'MMMCDVLIII', 'MMMCDVLIV', 'MMMLD', 'MMMLDI', 'MMMLDII', 'MMMLDIII', 'MMMLDIV', 'MMMLDV', 'MMMLDVI', 'MMMLDVII', 'MMMLDVIII', 'MMMLDIX', 'MMMLDX', 'MMMLDXI', 'MMMLDXII', 'MMMLDXIII', 'MMMLDXIV', 'MMMLDXV', 'MMMLDXVI', 'MMMLDXVII', 'MMMLDXVIII', 'MMMLDXIX', 'MMMLDXX', 'MMMLDXXI', 'MMMLDXXII', 'MMMLDXXIII', 'MMMLDXXIV', 'MMMLDXXV', 'MMMLDXXVI', 'MMMLDXXVII', 'MMMLDXXVIII', 'MMMLDXXIX', 'MMMLDXXX', 'MMMLDXXXI', 'MMMLDXXXII', 'MMMLDXXXIII', 'MMMLDXXXIV', 'MMMLDXXXV', 'MMMLDXXXVI', 'MMMLDXXXVII', 'MMMLDXXXVIII', 'MMMLDXXXIX', 'MMMLDXL', 'MMMLDXLI', 'MMMLDXLII', 'MMMLDXLIII', 'MMMLDXLIV', 'MMMLDVL', 'MMMLDVLI', 'MMMLDVLII', 'MMMLDVLIII', 'MMMLDVLIV', 'MMMD', 'MMMDI', 'MMMDII', 'MMMDIII', 'MMMDIV', 'MMMDV', 'MMMDVI', 'MMMDVII', 'MMMDVIII', 'MMMDIX', 'MMMDX', 'MMMDXI', 'MMMDXII', 'MMMDXIII', 'MMMDXIV', 'MMMDXV', 'MMMDXVI', 'MMMDXVII', 'MMMDXVIII', 'MMMDXIX', 'MMMDXX', 'MMMDXXI', 'MMMDXXII', 'MMMDXXIII', 'MMMDXXIV', 'MMMDXXV', 'MMMDXXVI', 'MMMDXXVII', 'MMMDXXVIII', 'MMMDXXIX', 'MMMDXXX', 'MMMDXXXI', 'MMMDXXXII', 'MMMDXXXIII', 'MMMDXXXIV', 'MMMDXXXV', 'MMMDXXXVI', 'MMMDXXXVII', 'MMMDXXXVIII', 'MMMDXXXIX', 'MMMDXL', 'MMMDXLI', 'MMMDXLII', 'MMMDXLIII', 'MMMDXLIV', 'MMMDVL', 'MMMDVLI', 'MMMDVLII', 'MMMDVLIII', 'MMMDVLIV', 'MMMDL', 'MMMDLI', 'MMMDLII', 'MMMDLIII', 'MMMDLIV', 'MMMDLV', 'MMMDLVI', 'MMMDLVII', 'MMMDLVIII', 'MMMDLIX', 'MMMDLX', 'MMMDLXI', 'MMMDLXII', 'MMMDLXIII', 'MMMDLXIV', 'MMMDLXV', 'MMMDLXVI', 'MMMDLXVII', 'MMMDLXVIII', 'MMMDLXIX', 'MMMDLXX', 'MMMDLXXI', 'MMMDLXXII', 'MMMDLXXIII', 'MMMDLXXIV', 'MMMDLXXV', 'MMMDLXXVI', 'MMMDLXXVII', 'MMMDLXXVIII', 'MMMDLXXIX', 'MMMDLXXX', 'MMMDLXXXI', 'MMMDLXXXII', 'MMMDLXXXIII', 'MMMDLXXXIV', 'MMMDLXXXV', 'MMMDLXXXVI', 'MMMDLXXXVII', 'MMMDLXXXVIII', 'MMMDLXXXIX', 'MMMDXC', 'MMMDXCI', 'MMMDXCII', 'MMMDXCIII', 'MMMDXCIV', 'MMMDVC', 'MMMDVCI', 'MMMDVCII', 'MMMDVCIII', 'MMMDVCIV', 'MMMDC', 'MMMDCI', 'MMMDCII', 'MMMDCIII', 'MMMDCIV', 'MMMDCV', 'MMMDCVI', 'MMMDCVII', 'MMMDCVIII', 'MMMDCIX', 'MMMDCX', 'MMMDCXI', 'MMMDCXII', 'MMMDCXIII', 'MMMDCXIV', 'MMMDCXV', 'MMMDCXVI', 'MMMDCXVII', 'MMMDCXVIII', 'MMMDCXIX', 'MMMDCXX', 'MMMDCXXI', 'MMMDCXXII', 'MMMDCXXIII', 'MMMDCXXIV', 'MMMDCXXV', 'MMMDCXXVI', 'MMMDCXXVII', 'MMMDCXXVIII', 'MMMDCXXIX', 'MMMDCXXX', 'MMMDCXXXI', 'MMMDCXXXII', 'MMMDCXXXIII', 'MMMDCXXXIV', 'MMMDCXXXV', 'MMMDCXXXVI', 'MMMDCXXXVII', 'MMMDCXXXVIII', 'MMMDCXXXIX', 'MMMDCXL', 'MMMDCXLI', 'MMMDCXLII', 'MMMDCXLIII', 'MMMDCXLIV', 'MMMDCVL', 'MMMDCVLI', 'MMMDCVLII', 'MMMDCVLIII', 'MMMDCVLIV', 'MMMDCL', 'MMMDCLI', 'MMMDCLII', 'MMMDCLIII', 'MMMDCLIV', 'MMMDCLV', 'MMMDCLVI', 'MMMDCLVII', 'MMMDCLVIII', 'MMMDCLIX', 'MMMDCLX', 'MMMDCLXI', 'MMMDCLXII', 'MMMDCLXIII', 'MMMDCLXIV', 'MMMDCLXV', 'MMMDCLXVI', 'MMMDCLXVII', 'MMMDCLXVIII', 'MMMDCLXIX', 'MMMDCLXX', 'MMMDCLXXI', 'MMMDCLXXII', 'MMMDCLXXIII', 'MMMDCLXXIV', 'MMMDCLXXV', 'MMMDCLXXVI', 'MMMDCLXXVII', 'MMMDCLXXVIII', 'MMMDCLXXIX', 'MMMDCLXXX', 'MMMDCLXXXI', 'MMMDCLXXXII', 'MMMDCLXXXIII', 'MMMDCLXXXIV', 'MMMDCLXXXV', 'MMMDCLXXXVI', 'MMMDCLXXXVII', 'MMMDCLXXXVIII', 'MMMDCLXXXIX', 'MMMDCXC', 'MMMDCXCI', 'MMMDCXCII', 'MMMDCXCIII', 'MMMDCXCIV', 'MMMDCVC', 'MMMDCVCI', 'MMMDCVCII', 'MMMDCVCIII', 'MMMDCVCIV', 'MMMDCC', 'MMMDCCI', 'MMMDCCII', 'MMMDCCIII', 'MMMDCCIV', 'MMMDCCV', 'MMMDCCVI', 'MMMDCCVII', 'MMMDCCVIII', 'MMMDCCIX', 'MMMDCCX', 'MMMDCCXI', 'MMMDCCXII', 'MMMDCCXIII', 'MMMDCCXIV', 'MMMDCCXV', 'MMMDCCXVI', 'MMMDCCXVII', 'MMMDCCXVIII', 'MMMDCCXIX', 'MMMDCCXX', 'MMMDCCXXI', 'MMMDCCXXII', 'MMMDCCXXIII', 'MMMDCCXXIV', 'MMMDCCXXV', 'MMMDCCXXVI', 'MMMDCCXXVII', 'MMMDCCXXVIII', 'MMMDCCXXIX', 'MMMDCCXXX', 'MMMDCCXXXI', 'MMMDCCXXXII', 'MMMDCCXXXIII', 'MMMDCCXXXIV', 'MMMDCCXXXV', 'MMMDCCXXXVI', 'MMMDCCXXXVII', 'MMMDCCXXXVIII', 'MMMDCCXXXIX', 'MMMDCCXL', 'MMMDCCXLI', 'MMMDCCXLII', 'MMMDCCXLIII', 'MMMDCCXLIV', 'MMMDCCVL', 'MMMDCCVLI', 'MMMDCCVLII', 'MMMDCCVLIII', 'MMMDCCVLIV', 'MMMDCCL', 'MMMDCCLI', 'MMMDCCLII', 'MMMDCCLIII', 'MMMDCCLIV', 'MMMDCCLV', 'MMMDCCLVI', 'MMMDCCLVII', 'MMMDCCLVIII', 'MMMDCCLIX', 'MMMDCCLX', 'MMMDCCLXI', 'MMMDCCLXII', 'MMMDCCLXIII', 'MMMDCCLXIV', 'MMMDCCLXV', 'MMMDCCLXVI', 'MMMDCCLXVII', 'MMMDCCLXVIII', 'MMMDCCLXIX', 'MMMDCCLXX', 'MMMDCCLXXI', 'MMMDCCLXXII', 'MMMDCCLXXIII', 'MMMDCCLXXIV', 'MMMDCCLXXV', 'MMMDCCLXXVI', 'MMMDCCLXXVII', 'MMMDCCLXXVIII', 'MMMDCCLXXIX', 'MMMDCCLXXX', 'MMMDCCLXXXI', 'MMMDCCLXXXII', 'MMMDCCLXXXIII', 'MMMDCCLXXXIV', 'MMMDCCLXXXV', 'MMMDCCLXXXVI', 'MMMDCCLXXXVII', 'MMMDCCLXXXVIII', 'MMMDCCLXXXIX', 'MMMDCCXC', 'MMMDCCXCI', 'MMMDCCXCII', 'MMMDCCXCIII', 'MMMDCCXCIV', 'MMMDCCVC', 'MMMDCCVCI', 'MMMDCCVCII', 'MMMDCCVCIII', 'MMMDCCVCIV', 'MMMDCCC', 'MMMDCCCI', 'MMMDCCCII', 'MMMDCCCIII', 'MMMDCCCIV', 'MMMDCCCV', 'MMMDCCCVI', 'MMMDCCCVII', 'MMMDCCCVIII', 'MMMDCCCIX', 'MMMDCCCX', 'MMMDCCCXI', 'MMMDCCCXII', 'MMMDCCCXIII', 'MMMDCCCXIV', 'MMMDCCCXV', 'MMMDCCCXVI', 'MMMDCCCXVII', 'MMMDCCCXVIII', 'MMMDCCCXIX', 'MMMDCCCXX', 'MMMDCCCXXI', 'MMMDCCCXXII', 'MMMDCCCXXIII', 'MMMDCCCXXIV', 'MMMDCCCXXV', 'MMMDCCCXXVI', 'MMMDCCCXXVII', 'MMMDCCCXXVIII', 'MMMDCCCXXIX', 'MMMDCCCXXX', 'MMMDCCCXXXI', 'MMMDCCCXXXII', 'MMMDCCCXXXIII', 'MMMDCCCXXXIV', 'MMMDCCCXXXV', 'MMMDCCCXXXVI', 'MMMDCCCXXXVII', 'MMMDCCCXXXVIII', 'MMMDCCCXXXIX', 'MMMDCCCXL', 'MMMDCCCXLI', 'MMMDCCCXLII', 'MMMDCCCXLIII', 'MMMDCCCXLIV', 'MMMDCCCVL', 'MMMDCCCVLI', 'MMMDCCCVLII', 'MMMDCCCVLIII', 'MMMDCCCVLIV', 'MMMDCCCL', 'MMMDCCCLI', 'MMMDCCCLII', 'MMMDCCCLIII', 'MMMDCCCLIV', 'MMMDCCCLV', 'MMMDCCCLVI', 'MMMDCCCLVII', 'MMMDCCCLVIII', 'MMMDCCCLIX', 'MMMDCCCLX', 'MMMDCCCLXI', 'MMMDCCCLXII', 'MMMDCCCLXIII', 'MMMDCCCLXIV', 'MMMDCCCLXV', 'MMMDCCCLXVI', 'MMMDCCCLXVII', 'MMMDCCCLXVIII', 'MMMDCCCLXIX', 'MMMDCCCLXX', 'MMMDCCCLXXI', 'MMMDCCCLXXII', 'MMMDCCCLXXIII', 'MMMDCCCLXXIV', 'MMMDCCCLXXV', 'MMMDCCCLXXVI', 'MMMDCCCLXXVII', 'MMMDCCCLXXVIII', 'MMMDCCCLXXIX', 'MMMDCCCLXXX', 'MMMDCCCLXXXI', 'MMMDCCCLXXXII', 'MMMDCCCLXXXIII', 'MMMDCCCLXXXIV', 'MMMDCCCLXXXV', 'MMMDCCCLXXXVI', 'MMMDCCCLXXXVII', 'MMMDCCCLXXXVIII', 'MMMDCCCLXXXIX', 'MMMDCCCXC', 'MMMDCCCXCI', 'MMMDCCCXCII', 'MMMDCCCXCIII', 'MMMDCCCXCIV', 'MMMDCCCVC', 'MMMDCCCVCI', 'MMMDCCCVCII', 'MMMDCCCVCIII', 'MMMDCCCVCIV', 'MMMCM', 'MMMCMI', 'MMMCMII', 'MMMCMIII', 'MMMCMIV', 'MMMCMV', 'MMMCMVI', 'MMMCMVII', 'MMMCMVIII', 'MMMCMIX', 'MMMCMX', 'MMMCMXI', 'MMMCMXII', 'MMMCMXIII', 'MMMCMXIV', 'MMMCMXV', 'MMMCMXVI', 'MMMCMXVII', 'MMMCMXVIII', 'MMMCMXIX', 'MMMCMXX', 'MMMCMXXI', 'MMMCMXXII', 'MMMCMXXIII', 'MMMCMXXIV', 'MMMCMXXV', 'MMMCMXXVI', 'MMMCMXXVII', 'MMMCMXXVIII', 'MMMCMXXIX', 'MMMCMXXX', 'MMMCMXXXI', 'MMMCMXXXII', 'MMMCMXXXIII', 'MMMCMXXXIV', 'MMMCMXXXV', 'MMMCMXXXVI', 'MMMCMXXXVII', 'MMMCMXXXVIII', 'MMMCMXXXIX', 'MMMCMXL', 'MMMCMXLI', 'MMMCMXLII', 'MMMCMXLIII', 'MMMCMXLIV', 'MMMCMVL', 'MMMCMVLI', 'MMMCMVLII', 'MMMCMVLIII', 'MMMCMVLIV', 'MMMLM', 'MMMLMI', 'MMMLMII', 'MMMLMIII', 'MMMLMIV', 'MMMLMV', 'MMMLMVI', 'MMMLMVII', 'MMMLMVIII', 'MMMLMIX', 'MMMLMX', 'MMMLMXI', 'MMMLMXII', 'MMMLMXIII', 'MMMLMXIV', 'MMMLMXV', 'MMMLMXVI', 'MMMLMXVII', 'MMMLMXVIII', 'MMMLMXIX', 'MMMLMXX', 'MMMLMXXI', 'MMMLMXXII', 'MMMLMXXIII', 'MMMLMXXIV', 'MMMLMXXV', 'MMMLMXXVI', 'MMMLMXXVII', 'MMMLMXXVIII', 'MMMLMXXIX', 'MMMLMXXX', 'MMMLMXXXI', 'MMMLMXXXII', 'MMMLMXXXIII', 'MMMLMXXXIV', 'MMMLMXXXV', 'MMMLMXXXVI', 'MMMLMXXXVII', 'MMMLMXXXVIII', 'MMMLMXXXIX', 'MMMLMXL', 'MMMLMXLI', 'MMMLMXLII', 'MMMLMXLIII', 'MMMLMXLIV', 'MMMLMVL', 'MMMLMVLI', 'MMMLMVLII', 'MMMLMVLIII', 'MMMLMVLIV', ] -const mode2 = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX', 'XXI', 'XXII', 'XXIII', 'XXIV', 'XXV', 'XXVI', 'XXVII', 'XXVIII', 'XXIX', 'XXX', 'XXXI', 'XXXII', 'XXXIII', 'XXXIV', 'XXXV', 'XXXVI', 'XXXVII', 'XXXVIII', 'XXXIX', 'XL', 'XLI', 'XLII', 'XLIII', 'XLIV', 'VL', 'VLI', 'VLII', 'VLIII', 'IL', 'L', 'LI', 'LII', 'LIII', 'LIV', 'LV', 'LVI', 'LVII', 'LVIII', 'LIX', 'LX', 'LXI', 'LXII', 'LXIII', 'LXIV', 'LXV', 'LXVI', 'LXVII', 'LXVIII', 'LXIX', 'LXX', 'LXXI', 'LXXII', 'LXXIII', 'LXXIV', 'LXXV', 'LXXVI', 'LXXVII', 'LXXVIII', 'LXXIX', 'LXXX', 'LXXXI', 'LXXXII', 'LXXXIII', 'LXXXIV', 'LXXXV', 'LXXXVI', 'LXXXVII', 'LXXXVIII', 'LXXXIX', 'XC', 'XCI', 'XCII', 'XCIII', 'XCIV', 'VC', 'VCI', 'VCII', 'VCIII', 'IC', 'C', 'CI', 'CII', 'CIII', 'CIV', 'CV', 'CVI', 'CVII', 'CVIII', 'CIX', 'CX', 'CXI', 'CXII', 'CXIII', 'CXIV', 'CXV', 'CXVI', 'CXVII', 'CXVIII', 'CXIX', 'CXX', 'CXXI', 'CXXII', 'CXXIII', 'CXXIV', 'CXXV', 'CXXVI', 'CXXVII', 'CXXVIII', 'CXXIX', 'CXXX', 'CXXXI', 'CXXXII', 'CXXXIII', 'CXXXIV', 'CXXXV', 'CXXXVI', 'CXXXVII', 'CXXXVIII', 'CXXXIX', 'CXL', 'CXLI', 'CXLII', 'CXLIII', 'CXLIV', 'CVL', 'CVLI', 'CVLII', 'CVLIII', 'CIL', 'CL', 'CLI', 'CLII', 'CLIII', 'CLIV', 'CLV', 'CLVI', 'CLVII', 'CLVIII', 'CLIX', 'CLX', 'CLXI', 'CLXII', 'CLXIII', 'CLXIV', 'CLXV', 'CLXVI', 'CLXVII', 'CLXVIII', 'CLXIX', 'CLXX', 'CLXXI', 'CLXXII', 'CLXXIII', 'CLXXIV', 'CLXXV', 'CLXXVI', 'CLXXVII', 'CLXXVIII', 'CLXXIX', 'CLXXX', 'CLXXXI', 'CLXXXII', 'CLXXXIII', 'CLXXXIV', 'CLXXXV', 'CLXXXVI', 'CLXXXVII', 'CLXXXVIII', 'CLXXXIX', 'CXC', 'CXCI', 'CXCII', 'CXCIII', 'CXCIV', 'CVC', 'CVCI', 'CVCII', 'CVCIII', 'CIC', 'CC', 'CCI', 'CCII', 'CCIII', 'CCIV', 'CCV', 'CCVI', 'CCVII', 'CCVIII', 'CCIX', 'CCX', 'CCXI', 'CCXII', 'CCXIII', 'CCXIV', 'CCXV', 'CCXVI', 'CCXVII', 'CCXVIII', 'CCXIX', 'CCXX', 'CCXXI', 'CCXXII', 'CCXXIII', 'CCXXIV', 'CCXXV', 'CCXXVI', 'CCXXVII', 'CCXXVIII', 'CCXXIX', 'CCXXX', 'CCXXXI', 'CCXXXII', 'CCXXXIII', 'CCXXXIV', 'CCXXXV', 'CCXXXVI', 'CCXXXVII', 'CCXXXVIII', 'CCXXXIX', 'CCXL', 'CCXLI', 'CCXLII', 'CCXLIII', 'CCXLIV', 'CCVL', 'CCVLI', 'CCVLII', 'CCVLIII', 'CCIL', 'CCL', 'CCLI', 'CCLII', 'CCLIII', 'CCLIV', 'CCLV', 'CCLVI', 'CCLVII', 'CCLVIII', 'CCLIX', 'CCLX', 'CCLXI', 'CCLXII', 'CCLXIII', 'CCLXIV', 'CCLXV', 'CCLXVI', 'CCLXVII', 'CCLXVIII', 'CCLXIX', 'CCLXX', 'CCLXXI', 'CCLXXII', 'CCLXXIII', 'CCLXXIV', 'CCLXXV', 'CCLXXVI', 'CCLXXVII', 'CCLXXVIII', 'CCLXXIX', 'CCLXXX', 'CCLXXXI', 'CCLXXXII', 'CCLXXXIII', 'CCLXXXIV', 'CCLXXXV', 'CCLXXXVI', 'CCLXXXVII', 'CCLXXXVIII', 'CCLXXXIX', 'CCXC', 'CCXCI', 'CCXCII', 'CCXCIII', 'CCXCIV', 'CCVC', 'CCVCI', 'CCVCII', 'CCVCIII', 'CCIC', 'CCC', 'CCCI', 'CCCII', 'CCCIII', 'CCCIV', 'CCCV', 'CCCVI', 'CCCVII', 'CCCVIII', 'CCCIX', 'CCCX', 'CCCXI', 'CCCXII', 'CCCXIII', 'CCCXIV', 'CCCXV', 'CCCXVI', 'CCCXVII', 'CCCXVIII', 'CCCXIX', 'CCCXX', 'CCCXXI', 'CCCXXII', 'CCCXXIII', 'CCCXXIV', 'CCCXXV', 'CCCXXVI', 'CCCXXVII', 'CCCXXVIII', 'CCCXXIX', 'CCCXXX', 'CCCXXXI', 'CCCXXXII', 'CCCXXXIII', 'CCCXXXIV', 'CCCXXXV', 'CCCXXXVI', 'CCCXXXVII', 'CCCXXXVIII', 'CCCXXXIX', 'CCCXL', 'CCCXLI', 'CCCXLII', 'CCCXLIII', 'CCCXLIV', 'CCCVL', 'CCCVLI', 'CCCVLII', 'CCCVLIII', 'CCCIL', 'CCCL', 'CCCLI', 'CCCLII', 'CCCLIII', 'CCCLIV', 'CCCLV', 'CCCLVI', 'CCCLVII', 'CCCLVIII', 'CCCLIX', 'CCCLX', 'CCCLXI', 'CCCLXII', 'CCCLXIII', 'CCCLXIV', 'CCCLXV', 'CCCLXVI', 'CCCLXVII', 'CCCLXVIII', 'CCCLXIX', 'CCCLXX', 'CCCLXXI', 'CCCLXXII', 'CCCLXXIII', 'CCCLXXIV', 'CCCLXXV', 'CCCLXXVI', 'CCCLXXVII', 'CCCLXXVIII', 'CCCLXXIX', 'CCCLXXX', 'CCCLXXXI', 'CCCLXXXII', 'CCCLXXXIII', 'CCCLXXXIV', 'CCCLXXXV', 'CCCLXXXVI', 'CCCLXXXVII', 'CCCLXXXVIII', 'CCCLXXXIX', 'CCCXC', 'CCCXCI', 'CCCXCII', 'CCCXCIII', 'CCCXCIV', 'CCCVC', 'CCCVCI', 'CCCVCII', 'CCCVCIII', 'CCCIC', 'CD', 'CDI', 'CDII', 'CDIII', 'CDIV', 'CDV', 'CDVI', 'CDVII', 'CDVIII', 'CDIX', 'CDX', 'CDXI', 'CDXII', 'CDXIII', 'CDXIV', 'CDXV', 'CDXVI', 'CDXVII', 'CDXVIII', 'CDXIX', 'CDXX', 'CDXXI', 'CDXXII', 'CDXXIII', 'CDXXIV', 'CDXXV', 'CDXXVI', 'CDXXVII', 'CDXXVIII', 'CDXXIX', 'CDXXX', 'CDXXXI', 'CDXXXII', 'CDXXXIII', 'CDXXXIV', 'CDXXXV', 'CDXXXVI', 'CDXXXVII', 'CDXXXVIII', 'CDXXXIX', 'CDXL', 'CDXLI', 'CDXLII', 'CDXLIII', 'CDXLIV', 'CDVL', 'CDVLI', 'CDVLII', 'CDVLIII', 'CDIL', 'LD', 'LDI', 'LDII', 'LDIII', 'LDIV', 'LDV', 'LDVI', 'LDVII', 'LDVIII', 'LDIX', 'LDX', 'LDXI', 'LDXII', 'LDXIII', 'LDXIV', 'LDXV', 'LDXVI', 'LDXVII', 'LDXVIII', 'LDXIX', 'LDXX', 'LDXXI', 'LDXXII', 'LDXXIII', 'LDXXIV', 'LDXXV', 'LDXXVI', 'LDXXVII', 'LDXXVIII', 'LDXXIX', 'LDXXX', 'LDXXXI', 'LDXXXII', 'LDXXXIII', 'LDXXXIV', 'LDXXXV', 'LDXXXVI', 'LDXXXVII', 'LDXXXVIII', 'LDXXXIX', 'XD', 'XDI', 'XDII', 'XDIII', 'XDIV', 'XDV', 'XDVI', 'XDVII', 'XDVIII', 'XDIX', 'D', 'DI', 'DII', 'DIII', 'DIV', 'DV', 'DVI', 'DVII', 'DVIII', 'DIX', 'DX', 'DXI', 'DXII', 'DXIII', 'DXIV', 'DXV', 'DXVI', 'DXVII', 'DXVIII', 'DXIX', 'DXX', 'DXXI', 'DXXII', 'DXXIII', 'DXXIV', 'DXXV', 'DXXVI', 'DXXVII', 'DXXVIII', 'DXXIX', 'DXXX', 'DXXXI', 'DXXXII', 'DXXXIII', 'DXXXIV', 'DXXXV', 'DXXXVI', 'DXXXVII', 'DXXXVIII', 'DXXXIX', 'DXL', 'DXLI', 'DXLII', 'DXLIII', 'DXLIV', 'DVL', 'DVLI', 'DVLII', 'DVLIII', 'DIL', 'DL', 'DLI', 'DLII', 'DLIII', 'DLIV', 'DLV', 'DLVI', 'DLVII', 'DLVIII', 'DLIX', 'DLX', 'DLXI', 'DLXII', 'DLXIII', 'DLXIV', 'DLXV', 'DLXVI', 'DLXVII', 'DLXVIII', 'DLXIX', 'DLXX', 'DLXXI', 'DLXXII', 'DLXXIII', 'DLXXIV', 'DLXXV', 'DLXXVI', 'DLXXVII', 'DLXXVIII', 'DLXXIX', 'DLXXX', 'DLXXXI', 'DLXXXII', 'DLXXXIII', 'DLXXXIV', 'DLXXXV', 'DLXXXVI', 'DLXXXVII', 'DLXXXVIII', 'DLXXXIX', 'DXC', 'DXCI', 'DXCII', 'DXCIII', 'DXCIV', 'DVC', 'DVCI', 'DVCII', 'DVCIII', 'DIC', 'DC', 'DCI', 'DCII', 'DCIII', 'DCIV', 'DCV', 'DCVI', 'DCVII', 'DCVIII', 'DCIX', 'DCX', 'DCXI', 'DCXII', 'DCXIII', 'DCXIV', 'DCXV', 'DCXVI', 'DCXVII', 'DCXVIII', 'DCXIX', 'DCXX', 'DCXXI', 'DCXXII', 'DCXXIII', 'DCXXIV', 'DCXXV', 'DCXXVI', 'DCXXVII', 'DCXXVIII', 'DCXXIX', 'DCXXX', 'DCXXXI', 'DCXXXII', 'DCXXXIII', 'DCXXXIV', 'DCXXXV', 'DCXXXVI', 'DCXXXVII', 'DCXXXVIII', 'DCXXXIX', 'DCXL', 'DCXLI', 'DCXLII', 'DCXLIII', 'DCXLIV', 'DCVL', 'DCVLI', 'DCVLII', 'DCVLIII', 'DCIL', 'DCL', 'DCLI', 'DCLII', 'DCLIII', 'DCLIV', 'DCLV', 'DCLVI', 'DCLVII', 'DCLVIII', 'DCLIX', 'DCLX', 'DCLXI', 'DCLXII', 'DCLXIII', 'DCLXIV', 'DCLXV', 'DCLXVI', 'DCLXVII', 'DCLXVIII', 'DCLXIX', 'DCLXX', 'DCLXXI', 'DCLXXII', 'DCLXXIII', 'DCLXXIV', 'DCLXXV', 'DCLXXVI', 'DCLXXVII', 'DCLXXVIII', 'DCLXXIX', 'DCLXXX', 'DCLXXXI', 'DCLXXXII', 'DCLXXXIII', 'DCLXXXIV', 'DCLXXXV', 'DCLXXXVI', 'DCLXXXVII', 'DCLXXXVIII', 'DCLXXXIX', 'DCXC', 'DCXCI', 'DCXCII', 'DCXCIII', 'DCXCIV', 'DCVC', 'DCVCI', 'DCVCII', 'DCVCIII', 'DCIC', 'DCC', 'DCCI', 'DCCII', 'DCCIII', 'DCCIV', 'DCCV', 'DCCVI', 'DCCVII', 'DCCVIII', 'DCCIX', 'DCCX', 'DCCXI', 'DCCXII', 'DCCXIII', 'DCCXIV', 'DCCXV', 'DCCXVI', 'DCCXVII', 'DCCXVIII', 'DCCXIX', 'DCCXX', 'DCCXXI', 'DCCXXII', 'DCCXXIII', 'DCCXXIV', 'DCCXXV', 'DCCXXVI', 'DCCXXVII', 'DCCXXVIII', 'DCCXXIX', 'DCCXXX', 'DCCXXXI', 'DCCXXXII', 'DCCXXXIII', 'DCCXXXIV', 'DCCXXXV', 'DCCXXXVI', 'DCCXXXVII', 'DCCXXXVIII', 'DCCXXXIX', 'DCCXL', 'DCCXLI', 'DCCXLII', 'DCCXLIII', 'DCCXLIV', 'DCCVL', 'DCCVLI', 'DCCVLII', 'DCCVLIII', 'DCCIL', 'DCCL', 'DCCLI', 'DCCLII', 'DCCLIII', 'DCCLIV', 'DCCLV', 'DCCLVI', 'DCCLVII', 'DCCLVIII', 'DCCLIX', 'DCCLX', 'DCCLXI', 'DCCLXII', 'DCCLXIII', 'DCCLXIV', 'DCCLXV', 'DCCLXVI', 'DCCLXVII', 'DCCLXVIII', 'DCCLXIX', 'DCCLXX', 'DCCLXXI', 'DCCLXXII', 'DCCLXXIII', 'DCCLXXIV', 'DCCLXXV', 'DCCLXXVI', 'DCCLXXVII', 'DCCLXXVIII', 'DCCLXXIX', 'DCCLXXX', 'DCCLXXXI', 'DCCLXXXII', 'DCCLXXXIII', 'DCCLXXXIV', 'DCCLXXXV', 'DCCLXXXVI', 'DCCLXXXVII', 'DCCLXXXVIII', 'DCCLXXXIX', 'DCCXC', 'DCCXCI', 'DCCXCII', 'DCCXCIII', 'DCCXCIV', 'DCCVC', 'DCCVCI', 'DCCVCII', 'DCCVCIII', 'DCCIC', 'DCCC', 'DCCCI', 'DCCCII', 'DCCCIII', 'DCCCIV', 'DCCCV', 'DCCCVI', 'DCCCVII', 'DCCCVIII', 'DCCCIX', 'DCCCX', 'DCCCXI', 'DCCCXII', 'DCCCXIII', 'DCCCXIV', 'DCCCXV', 'DCCCXVI', 'DCCCXVII', 'DCCCXVIII', 'DCCCXIX', 'DCCCXX', 'DCCCXXI', 'DCCCXXII', 'DCCCXXIII', 'DCCCXXIV', 'DCCCXXV', 'DCCCXXVI', 'DCCCXXVII', 'DCCCXXVIII', 'DCCCXXIX', 'DCCCXXX', 'DCCCXXXI', 'DCCCXXXII', 'DCCCXXXIII', 'DCCCXXXIV', 'DCCCXXXV', 'DCCCXXXVI', 'DCCCXXXVII', 'DCCCXXXVIII', 'DCCCXXXIX', 'DCCCXL', 'DCCCXLI', 'DCCCXLII', 'DCCCXLIII', 'DCCCXLIV', 'DCCCVL', 'DCCCVLI', 'DCCCVLII', 'DCCCVLIII', 'DCCCIL', 'DCCCL', 'DCCCLI', 'DCCCLII', 'DCCCLIII', 'DCCCLIV', 'DCCCLV', 'DCCCLVI', 'DCCCLVII', 'DCCCLVIII', 'DCCCLIX', 'DCCCLX', 'DCCCLXI', 'DCCCLXII', 'DCCCLXIII', 'DCCCLXIV', 'DCCCLXV', 'DCCCLXVI', 'DCCCLXVII', 'DCCCLXVIII', 'DCCCLXIX', 'DCCCLXX', 'DCCCLXXI', 'DCCCLXXII', 'DCCCLXXIII', 'DCCCLXXIV', 'DCCCLXXV', 'DCCCLXXVI', 'DCCCLXXVII', 'DCCCLXXVIII', 'DCCCLXXIX', 'DCCCLXXX', 'DCCCLXXXI', 'DCCCLXXXII', 'DCCCLXXXIII', 'DCCCLXXXIV', 'DCCCLXXXV', 'DCCCLXXXVI', 'DCCCLXXXVII', 'DCCCLXXXVIII', 'DCCCLXXXIX', 'DCCCXC', 'DCCCXCI', 'DCCCXCII', 'DCCCXCIII', 'DCCCXCIV', 'DCCCVC', 'DCCCVCI', 'DCCCVCII', 'DCCCVCIII', 'DCCCIC', 'CM', 'CMI', 'CMII', 'CMIII', 'CMIV', 'CMV', 'CMVI', 'CMVII', 'CMVIII', 'CMIX', 'CMX', 'CMXI', 'CMXII', 'CMXIII', 'CMXIV', 'CMXV', 'CMXVI', 'CMXVII', 'CMXVIII', 'CMXIX', 'CMXX', 'CMXXI', 'CMXXII', 'CMXXIII', 'CMXXIV', 'CMXXV', 'CMXXVI', 'CMXXVII', 'CMXXVIII', 'CMXXIX', 'CMXXX', 'CMXXXI', 'CMXXXII', 'CMXXXIII', 'CMXXXIV', 'CMXXXV', 'CMXXXVI', 'CMXXXVII', 'CMXXXVIII', 'CMXXXIX', 'CMXL', 'CMXLI', 'CMXLII', 'CMXLIII', 'CMXLIV', 'CMVL', 'CMVLI', 'CMVLII', 'CMVLIII', 'CMIL', 'LM', 'LMI', 'LMII', 'LMIII', 'LMIV', 'LMV', 'LMVI', 'LMVII', 'LMVIII', 'LMIX', 'LMX', 'LMXI', 'LMXII', 'LMXIII', 'LMXIV', 'LMXV', 'LMXVI', 'LMXVII', 'LMXVIII', 'LMXIX', 'LMXX', 'LMXXI', 'LMXXII', 'LMXXIII', 'LMXXIV', 'LMXXV', 'LMXXVI', 'LMXXVII', 'LMXXVIII', 'LMXXIX', 'LMXXX', 'LMXXXI', 'LMXXXII', 'LMXXXIII', 'LMXXXIV', 'LMXXXV', 'LMXXXVI', 'LMXXXVII', 'LMXXXVIII', 'LMXXXIX', 'XM', 'XMI', 'XMII', 'XMIII', 'XMIV', 'XMV', 'XMVI', 'XMVII', 'XMVIII', 'XMIX', 'M', 'MI', 'MII', 'MIII', 'MIV', 'MV', 'MVI', 'MVII', 'MVIII', 'MIX', 'MX', 'MXI', 'MXII', 'MXIII', 'MXIV', 'MXV', 'MXVI', 'MXVII', 'MXVIII', 'MXIX', 'MXX', 'MXXI', 'MXXII', 'MXXIII', 'MXXIV', 'MXXV', 'MXXVI', 'MXXVII', 'MXXVIII', 'MXXIX', 'MXXX', 'MXXXI', 'MXXXII', 'MXXXIII', 'MXXXIV', 'MXXXV', 'MXXXVI', 'MXXXVII', 'MXXXVIII', 'MXXXIX', 'MXL', 'MXLI', 'MXLII', 'MXLIII', 'MXLIV', 'MVL', 'MVLI', 'MVLII', 'MVLIII', 'MIL', 'ML', 'MLI', 'MLII', 'MLIII', 'MLIV', 'MLV', 'MLVI', 'MLVII', 'MLVIII', 'MLIX', 'MLX', 'MLXI', 'MLXII', 'MLXIII', 'MLXIV', 'MLXV', 'MLXVI', 'MLXVII', 'MLXVIII', 'MLXIX', 'MLXX', 'MLXXI', 'MLXXII', 'MLXXIII', 'MLXXIV', 'MLXXV', 'MLXXVI', 'MLXXVII', 'MLXXVIII', 'MLXXIX', 'MLXXX', 'MLXXXI', 'MLXXXII', 'MLXXXIII', 'MLXXXIV', 'MLXXXV', 'MLXXXVI', 'MLXXXVII', 'MLXXXVIII', 'MLXXXIX', 'MXC', 'MXCI', 'MXCII', 'MXCIII', 'MXCIV', 'MVC', 'MVCI', 'MVCII', 'MVCIII', 'MIC', 'MC', 'MCI', 'MCII', 'MCIII', 'MCIV', 'MCV', 'MCVI', 'MCVII', 'MCVIII', 'MCIX', 'MCX', 'MCXI', 'MCXII', 'MCXIII', 'MCXIV', 'MCXV', 'MCXVI', 'MCXVII', 'MCXVIII', 'MCXIX', 'MCXX', 'MCXXI', 'MCXXII', 'MCXXIII', 'MCXXIV', 'MCXXV', 'MCXXVI', 'MCXXVII', 'MCXXVIII', 'MCXXIX', 'MCXXX', 'MCXXXI', 'MCXXXII', 'MCXXXIII', 'MCXXXIV', 'MCXXXV', 'MCXXXVI', 'MCXXXVII', 'MCXXXVIII', 'MCXXXIX', 'MCXL', 'MCXLI', 'MCXLII', 'MCXLIII', 'MCXLIV', 'MCVL', 'MCVLI', 'MCVLII', 'MCVLIII', 'MCIL', 'MCL', 'MCLI', 'MCLII', 'MCLIII', 'MCLIV', 'MCLV', 'MCLVI', 'MCLVII', 'MCLVIII', 'MCLIX', 'MCLX', 'MCLXI', 'MCLXII', 'MCLXIII', 'MCLXIV', 'MCLXV', 'MCLXVI', 'MCLXVII', 'MCLXVIII', 'MCLXIX', 'MCLXX', 'MCLXXI', 'MCLXXII', 'MCLXXIII', 'MCLXXIV', 'MCLXXV', 'MCLXXVI', 'MCLXXVII', 'MCLXXVIII', 'MCLXXIX', 'MCLXXX', 'MCLXXXI', 'MCLXXXII', 'MCLXXXIII', 'MCLXXXIV', 'MCLXXXV', 'MCLXXXVI', 'MCLXXXVII', 'MCLXXXVIII', 'MCLXXXIX', 'MCXC', 'MCXCI', 'MCXCII', 'MCXCIII', 'MCXCIV', 'MCVC', 'MCVCI', 'MCVCII', 'MCVCIII', 'MCIC', 'MCC', 'MCCI', 'MCCII', 'MCCIII', 'MCCIV', 'MCCV', 'MCCVI', 'MCCVII', 'MCCVIII', 'MCCIX', 'MCCX', 'MCCXI', 'MCCXII', 'MCCXIII', 'MCCXIV', 'MCCXV', 'MCCXVI', 'MCCXVII', 'MCCXVIII', 'MCCXIX', 'MCCXX', 'MCCXXI', 'MCCXXII', 'MCCXXIII', 'MCCXXIV', 'MCCXXV', 'MCCXXVI', 'MCCXXVII', 'MCCXXVIII', 'MCCXXIX', 'MCCXXX', 'MCCXXXI', 'MCCXXXII', 'MCCXXXIII', 'MCCXXXIV', 'MCCXXXV', 'MCCXXXVI', 'MCCXXXVII', 'MCCXXXVIII', 'MCCXXXIX', 'MCCXL', 'MCCXLI', 'MCCXLII', 'MCCXLIII', 'MCCXLIV', 'MCCVL', 'MCCVLI', 'MCCVLII', 'MCCVLIII', 'MCCIL', 'MCCL', 'MCCLI', 'MCCLII', 'MCCLIII', 'MCCLIV', 'MCCLV', 'MCCLVI', 'MCCLVII', 'MCCLVIII', 'MCCLIX', 'MCCLX', 'MCCLXI', 'MCCLXII', 'MCCLXIII', 'MCCLXIV', 'MCCLXV', 'MCCLXVI', 'MCCLXVII', 'MCCLXVIII', 'MCCLXIX', 'MCCLXX', 'MCCLXXI', 'MCCLXXII', 'MCCLXXIII', 'MCCLXXIV', 'MCCLXXV', 'MCCLXXVI', 'MCCLXXVII', 'MCCLXXVIII', 'MCCLXXIX', 'MCCLXXX', 'MCCLXXXI', 'MCCLXXXII', 'MCCLXXXIII', 'MCCLXXXIV', 'MCCLXXXV', 'MCCLXXXVI', 'MCCLXXXVII', 'MCCLXXXVIII', 'MCCLXXXIX', 'MCCXC', 'MCCXCI', 'MCCXCII', 'MCCXCIII', 'MCCXCIV', 'MCCVC', 'MCCVCI', 'MCCVCII', 'MCCVCIII', 'MCCIC', 'MCCC', 'MCCCI', 'MCCCII', 'MCCCIII', 'MCCCIV', 'MCCCV', 'MCCCVI', 'MCCCVII', 'MCCCVIII', 'MCCCIX', 'MCCCX', 'MCCCXI', 'MCCCXII', 'MCCCXIII', 'MCCCXIV', 'MCCCXV', 'MCCCXVI', 'MCCCXVII', 'MCCCXVIII', 'MCCCXIX', 'MCCCXX', 'MCCCXXI', 'MCCCXXII', 'MCCCXXIII', 'MCCCXXIV', 'MCCCXXV', 'MCCCXXVI', 'MCCCXXVII', 'MCCCXXVIII', 'MCCCXXIX', 'MCCCXXX', 'MCCCXXXI', 'MCCCXXXII', 'MCCCXXXIII', 'MCCCXXXIV', 'MCCCXXXV', 'MCCCXXXVI', 'MCCCXXXVII', 'MCCCXXXVIII', 'MCCCXXXIX', 'MCCCXL', 'MCCCXLI', 'MCCCXLII', 'MCCCXLIII', 'MCCCXLIV', 'MCCCVL', 'MCCCVLI', 'MCCCVLII', 'MCCCVLIII', 'MCCCIL', 'MCCCL', 'MCCCLI', 'MCCCLII', 'MCCCLIII', 'MCCCLIV', 'MCCCLV', 'MCCCLVI', 'MCCCLVII', 'MCCCLVIII', 'MCCCLIX', 'MCCCLX', 'MCCCLXI', 'MCCCLXII', 'MCCCLXIII', 'MCCCLXIV', 'MCCCLXV', 'MCCCLXVI', 'MCCCLXVII', 'MCCCLXVIII', 'MCCCLXIX', 'MCCCLXX', 'MCCCLXXI', 'MCCCLXXII', 'MCCCLXXIII', 'MCCCLXXIV', 'MCCCLXXV', 'MCCCLXXVI', 'MCCCLXXVII', 'MCCCLXXVIII', 'MCCCLXXIX', 'MCCCLXXX', 'MCCCLXXXI', 'MCCCLXXXII', 'MCCCLXXXIII', 'MCCCLXXXIV', 'MCCCLXXXV', 'MCCCLXXXVI', 'MCCCLXXXVII', 'MCCCLXXXVIII', 'MCCCLXXXIX', 'MCCCXC', 'MCCCXCI', 'MCCCXCII', 'MCCCXCIII', 'MCCCXCIV', 'MCCCVC', 'MCCCVCI', 'MCCCVCII', 'MCCCVCIII', 'MCCCIC', 'MCD', 'MCDI', 'MCDII', 'MCDIII', 'MCDIV', 'MCDV', 'MCDVI', 'MCDVII', 'MCDVIII', 'MCDIX', 'MCDX', 'MCDXI', 'MCDXII', 'MCDXIII', 'MCDXIV', 'MCDXV', 'MCDXVI', 'MCDXVII', 'MCDXVIII', 'MCDXIX', 'MCDXX', 'MCDXXI', 'MCDXXII', 'MCDXXIII', 'MCDXXIV', 'MCDXXV', 'MCDXXVI', 'MCDXXVII', 'MCDXXVIII', 'MCDXXIX', 'MCDXXX', 'MCDXXXI', 'MCDXXXII', 'MCDXXXIII', 'MCDXXXIV', 'MCDXXXV', 'MCDXXXVI', 'MCDXXXVII', 'MCDXXXVIII', 'MCDXXXIX', 'MCDXL', 'MCDXLI', 'MCDXLII', 'MCDXLIII', 'MCDXLIV', 'MCDVL', 'MCDVLI', 'MCDVLII', 'MCDVLIII', 'MCDIL', 'MLD', 'MLDI', 'MLDII', 'MLDIII', 'MLDIV', 'MLDV', 'MLDVI', 'MLDVII', 'MLDVIII', 'MLDIX', 'MLDX', 'MLDXI', 'MLDXII', 'MLDXIII', 'MLDXIV', 'MLDXV', 'MLDXVI', 'MLDXVII', 'MLDXVIII', 'MLDXIX', 'MLDXX', 'MLDXXI', 'MLDXXII', 'MLDXXIII', 'MLDXXIV', 'MLDXXV', 'MLDXXVI', 'MLDXXVII', 'MLDXXVIII', 'MLDXXIX', 'MLDXXX', 'MLDXXXI', 'MLDXXXII', 'MLDXXXIII', 'MLDXXXIV', 'MLDXXXV', 'MLDXXXVI', 'MLDXXXVII', 'MLDXXXVIII', 'MLDXXXIX', 'MXD', 'MXDI', 'MXDII', 'MXDIII', 'MXDIV', 'MXDV', 'MXDVI', 'MXDVII', 'MXDVIII', 'MXDIX', 'MD', 'MDI', 'MDII', 'MDIII', 'MDIV', 'MDV', 'MDVI', 'MDVII', 'MDVIII', 'MDIX', 'MDX', 'MDXI', 'MDXII', 'MDXIII', 'MDXIV', 'MDXV', 'MDXVI', 'MDXVII', 'MDXVIII', 'MDXIX', 'MDXX', 'MDXXI', 'MDXXII', 'MDXXIII', 'MDXXIV', 'MDXXV', 'MDXXVI', 'MDXXVII', 'MDXXVIII', 'MDXXIX', 'MDXXX', 'MDXXXI', 'MDXXXII', 'MDXXXIII', 'MDXXXIV', 'MDXXXV', 'MDXXXVI', 'MDXXXVII', 'MDXXXVIII', 'MDXXXIX', 'MDXL', 'MDXLI', 'MDXLII', 'MDXLIII', 'MDXLIV', 'MDVL', 'MDVLI', 'MDVLII', 'MDVLIII', 'MDIL', 'MDL', 'MDLI', 'MDLII', 'MDLIII', 'MDLIV', 'MDLV', 'MDLVI', 'MDLVII', 'MDLVIII', 'MDLIX', 'MDLX', 'MDLXI', 'MDLXII', 'MDLXIII', 'MDLXIV', 'MDLXV', 'MDLXVI', 'MDLXVII', 'MDLXVIII', 'MDLXIX', 'MDLXX', 'MDLXXI', 'MDLXXII', 'MDLXXIII', 'MDLXXIV', 'MDLXXV', 'MDLXXVI', 'MDLXXVII', 'MDLXXVIII', 'MDLXXIX', 'MDLXXX', 'MDLXXXI', 'MDLXXXII', 'MDLXXXIII', 'MDLXXXIV', 'MDLXXXV', 'MDLXXXVI', 'MDLXXXVII', 'MDLXXXVIII', 'MDLXXXIX', 'MDXC', 'MDXCI', 'MDXCII', 'MDXCIII', 'MDXCIV', 'MDVC', 'MDVCI', 'MDVCII', 'MDVCIII', 'MDIC', 'MDC', 'MDCI', 'MDCII', 'MDCIII', 'MDCIV', 'MDCV', 'MDCVI', 'MDCVII', 'MDCVIII', 'MDCIX', 'MDCX', 'MDCXI', 'MDCXII', 'MDCXIII', 'MDCXIV', 'MDCXV', 'MDCXVI', 'MDCXVII', 'MDCXVIII', 'MDCXIX', 'MDCXX', 'MDCXXI', 'MDCXXII', 'MDCXXIII', 'MDCXXIV', 'MDCXXV', 'MDCXXVI', 'MDCXXVII', 'MDCXXVIII', 'MDCXXIX', 'MDCXXX', 'MDCXXXI', 'MDCXXXII', 'MDCXXXIII', 'MDCXXXIV', 'MDCXXXV', 'MDCXXXVI', 'MDCXXXVII', 'MDCXXXVIII', 'MDCXXXIX', 'MDCXL', 'MDCXLI', 'MDCXLII', 'MDCXLIII', 'MDCXLIV', 'MDCVL', 'MDCVLI', 'MDCVLII', 'MDCVLIII', 'MDCIL', 'MDCL', 'MDCLI', 'MDCLII', 'MDCLIII', 'MDCLIV', 'MDCLV', 'MDCLVI', 'MDCLVII', 'MDCLVIII', 'MDCLIX', 'MDCLX', 'MDCLXI', 'MDCLXII', 'MDCLXIII', 'MDCLXIV', 'MDCLXV', 'MDCLXVI', 'MDCLXVII', 'MDCLXVIII', 'MDCLXIX', 'MDCLXX', 'MDCLXXI', 'MDCLXXII', 'MDCLXXIII', 'MDCLXXIV', 'MDCLXXV', 'MDCLXXVI', 'MDCLXXVII', 'MDCLXXVIII', 'MDCLXXIX', 'MDCLXXX', 'MDCLXXXI', 'MDCLXXXII', 'MDCLXXXIII', 'MDCLXXXIV', 'MDCLXXXV', 'MDCLXXXVI', 'MDCLXXXVII', 'MDCLXXXVIII', 'MDCLXXXIX', 'MDCXC', 'MDCXCI', 'MDCXCII', 'MDCXCIII', 'MDCXCIV', 'MDCVC', 'MDCVCI', 'MDCVCII', 'MDCVCIII', 'MDCIC', 'MDCC', 'MDCCI', 'MDCCII', 'MDCCIII', 'MDCCIV', 'MDCCV', 'MDCCVI', 'MDCCVII', 'MDCCVIII', 'MDCCIX', 'MDCCX', 'MDCCXI', 'MDCCXII', 'MDCCXIII', 'MDCCXIV', 'MDCCXV', 'MDCCXVI', 'MDCCXVII', 'MDCCXVIII', 'MDCCXIX', 'MDCCXX', 'MDCCXXI', 'MDCCXXII', 'MDCCXXIII', 'MDCCXXIV', 'MDCCXXV', 'MDCCXXVI', 'MDCCXXVII', 'MDCCXXVIII', 'MDCCXXIX', 'MDCCXXX', 'MDCCXXXI', 'MDCCXXXII', 'MDCCXXXIII', 'MDCCXXXIV', 'MDCCXXXV', 'MDCCXXXVI', 'MDCCXXXVII', 'MDCCXXXVIII', 'MDCCXXXIX', 'MDCCXL', 'MDCCXLI', 'MDCCXLII', 'MDCCXLIII', 'MDCCXLIV', 'MDCCVL', 'MDCCVLI', 'MDCCVLII', 'MDCCVLIII', 'MDCCIL', 'MDCCL', 'MDCCLI', 'MDCCLII', 'MDCCLIII', 'MDCCLIV', 'MDCCLV', 'MDCCLVI', 'MDCCLVII', 'MDCCLVIII', 'MDCCLIX', 'MDCCLX', 'MDCCLXI', 'MDCCLXII', 'MDCCLXIII', 'MDCCLXIV', 'MDCCLXV', 'MDCCLXVI', 'MDCCLXVII', 'MDCCLXVIII', 'MDCCLXIX', 'MDCCLXX', 'MDCCLXXI', 'MDCCLXXII', 'MDCCLXXIII', 'MDCCLXXIV', 'MDCCLXXV', 'MDCCLXXVI', 'MDCCLXXVII', 'MDCCLXXVIII', 'MDCCLXXIX', 'MDCCLXXX', 'MDCCLXXXI', 'MDCCLXXXII', 'MDCCLXXXIII', 'MDCCLXXXIV', 'MDCCLXXXV', 'MDCCLXXXVI', 'MDCCLXXXVII', 'MDCCLXXXVIII', 'MDCCLXXXIX', 'MDCCXC', 'MDCCXCI', 'MDCCXCII', 'MDCCXCIII', 'MDCCXCIV', 'MDCCVC', 'MDCCVCI', 'MDCCVCII', 'MDCCVCIII', 'MDCCIC', 'MDCCC', 'MDCCCI', 'MDCCCII', 'MDCCCIII', 'MDCCCIV', 'MDCCCV', 'MDCCCVI', 'MDCCCVII', 'MDCCCVIII', 'MDCCCIX', 'MDCCCX', 'MDCCCXI', 'MDCCCXII', 'MDCCCXIII', 'MDCCCXIV', 'MDCCCXV', 'MDCCCXVI', 'MDCCCXVII', 'MDCCCXVIII', 'MDCCCXIX', 'MDCCCXX', 'MDCCCXXI', 'MDCCCXXII', 'MDCCCXXIII', 'MDCCCXXIV', 'MDCCCXXV', 'MDCCCXXVI', 'MDCCCXXVII', 'MDCCCXXVIII', 'MDCCCXXIX', 'MDCCCXXX', 'MDCCCXXXI', 'MDCCCXXXII', 'MDCCCXXXIII', 'MDCCCXXXIV', 'MDCCCXXXV', 'MDCCCXXXVI', 'MDCCCXXXVII', 'MDCCCXXXVIII', 'MDCCCXXXIX', 'MDCCCXL', 'MDCCCXLI', 'MDCCCXLII', 'MDCCCXLIII', 'MDCCCXLIV', 'MDCCCVL', 'MDCCCVLI', 'MDCCCVLII', 'MDCCCVLIII', 'MDCCCIL', 'MDCCCL', 'MDCCCLI', 'MDCCCLII', 'MDCCCLIII', 'MDCCCLIV', 'MDCCCLV', 'MDCCCLVI', 'MDCCCLVII', 'MDCCCLVIII', 'MDCCCLIX', 'MDCCCLX', 'MDCCCLXI', 'MDCCCLXII', 'MDCCCLXIII', 'MDCCCLXIV', 'MDCCCLXV', 'MDCCCLXVI', 'MDCCCLXVII', 'MDCCCLXVIII', 'MDCCCLXIX', 'MDCCCLXX', 'MDCCCLXXI', 'MDCCCLXXII', 'MDCCCLXXIII', 'MDCCCLXXIV', 'MDCCCLXXV', 'MDCCCLXXVI', 'MDCCCLXXVII', 'MDCCCLXXVIII', 'MDCCCLXXIX', 'MDCCCLXXX', 'MDCCCLXXXI', 'MDCCCLXXXII', 'MDCCCLXXXIII', 'MDCCCLXXXIV', 'MDCCCLXXXV', 'MDCCCLXXXVI', 'MDCCCLXXXVII', 'MDCCCLXXXVIII', 'MDCCCLXXXIX', 'MDCCCXC', 'MDCCCXCI', 'MDCCCXCII', 'MDCCCXCIII', 'MDCCCXCIV', 'MDCCCVC', 'MDCCCVCI', 'MDCCCVCII', 'MDCCCVCIII', 'MDCCCIC', 'MCM', 'MCMI', 'MCMII', 'MCMIII', 'MCMIV', 'MCMV', 'MCMVI', 'MCMVII', 'MCMVIII', 'MCMIX', 'MCMX', 'MCMXI', 'MCMXII', 'MCMXIII', 'MCMXIV', 'MCMXV', 'MCMXVI', 'MCMXVII', 'MCMXVIII', 'MCMXIX', 'MCMXX', 'MCMXXI', 'MCMXXII', 'MCMXXIII', 'MCMXXIV', 'MCMXXV', 'MCMXXVI', 'MCMXXVII', 'MCMXXVIII', 'MCMXXIX', 'MCMXXX', 'MCMXXXI', 'MCMXXXII', 'MCMXXXIII', 'MCMXXXIV', 'MCMXXXV', 'MCMXXXVI', 'MCMXXXVII', 'MCMXXXVIII', 'MCMXXXIX', 'MCMXL', 'MCMXLI', 'MCMXLII', 'MCMXLIII', 'MCMXLIV', 'MCMVL', 'MCMVLI', 'MCMVLII', 'MCMVLIII', 'MCMIL', 'MLM', 'MLMI', 'MLMII', 'MLMIII', 'MLMIV', 'MLMV', 'MLMVI', 'MLMVII', 'MLMVIII', 'MLMIX', 'MLMX', 'MLMXI', 'MLMXII', 'MLMXIII', 'MLMXIV', 'MLMXV', 'MLMXVI', 'MLMXVII', 'MLMXVIII', 'MLMXIX', 'MLMXX', 'MLMXXI', 'MLMXXII', 'MLMXXIII', 'MLMXXIV', 'MLMXXV', 'MLMXXVI', 'MLMXXVII', 'MLMXXVIII', 'MLMXXIX', 'MLMXXX', 'MLMXXXI', 'MLMXXXII', 'MLMXXXIII', 'MLMXXXIV', 'MLMXXXV', 'MLMXXXVI', 'MLMXXXVII', 'MLMXXXVIII', 'MLMXXXIX', 'MXM', 'MXMI', 'MXMII', 'MXMIII', 'MXMIV', 'MXMV', 'MXMVI', 'MXMVII', 'MXMVIII', 'MXMIX', 'MM', 'MMI', 'MMII', 'MMIII', 'MMIV', 'MMV', 'MMVI', 'MMVII', 'MMVIII', 'MMIX', 'MMX', 'MMXI', 'MMXII', 'MMXIII', 'MMXIV', 'MMXV', 'MMXVI', 'MMXVII', 'MMXVIII', 'MMXIX', 'MMXX', 'MMXXI', 'MMXXII', 'MMXXIII', 'MMXXIV', 'MMXXV', 'MMXXVI', 'MMXXVII', 'MMXXVIII', 'MMXXIX', 'MMXXX', 'MMXXXI', 'MMXXXII', 'MMXXXIII', 'MMXXXIV', 'MMXXXV', 'MMXXXVI', 'MMXXXVII', 'MMXXXVIII', 'MMXXXIX', 'MMXL', 'MMXLI', 'MMXLII', 'MMXLIII', 'MMXLIV', 'MMVL', 'MMVLI', 'MMVLII', 'MMVLIII', 'MMIL', 'MML', 'MMLI', 'MMLII', 'MMLIII', 'MMLIV', 'MMLV', 'MMLVI', 'MMLVII', 'MMLVIII', 'MMLIX', 'MMLX', 'MMLXI', 'MMLXII', 'MMLXIII', 'MMLXIV', 'MMLXV', 'MMLXVI', 'MMLXVII', 'MMLXVIII', 'MMLXIX', 'MMLXX', 'MMLXXI', 'MMLXXII', 'MMLXXIII', 'MMLXXIV', 'MMLXXV', 'MMLXXVI', 'MMLXXVII', 'MMLXXVIII', 'MMLXXIX', 'MMLXXX', 'MMLXXXI', 'MMLXXXII', 'MMLXXXIII', 'MMLXXXIV', 'MMLXXXV', 'MMLXXXVI', 'MMLXXXVII', 'MMLXXXVIII', 'MMLXXXIX', 'MMXC', 'MMXCI', 'MMXCII', 'MMXCIII', 'MMXCIV', 'MMVC', 'MMVCI', 'MMVCII', 'MMVCIII', 'MMIC', 'MMC', 'MMCI', 'MMCII', 'MMCIII', 'MMCIV', 'MMCV', 'MMCVI', 'MMCVII', 'MMCVIII', 'MMCIX', 'MMCX', 'MMCXI', 'MMCXII', 'MMCXIII', 'MMCXIV', 'MMCXV', 'MMCXVI', 'MMCXVII', 'MMCXVIII', 'MMCXIX', 'MMCXX', 'MMCXXI', 'MMCXXII', 'MMCXXIII', 'MMCXXIV', 'MMCXXV', 'MMCXXVI', 'MMCXXVII', 'MMCXXVIII', 'MMCXXIX', 'MMCXXX', 'MMCXXXI', 'MMCXXXII', 'MMCXXXIII', 'MMCXXXIV', 'MMCXXXV', 'MMCXXXVI', 'MMCXXXVII', 'MMCXXXVIII', 'MMCXXXIX', 'MMCXL', 'MMCXLI', 'MMCXLII', 'MMCXLIII', 'MMCXLIV', 'MMCVL', 'MMCVLI', 'MMCVLII', 'MMCVLIII', 'MMCIL', 'MMCL', 'MMCLI', 'MMCLII', 'MMCLIII', 'MMCLIV', 'MMCLV', 'MMCLVI', 'MMCLVII', 'MMCLVIII', 'MMCLIX', 'MMCLX', 'MMCLXI', 'MMCLXII', 'MMCLXIII', 'MMCLXIV', 'MMCLXV', 'MMCLXVI', 'MMCLXVII', 'MMCLXVIII', 'MMCLXIX', 'MMCLXX', 'MMCLXXI', 'MMCLXXII', 'MMCLXXIII', 'MMCLXXIV', 'MMCLXXV', 'MMCLXXVI', 'MMCLXXVII', 'MMCLXXVIII', 'MMCLXXIX', 'MMCLXXX', 'MMCLXXXI', 'MMCLXXXII', 'MMCLXXXIII', 'MMCLXXXIV', 'MMCLXXXV', 'MMCLXXXVI', 'MMCLXXXVII', 'MMCLXXXVIII', 'MMCLXXXIX', 'MMCXC', 'MMCXCI', 'MMCXCII', 'MMCXCIII', 'MMCXCIV', 'MMCVC', 'MMCVCI', 'MMCVCII', 'MMCVCIII', 'MMCIC', 'MMCC', 'MMCCI', 'MMCCII', 'MMCCIII', 'MMCCIV', 'MMCCV', 'MMCCVI', 'MMCCVII', 'MMCCVIII', 'MMCCIX', 'MMCCX', 'MMCCXI', 'MMCCXII', 'MMCCXIII', 'MMCCXIV', 'MMCCXV', 'MMCCXVI', 'MMCCXVII', 'MMCCXVIII', 'MMCCXIX', 'MMCCXX', 'MMCCXXI', 'MMCCXXII', 'MMCCXXIII', 'MMCCXXIV', 'MMCCXXV', 'MMCCXXVI', 'MMCCXXVII', 'MMCCXXVIII', 'MMCCXXIX', 'MMCCXXX', 'MMCCXXXI', 'MMCCXXXII', 'MMCCXXXIII', 'MMCCXXXIV', 'MMCCXXXV', 'MMCCXXXVI', 'MMCCXXXVII', 'MMCCXXXVIII', 'MMCCXXXIX', 'MMCCXL', 'MMCCXLI', 'MMCCXLII', 'MMCCXLIII', 'MMCCXLIV', 'MMCCVL', 'MMCCVLI', 'MMCCVLII', 'MMCCVLIII', 'MMCCIL', 'MMCCL', 'MMCCLI', 'MMCCLII', 'MMCCLIII', 'MMCCLIV', 'MMCCLV', 'MMCCLVI', 'MMCCLVII', 'MMCCLVIII', 'MMCCLIX', 'MMCCLX', 'MMCCLXI', 'MMCCLXII', 'MMCCLXIII', 'MMCCLXIV', 'MMCCLXV', 'MMCCLXVI', 'MMCCLXVII', 'MMCCLXVIII', 'MMCCLXIX', 'MMCCLXX', 'MMCCLXXI', 'MMCCLXXII', 'MMCCLXXIII', 'MMCCLXXIV', 'MMCCLXXV', 'MMCCLXXVI', 'MMCCLXXVII', 'MMCCLXXVIII', 'MMCCLXXIX', 'MMCCLXXX', 'MMCCLXXXI', 'MMCCLXXXII', 'MMCCLXXXIII', 'MMCCLXXXIV', 'MMCCLXXXV', 'MMCCLXXXVI', 'MMCCLXXXVII', 'MMCCLXXXVIII', 'MMCCLXXXIX', 'MMCCXC', 'MMCCXCI', 'MMCCXCII', 'MMCCXCIII', 'MMCCXCIV', 'MMCCVC', 'MMCCVCI', 'MMCCVCII', 'MMCCVCIII', 'MMCCIC', 'MMCCC', 'MMCCCI', 'MMCCCII', 'MMCCCIII', 'MMCCCIV', 'MMCCCV', 'MMCCCVI', 'MMCCCVII', 'MMCCCVIII', 'MMCCCIX', 'MMCCCX', 'MMCCCXI', 'MMCCCXII', 'MMCCCXIII', 'MMCCCXIV', 'MMCCCXV', 'MMCCCXVI', 'MMCCCXVII', 'MMCCCXVIII', 'MMCCCXIX', 'MMCCCXX', 'MMCCCXXI', 'MMCCCXXII', 'MMCCCXXIII', 'MMCCCXXIV', 'MMCCCXXV', 'MMCCCXXVI', 'MMCCCXXVII', 'MMCCCXXVIII', 'MMCCCXXIX', 'MMCCCXXX', 'MMCCCXXXI', 'MMCCCXXXII', 'MMCCCXXXIII', 'MMCCCXXXIV', 'MMCCCXXXV', 'MMCCCXXXVI', 'MMCCCXXXVII', 'MMCCCXXXVIII', 'MMCCCXXXIX', 'MMCCCXL', 'MMCCCXLI', 'MMCCCXLII', 'MMCCCXLIII', 'MMCCCXLIV', 'MMCCCVL', 'MMCCCVLI', 'MMCCCVLII', 'MMCCCVLIII', 'MMCCCIL', 'MMCCCL', 'MMCCCLI', 'MMCCCLII', 'MMCCCLIII', 'MMCCCLIV', 'MMCCCLV', 'MMCCCLVI', 'MMCCCLVII', 'MMCCCLVIII', 'MMCCCLIX', 'MMCCCLX', 'MMCCCLXI', 'MMCCCLXII', 'MMCCCLXIII', 'MMCCCLXIV', 'MMCCCLXV', 'MMCCCLXVI', 'MMCCCLXVII', 'MMCCCLXVIII', 'MMCCCLXIX', 'MMCCCLXX', 'MMCCCLXXI', 'MMCCCLXXII', 'MMCCCLXXIII', 'MMCCCLXXIV', 'MMCCCLXXV', 'MMCCCLXXVI', 'MMCCCLXXVII', 'MMCCCLXXVIII', 'MMCCCLXXIX', 'MMCCCLXXX', 'MMCCCLXXXI', 'MMCCCLXXXII', 'MMCCCLXXXIII', 'MMCCCLXXXIV', 'MMCCCLXXXV', 'MMCCCLXXXVI', 'MMCCCLXXXVII', 'MMCCCLXXXVIII', 'MMCCCLXXXIX', 'MMCCCXC', 'MMCCCXCI', 'MMCCCXCII', 'MMCCCXCIII', 'MMCCCXCIV', 'MMCCCVC', 'MMCCCVCI', 'MMCCCVCII', 'MMCCCVCIII', 'MMCCCIC', 'MMCD', 'MMCDI', 'MMCDII', 'MMCDIII', 'MMCDIV', 'MMCDV', 'MMCDVI', 'MMCDVII', 'MMCDVIII', 'MMCDIX', 'MMCDX', 'MMCDXI', 'MMCDXII', 'MMCDXIII', 'MMCDXIV', 'MMCDXV', 'MMCDXVI', 'MMCDXVII', 'MMCDXVIII', 'MMCDXIX', 'MMCDXX', 'MMCDXXI', 'MMCDXXII', 'MMCDXXIII', 'MMCDXXIV', 'MMCDXXV', 'MMCDXXVI', 'MMCDXXVII', 'MMCDXXVIII', 'MMCDXXIX', 'MMCDXXX', 'MMCDXXXI', 'MMCDXXXII', 'MMCDXXXIII', 'MMCDXXXIV', 'MMCDXXXV', 'MMCDXXXVI', 'MMCDXXXVII', 'MMCDXXXVIII', 'MMCDXXXIX', 'MMCDXL', 'MMCDXLI', 'MMCDXLII', 'MMCDXLIII', 'MMCDXLIV', 'MMCDVL', 'MMCDVLI', 'MMCDVLII', 'MMCDVLIII', 'MMCDIL', 'MMLD', 'MMLDI', 'MMLDII', 'MMLDIII', 'MMLDIV', 'MMLDV', 'MMLDVI', 'MMLDVII', 'MMLDVIII', 'MMLDIX', 'MMLDX', 'MMLDXI', 'MMLDXII', 'MMLDXIII', 'MMLDXIV', 'MMLDXV', 'MMLDXVI', 'MMLDXVII', 'MMLDXVIII', 'MMLDXIX', 'MMLDXX', 'MMLDXXI', 'MMLDXXII', 'MMLDXXIII', 'MMLDXXIV', 'MMLDXXV', 'MMLDXXVI', 'MMLDXXVII', 'MMLDXXVIII', 'MMLDXXIX', 'MMLDXXX', 'MMLDXXXI', 'MMLDXXXII', 'MMLDXXXIII', 'MMLDXXXIV', 'MMLDXXXV', 'MMLDXXXVI', 'MMLDXXXVII', 'MMLDXXXVIII', 'MMLDXXXIX', 'MMXD', 'MMXDI', 'MMXDII', 'MMXDIII', 'MMXDIV', 'MMXDV', 'MMXDVI', 'MMXDVII', 'MMXDVIII', 'MMXDIX', 'MMD', 'MMDI', 'MMDII', 'MMDIII', 'MMDIV', 'MMDV', 'MMDVI', 'MMDVII', 'MMDVIII', 'MMDIX', 'MMDX', 'MMDXI', 'MMDXII', 'MMDXIII', 'MMDXIV', 'MMDXV', 'MMDXVI', 'MMDXVII', 'MMDXVIII', 'MMDXIX', 'MMDXX', 'MMDXXI', 'MMDXXII', 'MMDXXIII', 'MMDXXIV', 'MMDXXV', 'MMDXXVI', 'MMDXXVII', 'MMDXXVIII', 'MMDXXIX', 'MMDXXX', 'MMDXXXI', 'MMDXXXII', 'MMDXXXIII', 'MMDXXXIV', 'MMDXXXV', 'MMDXXXVI', 'MMDXXXVII', 'MMDXXXVIII', 'MMDXXXIX', 'MMDXL', 'MMDXLI', 'MMDXLII', 'MMDXLIII', 'MMDXLIV', 'MMDVL', 'MMDVLI', 'MMDVLII', 'MMDVLIII', 'MMDIL', 'MMDL', 'MMDLI', 'MMDLII', 'MMDLIII', 'MMDLIV', 'MMDLV', 'MMDLVI', 'MMDLVII', 'MMDLVIII', 'MMDLIX', 'MMDLX', 'MMDLXI', 'MMDLXII', 'MMDLXIII', 'MMDLXIV', 'MMDLXV', 'MMDLXVI', 'MMDLXVII', 'MMDLXVIII', 'MMDLXIX', 'MMDLXX', 'MMDLXXI', 'MMDLXXII', 'MMDLXXIII', 'MMDLXXIV', 'MMDLXXV', 'MMDLXXVI', 'MMDLXXVII', 'MMDLXXVIII', 'MMDLXXIX', 'MMDLXXX', 'MMDLXXXI', 'MMDLXXXII', 'MMDLXXXIII', 'MMDLXXXIV', 'MMDLXXXV', 'MMDLXXXVI', 'MMDLXXXVII', 'MMDLXXXVIII', 'MMDLXXXIX', 'MMDXC', 'MMDXCI', 'MMDXCII', 'MMDXCIII', 'MMDXCIV', 'MMDVC', 'MMDVCI', 'MMDVCII', 'MMDVCIII', 'MMDIC', 'MMDC', 'MMDCI', 'MMDCII', 'MMDCIII', 'MMDCIV', 'MMDCV', 'MMDCVI', 'MMDCVII', 'MMDCVIII', 'MMDCIX', 'MMDCX', 'MMDCXI', 'MMDCXII', 'MMDCXIII', 'MMDCXIV', 'MMDCXV', 'MMDCXVI', 'MMDCXVII', 'MMDCXVIII', 'MMDCXIX', 'MMDCXX', 'MMDCXXI', 'MMDCXXII', 'MMDCXXIII', 'MMDCXXIV', 'MMDCXXV', 'MMDCXXVI', 'MMDCXXVII', 'MMDCXXVIII', 'MMDCXXIX', 'MMDCXXX', 'MMDCXXXI', 'MMDCXXXII', 'MMDCXXXIII', 'MMDCXXXIV', 'MMDCXXXV', 'MMDCXXXVI', 'MMDCXXXVII', 'MMDCXXXVIII', 'MMDCXXXIX', 'MMDCXL', 'MMDCXLI', 'MMDCXLII', 'MMDCXLIII', 'MMDCXLIV', 'MMDCVL', 'MMDCVLI', 'MMDCVLII', 'MMDCVLIII', 'MMDCIL', 'MMDCL', 'MMDCLI', 'MMDCLII', 'MMDCLIII', 'MMDCLIV', 'MMDCLV', 'MMDCLVI', 'MMDCLVII', 'MMDCLVIII', 'MMDCLIX', 'MMDCLX', 'MMDCLXI', 'MMDCLXII', 'MMDCLXIII', 'MMDCLXIV', 'MMDCLXV', 'MMDCLXVI', 'MMDCLXVII', 'MMDCLXVIII', 'MMDCLXIX', 'MMDCLXX', 'MMDCLXXI', 'MMDCLXXII', 'MMDCLXXIII', 'MMDCLXXIV', 'MMDCLXXV', 'MMDCLXXVI', 'MMDCLXXVII', 'MMDCLXXVIII', 'MMDCLXXIX', 'MMDCLXXX', 'MMDCLXXXI', 'MMDCLXXXII', 'MMDCLXXXIII', 'MMDCLXXXIV', 'MMDCLXXXV', 'MMDCLXXXVI', 'MMDCLXXXVII', 'MMDCLXXXVIII', 'MMDCLXXXIX', 'MMDCXC', 'MMDCXCI', 'MMDCXCII', 'MMDCXCIII', 'MMDCXCIV', 'MMDCVC', 'MMDCVCI', 'MMDCVCII', 'MMDCVCIII', 'MMDCIC', 'MMDCC', 'MMDCCI', 'MMDCCII', 'MMDCCIII', 'MMDCCIV', 'MMDCCV', 'MMDCCVI', 'MMDCCVII', 'MMDCCVIII', 'MMDCCIX', 'MMDCCX', 'MMDCCXI', 'MMDCCXII', 'MMDCCXIII', 'MMDCCXIV', 'MMDCCXV', 'MMDCCXVI', 'MMDCCXVII', 'MMDCCXVIII', 'MMDCCXIX', 'MMDCCXX', 'MMDCCXXI', 'MMDCCXXII', 'MMDCCXXIII', 'MMDCCXXIV', 'MMDCCXXV', 'MMDCCXXVI', 'MMDCCXXVII', 'MMDCCXXVIII', 'MMDCCXXIX', 'MMDCCXXX', 'MMDCCXXXI', 'MMDCCXXXII', 'MMDCCXXXIII', 'MMDCCXXXIV', 'MMDCCXXXV', 'MMDCCXXXVI', 'MMDCCXXXVII', 'MMDCCXXXVIII', 'MMDCCXXXIX', 'MMDCCXL', 'MMDCCXLI', 'MMDCCXLII', 'MMDCCXLIII', 'MMDCCXLIV', 'MMDCCVL', 'MMDCCVLI', 'MMDCCVLII', 'MMDCCVLIII', 'MMDCCIL', 'MMDCCL', 'MMDCCLI', 'MMDCCLII', 'MMDCCLIII', 'MMDCCLIV', 'MMDCCLV', 'MMDCCLVI', 'MMDCCLVII', 'MMDCCLVIII', 'MMDCCLIX', 'MMDCCLX', 'MMDCCLXI', 'MMDCCLXII', 'MMDCCLXIII', 'MMDCCLXIV', 'MMDCCLXV', 'MMDCCLXVI', 'MMDCCLXVII', 'MMDCCLXVIII', 'MMDCCLXIX', 'MMDCCLXX', 'MMDCCLXXI', 'MMDCCLXXII', 'MMDCCLXXIII', 'MMDCCLXXIV', 'MMDCCLXXV', 'MMDCCLXXVI', 'MMDCCLXXVII', 'MMDCCLXXVIII', 'MMDCCLXXIX', 'MMDCCLXXX', 'MMDCCLXXXI', 'MMDCCLXXXII', 'MMDCCLXXXIII', 'MMDCCLXXXIV', 'MMDCCLXXXV', 'MMDCCLXXXVI', 'MMDCCLXXXVII', 'MMDCCLXXXVIII', 'MMDCCLXXXIX', 'MMDCCXC', 'MMDCCXCI', 'MMDCCXCII', 'MMDCCXCIII', 'MMDCCXCIV', 'MMDCCVC', 'MMDCCVCI', 'MMDCCVCII', 'MMDCCVCIII', 'MMDCCIC', 'MMDCCC', 'MMDCCCI', 'MMDCCCII', 'MMDCCCIII', 'MMDCCCIV', 'MMDCCCV', 'MMDCCCVI', 'MMDCCCVII', 'MMDCCCVIII', 'MMDCCCIX', 'MMDCCCX', 'MMDCCCXI', 'MMDCCCXII', 'MMDCCCXIII', 'MMDCCCXIV', 'MMDCCCXV', 'MMDCCCXVI', 'MMDCCCXVII', 'MMDCCCXVIII', 'MMDCCCXIX', 'MMDCCCXX', 'MMDCCCXXI', 'MMDCCCXXII', 'MMDCCCXXIII', 'MMDCCCXXIV', 'MMDCCCXXV', 'MMDCCCXXVI', 'MMDCCCXXVII', 'MMDCCCXXVIII', 'MMDCCCXXIX', 'MMDCCCXXX', 'MMDCCCXXXI', 'MMDCCCXXXII', 'MMDCCCXXXIII', 'MMDCCCXXXIV', 'MMDCCCXXXV', 'MMDCCCXXXVI', 'MMDCCCXXXVII', 'MMDCCCXXXVIII', 'MMDCCCXXXIX', 'MMDCCCXL', 'MMDCCCXLI', 'MMDCCCXLII', 'MMDCCCXLIII', 'MMDCCCXLIV', 'MMDCCCVL', 'MMDCCCVLI', 'MMDCCCVLII', 'MMDCCCVLIII', 'MMDCCCIL', 'MMDCCCL', 'MMDCCCLI', 'MMDCCCLII', 'MMDCCCLIII', 'MMDCCCLIV', 'MMDCCCLV', 'MMDCCCLVI', 'MMDCCCLVII', 'MMDCCCLVIII', 'MMDCCCLIX', 'MMDCCCLX', 'MMDCCCLXI', 'MMDCCCLXII', 'MMDCCCLXIII', 'MMDCCCLXIV', 'MMDCCCLXV', 'MMDCCCLXVI', 'MMDCCCLXVII', 'MMDCCCLXVIII', 'MMDCCCLXIX', 'MMDCCCLXX', 'MMDCCCLXXI', 'MMDCCCLXXII', 'MMDCCCLXXIII', 'MMDCCCLXXIV', 'MMDCCCLXXV', 'MMDCCCLXXVI', 'MMDCCCLXXVII', 'MMDCCCLXXVIII', 'MMDCCCLXXIX', 'MMDCCCLXXX', 'MMDCCCLXXXI', 'MMDCCCLXXXII', 'MMDCCCLXXXIII', 'MMDCCCLXXXIV', 'MMDCCCLXXXV', 'MMDCCCLXXXVI', 'MMDCCCLXXXVII', 'MMDCCCLXXXVIII', 'MMDCCCLXXXIX', 'MMDCCCXC', 'MMDCCCXCI', 'MMDCCCXCII', 'MMDCCCXCIII', 'MMDCCCXCIV', 'MMDCCCVC', 'MMDCCCVCI', 'MMDCCCVCII', 'MMDCCCVCIII', 'MMDCCCIC', 'MMCM', 'MMCMI', 'MMCMII', 'MMCMIII', 'MMCMIV', 'MMCMV', 'MMCMVI', 'MMCMVII', 'MMCMVIII', 'MMCMIX', 'MMCMX', 'MMCMXI', 'MMCMXII', 'MMCMXIII', 'MMCMXIV', 'MMCMXV', 'MMCMXVI', 'MMCMXVII', 'MMCMXVIII', 'MMCMXIX', 'MMCMXX', 'MMCMXXI', 'MMCMXXII', 'MMCMXXIII', 'MMCMXXIV', 'MMCMXXV', 'MMCMXXVI', 'MMCMXXVII', 'MMCMXXVIII', 'MMCMXXIX', 'MMCMXXX', 'MMCMXXXI', 'MMCMXXXII', 'MMCMXXXIII', 'MMCMXXXIV', 'MMCMXXXV', 'MMCMXXXVI', 'MMCMXXXVII', 'MMCMXXXVIII', 'MMCMXXXIX', 'MMCMXL', 'MMCMXLI', 'MMCMXLII', 'MMCMXLIII', 'MMCMXLIV', 'MMCMVL', 'MMCMVLI', 'MMCMVLII', 'MMCMVLIII', 'MMCMIL', 'MMLM', 'MMLMI', 'MMLMII', 'MMLMIII', 'MMLMIV', 'MMLMV', 'MMLMVI', 'MMLMVII', 'MMLMVIII', 'MMLMIX', 'MMLMX', 'MMLMXI', 'MMLMXII', 'MMLMXIII', 'MMLMXIV', 'MMLMXV', 'MMLMXVI', 'MMLMXVII', 'MMLMXVIII', 'MMLMXIX', 'MMLMXX', 'MMLMXXI', 'MMLMXXII', 'MMLMXXIII', 'MMLMXXIV', 'MMLMXXV', 'MMLMXXVI', 'MMLMXXVII', 'MMLMXXVIII', 'MMLMXXIX', 'MMLMXXX', 'MMLMXXXI', 'MMLMXXXII', 'MMLMXXXIII', 'MMLMXXXIV', 'MMLMXXXV', 'MMLMXXXVI', 'MMLMXXXVII', 'MMLMXXXVIII', 'MMLMXXXIX', 'MMXM', 'MMXMI', 'MMXMII', 'MMXMIII', 'MMXMIV', 'MMXMV', 'MMXMVI', 'MMXMVII', 'MMXMVIII', 'MMXMIX', 'MMM', 'MMMI', 'MMMII', 'MMMIII', 'MMMIV', 'MMMV', 'MMMVI', 'MMMVII', 'MMMVIII', 'MMMIX', 'MMMX', 'MMMXI', 'MMMXII', 'MMMXIII', 'MMMXIV', 'MMMXV', 'MMMXVI', 'MMMXVII', 'MMMXVIII', 'MMMXIX', 'MMMXX', 'MMMXXI', 'MMMXXII', 'MMMXXIII', 'MMMXXIV', 'MMMXXV', 'MMMXXVI', 'MMMXXVII', 'MMMXXVIII', 'MMMXXIX', 'MMMXXX', 'MMMXXXI', 'MMMXXXII', 'MMMXXXIII', 'MMMXXXIV', 'MMMXXXV', 'MMMXXXVI', 'MMMXXXVII', 'MMMXXXVIII', 'MMMXXXIX', 'MMMXL', 'MMMXLI', 'MMMXLII', 'MMMXLIII', 'MMMXLIV', 'MMMVL', 'MMMVLI', 'MMMVLII', 'MMMVLIII', 'MMMIL', 'MMML', 'MMMLI', 'MMMLII', 'MMMLIII', 'MMMLIV', 'MMMLV', 'MMMLVI', 'MMMLVII', 'MMMLVIII', 'MMMLIX', 'MMMLX', 'MMMLXI', 'MMMLXII', 'MMMLXIII', 'MMMLXIV', 'MMMLXV', 'MMMLXVI', 'MMMLXVII', 'MMMLXVIII', 'MMMLXIX', 'MMMLXX', 'MMMLXXI', 'MMMLXXII', 'MMMLXXIII', 'MMMLXXIV', 'MMMLXXV', 'MMMLXXVI', 'MMMLXXVII', 'MMMLXXVIII', 'MMMLXXIX', 'MMMLXXX', 'MMMLXXXI', 'MMMLXXXII', 'MMMLXXXIII', 'MMMLXXXIV', 'MMMLXXXV', 'MMMLXXXVI', 'MMMLXXXVII', 'MMMLXXXVIII', 'MMMLXXXIX', 'MMMXC', 'MMMXCI', 'MMMXCII', 'MMMXCIII', 'MMMXCIV', 'MMMVC', 'MMMVCI', 'MMMVCII', 'MMMVCIII', 'MMMIC', 'MMMC', 'MMMCI', 'MMMCII', 'MMMCIII', 'MMMCIV', 'MMMCV', 'MMMCVI', 'MMMCVII', 'MMMCVIII', 'MMMCIX', 'MMMCX', 'MMMCXI', 'MMMCXII', 'MMMCXIII', 'MMMCXIV', 'MMMCXV', 'MMMCXVI', 'MMMCXVII', 'MMMCXVIII', 'MMMCXIX', 'MMMCXX', 'MMMCXXI', 'MMMCXXII', 'MMMCXXIII', 'MMMCXXIV', 'MMMCXXV', 'MMMCXXVI', 'MMMCXXVII', 'MMMCXXVIII', 'MMMCXXIX', 'MMMCXXX', 'MMMCXXXI', 'MMMCXXXII', 'MMMCXXXIII', 'MMMCXXXIV', 'MMMCXXXV', 'MMMCXXXVI', 'MMMCXXXVII', 'MMMCXXXVIII', 'MMMCXXXIX', 'MMMCXL', 'MMMCXLI', 'MMMCXLII', 'MMMCXLIII', 'MMMCXLIV', 'MMMCVL', 'MMMCVLI', 'MMMCVLII', 'MMMCVLIII', 'MMMCIL', 'MMMCL', 'MMMCLI', 'MMMCLII', 'MMMCLIII', 'MMMCLIV', 'MMMCLV', 'MMMCLVI', 'MMMCLVII', 'MMMCLVIII', 'MMMCLIX', 'MMMCLX', 'MMMCLXI', 'MMMCLXII', 'MMMCLXIII', 'MMMCLXIV', 'MMMCLXV', 'MMMCLXVI', 'MMMCLXVII', 'MMMCLXVIII', 'MMMCLXIX', 'MMMCLXX', 'MMMCLXXI', 'MMMCLXXII', 'MMMCLXXIII', 'MMMCLXXIV', 'MMMCLXXV', 'MMMCLXXVI', 'MMMCLXXVII', 'MMMCLXXVIII', 'MMMCLXXIX', 'MMMCLXXX', 'MMMCLXXXI', 'MMMCLXXXII', 'MMMCLXXXIII', 'MMMCLXXXIV', 'MMMCLXXXV', 'MMMCLXXXVI', 'MMMCLXXXVII', 'MMMCLXXXVIII', 'MMMCLXXXIX', 'MMMCXC', 'MMMCXCI', 'MMMCXCII', 'MMMCXCIII', 'MMMCXCIV', 'MMMCVC', 'MMMCVCI', 'MMMCVCII', 'MMMCVCIII', 'MMMCIC', 'MMMCC', 'MMMCCI', 'MMMCCII', 'MMMCCIII', 'MMMCCIV', 'MMMCCV', 'MMMCCVI', 'MMMCCVII', 'MMMCCVIII', 'MMMCCIX', 'MMMCCX', 'MMMCCXI', 'MMMCCXII', 'MMMCCXIII', 'MMMCCXIV', 'MMMCCXV', 'MMMCCXVI', 'MMMCCXVII', 'MMMCCXVIII', 'MMMCCXIX', 'MMMCCXX', 'MMMCCXXI', 'MMMCCXXII', 'MMMCCXXIII', 'MMMCCXXIV', 'MMMCCXXV', 'MMMCCXXVI', 'MMMCCXXVII', 'MMMCCXXVIII', 'MMMCCXXIX', 'MMMCCXXX', 'MMMCCXXXI', 'MMMCCXXXII', 'MMMCCXXXIII', 'MMMCCXXXIV', 'MMMCCXXXV', 'MMMCCXXXVI', 'MMMCCXXXVII', 'MMMCCXXXVIII', 'MMMCCXXXIX', 'MMMCCXL', 'MMMCCXLI', 'MMMCCXLII', 'MMMCCXLIII', 'MMMCCXLIV', 'MMMCCVL', 'MMMCCVLI', 'MMMCCVLII', 'MMMCCVLIII', 'MMMCCIL', 'MMMCCL', 'MMMCCLI', 'MMMCCLII', 'MMMCCLIII', 'MMMCCLIV', 'MMMCCLV', 'MMMCCLVI', 'MMMCCLVII', 'MMMCCLVIII', 'MMMCCLIX', 'MMMCCLX', 'MMMCCLXI', 'MMMCCLXII', 'MMMCCLXIII', 'MMMCCLXIV', 'MMMCCLXV', 'MMMCCLXVI', 'MMMCCLXVII', 'MMMCCLXVIII', 'MMMCCLXIX', 'MMMCCLXX', 'MMMCCLXXI', 'MMMCCLXXII', 'MMMCCLXXIII', 'MMMCCLXXIV', 'MMMCCLXXV', 'MMMCCLXXVI', 'MMMCCLXXVII', 'MMMCCLXXVIII', 'MMMCCLXXIX', 'MMMCCLXXX', 'MMMCCLXXXI', 'MMMCCLXXXII', 'MMMCCLXXXIII', 'MMMCCLXXXIV', 'MMMCCLXXXV', 'MMMCCLXXXVI', 'MMMCCLXXXVII', 'MMMCCLXXXVIII', 'MMMCCLXXXIX', 'MMMCCXC', 'MMMCCXCI', 'MMMCCXCII', 'MMMCCXCIII', 'MMMCCXCIV', 'MMMCCVC', 'MMMCCVCI', 'MMMCCVCII', 'MMMCCVCIII', 'MMMCCIC', 'MMMCCC', 'MMMCCCI', 'MMMCCCII', 'MMMCCCIII', 'MMMCCCIV', 'MMMCCCV', 'MMMCCCVI', 'MMMCCCVII', 'MMMCCCVIII', 'MMMCCCIX', 'MMMCCCX', 'MMMCCCXI', 'MMMCCCXII', 'MMMCCCXIII', 'MMMCCCXIV', 'MMMCCCXV', 'MMMCCCXVI', 'MMMCCCXVII', 'MMMCCCXVIII', 'MMMCCCXIX', 'MMMCCCXX', 'MMMCCCXXI', 'MMMCCCXXII', 'MMMCCCXXIII', 'MMMCCCXXIV', 'MMMCCCXXV', 'MMMCCCXXVI', 'MMMCCCXXVII', 'MMMCCCXXVIII', 'MMMCCCXXIX', 'MMMCCCXXX', 'MMMCCCXXXI', 'MMMCCCXXXII', 'MMMCCCXXXIII', 'MMMCCCXXXIV', 'MMMCCCXXXV', 'MMMCCCXXXVI', 'MMMCCCXXXVII', 'MMMCCCXXXVIII', 'MMMCCCXXXIX', 'MMMCCCXL', 'MMMCCCXLI', 'MMMCCCXLII', 'MMMCCCXLIII', 'MMMCCCXLIV', 'MMMCCCVL', 'MMMCCCVLI', 'MMMCCCVLII', 'MMMCCCVLIII', 'MMMCCCIL', 'MMMCCCL', 'MMMCCCLI', 'MMMCCCLII', 'MMMCCCLIII', 'MMMCCCLIV', 'MMMCCCLV', 'MMMCCCLVI', 'MMMCCCLVII', 'MMMCCCLVIII', 'MMMCCCLIX', 'MMMCCCLX', 'MMMCCCLXI', 'MMMCCCLXII', 'MMMCCCLXIII', 'MMMCCCLXIV', 'MMMCCCLXV', 'MMMCCCLXVI', 'MMMCCCLXVII', 'MMMCCCLXVIII', 'MMMCCCLXIX', 'MMMCCCLXX', 'MMMCCCLXXI', 'MMMCCCLXXII', 'MMMCCCLXXIII', 'MMMCCCLXXIV', 'MMMCCCLXXV', 'MMMCCCLXXVI', 'MMMCCCLXXVII', 'MMMCCCLXXVIII', 'MMMCCCLXXIX', 'MMMCCCLXXX', 'MMMCCCLXXXI', 'MMMCCCLXXXII', 'MMMCCCLXXXIII', 'MMMCCCLXXXIV', 'MMMCCCLXXXV', 'MMMCCCLXXXVI', 'MMMCCCLXXXVII', 'MMMCCCLXXXVIII', 'MMMCCCLXXXIX', 'MMMCCCXC', 'MMMCCCXCI', 'MMMCCCXCII', 'MMMCCCXCIII', 'MMMCCCXCIV', 'MMMCCCVC', 'MMMCCCVCI', 'MMMCCCVCII', 'MMMCCCVCIII', 'MMMCCCIC', 'MMMCD', 'MMMCDI', 'MMMCDII', 'MMMCDIII', 'MMMCDIV', 'MMMCDV', 'MMMCDVI', 'MMMCDVII', 'MMMCDVIII', 'MMMCDIX', 'MMMCDX', 'MMMCDXI', 'MMMCDXII', 'MMMCDXIII', 'MMMCDXIV', 'MMMCDXV', 'MMMCDXVI', 'MMMCDXVII', 'MMMCDXVIII', 'MMMCDXIX', 'MMMCDXX', 'MMMCDXXI', 'MMMCDXXII', 'MMMCDXXIII', 'MMMCDXXIV', 'MMMCDXXV', 'MMMCDXXVI', 'MMMCDXXVII', 'MMMCDXXVIII', 'MMMCDXXIX', 'MMMCDXXX', 'MMMCDXXXI', 'MMMCDXXXII', 'MMMCDXXXIII', 'MMMCDXXXIV', 'MMMCDXXXV', 'MMMCDXXXVI', 'MMMCDXXXVII', 'MMMCDXXXVIII', 'MMMCDXXXIX', 'MMMCDXL', 'MMMCDXLI', 'MMMCDXLII', 'MMMCDXLIII', 'MMMCDXLIV', 'MMMCDVL', 'MMMCDVLI', 'MMMCDVLII', 'MMMCDVLIII', 'MMMCDIL', 'MMMLD', 'MMMLDI', 'MMMLDII', 'MMMLDIII', 'MMMLDIV', 'MMMLDV', 'MMMLDVI', 'MMMLDVII', 'MMMLDVIII', 'MMMLDIX', 'MMMLDX', 'MMMLDXI', 'MMMLDXII', 'MMMLDXIII', 'MMMLDXIV', 'MMMLDXV', 'MMMLDXVI', 'MMMLDXVII', 'MMMLDXVIII', 'MMMLDXIX', 'MMMLDXX', 'MMMLDXXI', 'MMMLDXXII', 'MMMLDXXIII', 'MMMLDXXIV', 'MMMLDXXV', 'MMMLDXXVI', 'MMMLDXXVII', 'MMMLDXXVIII', 'MMMLDXXIX', 'MMMLDXXX', 'MMMLDXXXI', 'MMMLDXXXII', 'MMMLDXXXIII', 'MMMLDXXXIV', 'MMMLDXXXV', 'MMMLDXXXVI', 'MMMLDXXXVII', 'MMMLDXXXVIII', 'MMMLDXXXIX', 'MMMXD', 'MMMXDI', 'MMMXDII', 'MMMXDIII', 'MMMXDIV', 'MMMXDV', 'MMMXDVI', 'MMMXDVII', 'MMMXDVIII', 'MMMXDIX', 'MMMD', 'MMMDI', 'MMMDII', 'MMMDIII', 'MMMDIV', 'MMMDV', 'MMMDVI', 'MMMDVII', 'MMMDVIII', 'MMMDIX', 'MMMDX', 'MMMDXI', 'MMMDXII', 'MMMDXIII', 'MMMDXIV', 'MMMDXV', 'MMMDXVI', 'MMMDXVII', 'MMMDXVIII', 'MMMDXIX', 'MMMDXX', 'MMMDXXI', 'MMMDXXII', 'MMMDXXIII', 'MMMDXXIV', 'MMMDXXV', 'MMMDXXVI', 'MMMDXXVII', 'MMMDXXVIII', 'MMMDXXIX', 'MMMDXXX', 'MMMDXXXI', 'MMMDXXXII', 'MMMDXXXIII', 'MMMDXXXIV', 'MMMDXXXV', 'MMMDXXXVI', 'MMMDXXXVII', 'MMMDXXXVIII', 'MMMDXXXIX', 'MMMDXL', 'MMMDXLI', 'MMMDXLII', 'MMMDXLIII', 'MMMDXLIV', 'MMMDVL', 'MMMDVLI', 'MMMDVLII', 'MMMDVLIII', 'MMMDIL', 'MMMDL', 'MMMDLI', 'MMMDLII', 'MMMDLIII', 'MMMDLIV', 'MMMDLV', 'MMMDLVI', 'MMMDLVII', 'MMMDLVIII', 'MMMDLIX', 'MMMDLX', 'MMMDLXI', 'MMMDLXII', 'MMMDLXIII', 'MMMDLXIV', 'MMMDLXV', 'MMMDLXVI', 'MMMDLXVII', 'MMMDLXVIII', 'MMMDLXIX', 'MMMDLXX', 'MMMDLXXI', 'MMMDLXXII', 'MMMDLXXIII', 'MMMDLXXIV', 'MMMDLXXV', 'MMMDLXXVI', 'MMMDLXXVII', 'MMMDLXXVIII', 'MMMDLXXIX', 'MMMDLXXX', 'MMMDLXXXI', 'MMMDLXXXII', 'MMMDLXXXIII', 'MMMDLXXXIV', 'MMMDLXXXV', 'MMMDLXXXVI', 'MMMDLXXXVII', 'MMMDLXXXVIII', 'MMMDLXXXIX', 'MMMDXC', 'MMMDXCI', 'MMMDXCII', 'MMMDXCIII', 'MMMDXCIV', 'MMMDVC', 'MMMDVCI', 'MMMDVCII', 'MMMDVCIII', 'MMMDIC', 'MMMDC', 'MMMDCI', 'MMMDCII', 'MMMDCIII', 'MMMDCIV', 'MMMDCV', 'MMMDCVI', 'MMMDCVII', 'MMMDCVIII', 'MMMDCIX', 'MMMDCX', 'MMMDCXI', 'MMMDCXII', 'MMMDCXIII', 'MMMDCXIV', 'MMMDCXV', 'MMMDCXVI', 'MMMDCXVII', 'MMMDCXVIII', 'MMMDCXIX', 'MMMDCXX', 'MMMDCXXI', 'MMMDCXXII', 'MMMDCXXIII', 'MMMDCXXIV', 'MMMDCXXV', 'MMMDCXXVI', 'MMMDCXXVII', 'MMMDCXXVIII', 'MMMDCXXIX', 'MMMDCXXX', 'MMMDCXXXI', 'MMMDCXXXII', 'MMMDCXXXIII', 'MMMDCXXXIV', 'MMMDCXXXV', 'MMMDCXXXVI', 'MMMDCXXXVII', 'MMMDCXXXVIII', 'MMMDCXXXIX', 'MMMDCXL', 'MMMDCXLI', 'MMMDCXLII', 'MMMDCXLIII', 'MMMDCXLIV', 'MMMDCVL', 'MMMDCVLI', 'MMMDCVLII', 'MMMDCVLIII', 'MMMDCIL', 'MMMDCL', 'MMMDCLI', 'MMMDCLII', 'MMMDCLIII', 'MMMDCLIV', 'MMMDCLV', 'MMMDCLVI', 'MMMDCLVII', 'MMMDCLVIII', 'MMMDCLIX', 'MMMDCLX', 'MMMDCLXI', 'MMMDCLXII', 'MMMDCLXIII', 'MMMDCLXIV', 'MMMDCLXV', 'MMMDCLXVI', 'MMMDCLXVII', 'MMMDCLXVIII', 'MMMDCLXIX', 'MMMDCLXX', 'MMMDCLXXI', 'MMMDCLXXII', 'MMMDCLXXIII', 'MMMDCLXXIV', 'MMMDCLXXV', 'MMMDCLXXVI', 'MMMDCLXXVII', 'MMMDCLXXVIII', 'MMMDCLXXIX', 'MMMDCLXXX', 'MMMDCLXXXI', 'MMMDCLXXXII', 'MMMDCLXXXIII', 'MMMDCLXXXIV', 'MMMDCLXXXV', 'MMMDCLXXXVI', 'MMMDCLXXXVII', 'MMMDCLXXXVIII', 'MMMDCLXXXIX', 'MMMDCXC', 'MMMDCXCI', 'MMMDCXCII', 'MMMDCXCIII', 'MMMDCXCIV', 'MMMDCVC', 'MMMDCVCI', 'MMMDCVCII', 'MMMDCVCIII', 'MMMDCIC', 'MMMDCC', 'MMMDCCI', 'MMMDCCII', 'MMMDCCIII', 'MMMDCCIV', 'MMMDCCV', 'MMMDCCVI', 'MMMDCCVII', 'MMMDCCVIII', 'MMMDCCIX', 'MMMDCCX', 'MMMDCCXI', 'MMMDCCXII', 'MMMDCCXIII', 'MMMDCCXIV', 'MMMDCCXV', 'MMMDCCXVI', 'MMMDCCXVII', 'MMMDCCXVIII', 'MMMDCCXIX', 'MMMDCCXX', 'MMMDCCXXI', 'MMMDCCXXII', 'MMMDCCXXIII', 'MMMDCCXXIV', 'MMMDCCXXV', 'MMMDCCXXVI', 'MMMDCCXXVII', 'MMMDCCXXVIII', 'MMMDCCXXIX', 'MMMDCCXXX', 'MMMDCCXXXI', 'MMMDCCXXXII', 'MMMDCCXXXIII', 'MMMDCCXXXIV', 'MMMDCCXXXV', 'MMMDCCXXXVI', 'MMMDCCXXXVII', 'MMMDCCXXXVIII', 'MMMDCCXXXIX', 'MMMDCCXL', 'MMMDCCXLI', 'MMMDCCXLII', 'MMMDCCXLIII', 'MMMDCCXLIV', 'MMMDCCVL', 'MMMDCCVLI', 'MMMDCCVLII', 'MMMDCCVLIII', 'MMMDCCIL', 'MMMDCCL', 'MMMDCCLI', 'MMMDCCLII', 'MMMDCCLIII', 'MMMDCCLIV', 'MMMDCCLV', 'MMMDCCLVI', 'MMMDCCLVII', 'MMMDCCLVIII', 'MMMDCCLIX', 'MMMDCCLX', 'MMMDCCLXI', 'MMMDCCLXII', 'MMMDCCLXIII', 'MMMDCCLXIV', 'MMMDCCLXV', 'MMMDCCLXVI', 'MMMDCCLXVII', 'MMMDCCLXVIII', 'MMMDCCLXIX', 'MMMDCCLXX', 'MMMDCCLXXI', 'MMMDCCLXXII', 'MMMDCCLXXIII', 'MMMDCCLXXIV', 'MMMDCCLXXV', 'MMMDCCLXXVI', 'MMMDCCLXXVII', 'MMMDCCLXXVIII', 'MMMDCCLXXIX', 'MMMDCCLXXX', 'MMMDCCLXXXI', 'MMMDCCLXXXII', 'MMMDCCLXXXIII', 'MMMDCCLXXXIV', 'MMMDCCLXXXV', 'MMMDCCLXXXVI', 'MMMDCCLXXXVII', 'MMMDCCLXXXVIII', 'MMMDCCLXXXIX', 'MMMDCCXC', 'MMMDCCXCI', 'MMMDCCXCII', 'MMMDCCXCIII', 'MMMDCCXCIV', 'MMMDCCVC', 'MMMDCCVCI', 'MMMDCCVCII', 'MMMDCCVCIII', 'MMMDCCIC', 'MMMDCCC', 'MMMDCCCI', 'MMMDCCCII', 'MMMDCCCIII', 'MMMDCCCIV', 'MMMDCCCV', 'MMMDCCCVI', 'MMMDCCCVII', 'MMMDCCCVIII', 'MMMDCCCIX', 'MMMDCCCX', 'MMMDCCCXI', 'MMMDCCCXII', 'MMMDCCCXIII', 'MMMDCCCXIV', 'MMMDCCCXV', 'MMMDCCCXVI', 'MMMDCCCXVII', 'MMMDCCCXVIII', 'MMMDCCCXIX', 'MMMDCCCXX', 'MMMDCCCXXI', 'MMMDCCCXXII', 'MMMDCCCXXIII', 'MMMDCCCXXIV', 'MMMDCCCXXV', 'MMMDCCCXXVI', 'MMMDCCCXXVII', 'MMMDCCCXXVIII', 'MMMDCCCXXIX', 'MMMDCCCXXX', 'MMMDCCCXXXI', 'MMMDCCCXXXII', 'MMMDCCCXXXIII', 'MMMDCCCXXXIV', 'MMMDCCCXXXV', 'MMMDCCCXXXVI', 'MMMDCCCXXXVII', 'MMMDCCCXXXVIII', 'MMMDCCCXXXIX', 'MMMDCCCXL', 'MMMDCCCXLI', 'MMMDCCCXLII', 'MMMDCCCXLIII', 'MMMDCCCXLIV', 'MMMDCCCVL', 'MMMDCCCVLI', 'MMMDCCCVLII', 'MMMDCCCVLIII', 'MMMDCCCIL', 'MMMDCCCL', 'MMMDCCCLI', 'MMMDCCCLII', 'MMMDCCCLIII', 'MMMDCCCLIV', 'MMMDCCCLV', 'MMMDCCCLVI', 'MMMDCCCLVII', 'MMMDCCCLVIII', 'MMMDCCCLIX', 'MMMDCCCLX', 'MMMDCCCLXI', 'MMMDCCCLXII', 'MMMDCCCLXIII', 'MMMDCCCLXIV', 'MMMDCCCLXV', 'MMMDCCCLXVI', 'MMMDCCCLXVII', 'MMMDCCCLXVIII', 'MMMDCCCLXIX', 'MMMDCCCLXX', 'MMMDCCCLXXI', 'MMMDCCCLXXII', 'MMMDCCCLXXIII', 'MMMDCCCLXXIV', 'MMMDCCCLXXV', 'MMMDCCCLXXVI', 'MMMDCCCLXXVII', 'MMMDCCCLXXVIII', 'MMMDCCCLXXIX', 'MMMDCCCLXXX', 'MMMDCCCLXXXI', 'MMMDCCCLXXXII', 'MMMDCCCLXXXIII', 'MMMDCCCLXXXIV', 'MMMDCCCLXXXV', 'MMMDCCCLXXXVI', 'MMMDCCCLXXXVII', 'MMMDCCCLXXXVIII', 'MMMDCCCLXXXIX', 'MMMDCCCXC', 'MMMDCCCXCI', 'MMMDCCCXCII', 'MMMDCCCXCIII', 'MMMDCCCXCIV', 'MMMDCCCVC', 'MMMDCCCVCI', 'MMMDCCCVCII', 'MMMDCCCVCIII', 'MMMDCCCIC', 'MMMCM', 'MMMCMI', 'MMMCMII', 'MMMCMIII', 'MMMCMIV', 'MMMCMV', 'MMMCMVI', 'MMMCMVII', 'MMMCMVIII', 'MMMCMIX', 'MMMCMX', 'MMMCMXI', 'MMMCMXII', 'MMMCMXIII', 'MMMCMXIV', 'MMMCMXV', 'MMMCMXVI', 'MMMCMXVII', 'MMMCMXVIII', 'MMMCMXIX', 'MMMCMXX', 'MMMCMXXI', 'MMMCMXXII', 'MMMCMXXIII', 'MMMCMXXIV', 'MMMCMXXV', 'MMMCMXXVI', 'MMMCMXXVII', 'MMMCMXXVIII', 'MMMCMXXIX', 'MMMCMXXX', 'MMMCMXXXI', 'MMMCMXXXII', 'MMMCMXXXIII', 'MMMCMXXXIV', 'MMMCMXXXV', 'MMMCMXXXVI', 'MMMCMXXXVII', 'MMMCMXXXVIII', 'MMMCMXXXIX', 'MMMCMXL', 'MMMCMXLI', 'MMMCMXLII', 'MMMCMXLIII', 'MMMCMXLIV', 'MMMCMVL', 'MMMCMVLI', 'MMMCMVLII', 'MMMCMVLIII', 'MMMCMIL', 'MMMLM', 'MMMLMI', 'MMMLMII', 'MMMLMIII', 'MMMLMIV', 'MMMLMV', 'MMMLMVI', 'MMMLMVII', 'MMMLMVIII', 'MMMLMIX', 'MMMLMX', 'MMMLMXI', 'MMMLMXII', 'MMMLMXIII', 'MMMLMXIV', 'MMMLMXV', 'MMMLMXVI', 'MMMLMXVII', 'MMMLMXVIII', 'MMMLMXIX', 'MMMLMXX', 'MMMLMXXI', 'MMMLMXXII', 'MMMLMXXIII', 'MMMLMXXIV', 'MMMLMXXV', 'MMMLMXXVI', 'MMMLMXXVII', 'MMMLMXXVIII', 'MMMLMXXIX', 'MMMLMXXX', 'MMMLMXXXI', 'MMMLMXXXII', 'MMMLMXXXIII', 'MMMLMXXXIV', 'MMMLMXXXV', 'MMMLMXXXVI', 'MMMLMXXXVII', 'MMMLMXXXVIII', 'MMMLMXXXIX', 'MMMXM', 'MMMXMI', 'MMMXMII', 'MMMXMIII', 'MMMXMIV', 'MMMXMV', 'MMMXMVI', 'MMMXMVII', 'MMMXMVIII', 'MMMXMIX', ] -const mode3 = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX', 'XXI', 'XXII', 'XXIII', 'XXIV', 'XXV', 'XXVI', 'XXVII', 'XXVIII', 'XXIX', 'XXX', 'XXXI', 'XXXII', 'XXXIII', 'XXXIV', 'XXXV', 'XXXVI', 'XXXVII', 'XXXVIII', 'XXXIX', 'XL', 'XLI', 'XLII', 'XLIII', 'XLIV', 'VL', 'VLI', 'VLII', 'VLIII', 'IL', 'L', 'LI', 'LII', 'LIII', 'LIV', 'LV', 'LVI', 'LVII', 'LVIII', 'LIX', 'LX', 'LXI', 'LXII', 'LXIII', 'LXIV', 'LXV', 'LXVI', 'LXVII', 'LXVIII', 'LXIX', 'LXX', 'LXXI', 'LXXII', 'LXXIII', 'LXXIV', 'LXXV', 'LXXVI', 'LXXVII', 'LXXVIII', 'LXXIX', 'LXXX', 'LXXXI', 'LXXXII', 'LXXXIII', 'LXXXIV', 'LXXXV', 'LXXXVI', 'LXXXVII', 'LXXXVIII', 'LXXXIX', 'XC', 'XCI', 'XCII', 'XCIII', 'XCIV', 'VC', 'VCI', 'VCII', 'VCIII', 'IC', 'C', 'CI', 'CII', 'CIII', 'CIV', 'CV', 'CVI', 'CVII', 'CVIII', 'CIX', 'CX', 'CXI', 'CXII', 'CXIII', 'CXIV', 'CXV', 'CXVI', 'CXVII', 'CXVIII', 'CXIX', 'CXX', 'CXXI', 'CXXII', 'CXXIII', 'CXXIV', 'CXXV', 'CXXVI', 'CXXVII', 'CXXVIII', 'CXXIX', 'CXXX', 'CXXXI', 'CXXXII', 'CXXXIII', 'CXXXIV', 'CXXXV', 'CXXXVI', 'CXXXVII', 'CXXXVIII', 'CXXXIX', 'CXL', 'CXLI', 'CXLII', 'CXLIII', 'CXLIV', 'CVL', 'CVLI', 'CVLII', 'CVLIII', 'CIL', 'CL', 'CLI', 'CLII', 'CLIII', 'CLIV', 'CLV', 'CLVI', 'CLVII', 'CLVIII', 'CLIX', 'CLX', 'CLXI', 'CLXII', 'CLXIII', 'CLXIV', 'CLXV', 'CLXVI', 'CLXVII', 'CLXVIII', 'CLXIX', 'CLXX', 'CLXXI', 'CLXXII', 'CLXXIII', 'CLXXIV', 'CLXXV', 'CLXXVI', 'CLXXVII', 'CLXXVIII', 'CLXXIX', 'CLXXX', 'CLXXXI', 'CLXXXII', 'CLXXXIII', 'CLXXXIV', 'CLXXXV', 'CLXXXVI', 'CLXXXVII', 'CLXXXVIII', 'CLXXXIX', 'CXC', 'CXCI', 'CXCII', 'CXCIII', 'CXCIV', 'CVC', 'CVCI', 'CVCII', 'CVCIII', 'CIC', 'CC', 'CCI', 'CCII', 'CCIII', 'CCIV', 'CCV', 'CCVI', 'CCVII', 'CCVIII', 'CCIX', 'CCX', 'CCXI', 'CCXII', 'CCXIII', 'CCXIV', 'CCXV', 'CCXVI', 'CCXVII', 'CCXVIII', 'CCXIX', 'CCXX', 'CCXXI', 'CCXXII', 'CCXXIII', 'CCXXIV', 'CCXXV', 'CCXXVI', 'CCXXVII', 'CCXXVIII', 'CCXXIX', 'CCXXX', 'CCXXXI', 'CCXXXII', 'CCXXXIII', 'CCXXXIV', 'CCXXXV', 'CCXXXVI', 'CCXXXVII', 'CCXXXVIII', 'CCXXXIX', 'CCXL', 'CCXLI', 'CCXLII', 'CCXLIII', 'CCXLIV', 'CCVL', 'CCVLI', 'CCVLII', 'CCVLIII', 'CCIL', 'CCL', 'CCLI', 'CCLII', 'CCLIII', 'CCLIV', 'CCLV', 'CCLVI', 'CCLVII', 'CCLVIII', 'CCLIX', 'CCLX', 'CCLXI', 'CCLXII', 'CCLXIII', 'CCLXIV', 'CCLXV', 'CCLXVI', 'CCLXVII', 'CCLXVIII', 'CCLXIX', 'CCLXX', 'CCLXXI', 'CCLXXII', 'CCLXXIII', 'CCLXXIV', 'CCLXXV', 'CCLXXVI', 'CCLXXVII', 'CCLXXVIII', 'CCLXXIX', 'CCLXXX', 'CCLXXXI', 'CCLXXXII', 'CCLXXXIII', 'CCLXXXIV', 'CCLXXXV', 'CCLXXXVI', 'CCLXXXVII', 'CCLXXXVIII', 'CCLXXXIX', 'CCXC', 'CCXCI', 'CCXCII', 'CCXCIII', 'CCXCIV', 'CCVC', 'CCVCI', 'CCVCII', 'CCVCIII', 'CCIC', 'CCC', 'CCCI', 'CCCII', 'CCCIII', 'CCCIV', 'CCCV', 'CCCVI', 'CCCVII', 'CCCVIII', 'CCCIX', 'CCCX', 'CCCXI', 'CCCXII', 'CCCXIII', 'CCCXIV', 'CCCXV', 'CCCXVI', 'CCCXVII', 'CCCXVIII', 'CCCXIX', 'CCCXX', 'CCCXXI', 'CCCXXII', 'CCCXXIII', 'CCCXXIV', 'CCCXXV', 'CCCXXVI', 'CCCXXVII', 'CCCXXVIII', 'CCCXXIX', 'CCCXXX', 'CCCXXXI', 'CCCXXXII', 'CCCXXXIII', 'CCCXXXIV', 'CCCXXXV', 'CCCXXXVI', 'CCCXXXVII', 'CCCXXXVIII', 'CCCXXXIX', 'CCCXL', 'CCCXLI', 'CCCXLII', 'CCCXLIII', 'CCCXLIV', 'CCCVL', 'CCCVLI', 'CCCVLII', 'CCCVLIII', 'CCCIL', 'CCCL', 'CCCLI', 'CCCLII', 'CCCLIII', 'CCCLIV', 'CCCLV', 'CCCLVI', 'CCCLVII', 'CCCLVIII', 'CCCLIX', 'CCCLX', 'CCCLXI', 'CCCLXII', 'CCCLXIII', 'CCCLXIV', 'CCCLXV', 'CCCLXVI', 'CCCLXVII', 'CCCLXVIII', 'CCCLXIX', 'CCCLXX', 'CCCLXXI', 'CCCLXXII', 'CCCLXXIII', 'CCCLXXIV', 'CCCLXXV', 'CCCLXXVI', 'CCCLXXVII', 'CCCLXXVIII', 'CCCLXXIX', 'CCCLXXX', 'CCCLXXXI', 'CCCLXXXII', 'CCCLXXXIII', 'CCCLXXXIV', 'CCCLXXXV', 'CCCLXXXVI', 'CCCLXXXVII', 'CCCLXXXVIII', 'CCCLXXXIX', 'CCCXC', 'CCCXCI', 'CCCXCII', 'CCCXCIII', 'CCCXCIV', 'CCCVC', 'CCCVCI', 'CCCVCII', 'CCCVCIII', 'CCCIC', 'CD', 'CDI', 'CDII', 'CDIII', 'CDIV', 'CDV', 'CDVI', 'CDVII', 'CDVIII', 'CDIX', 'CDX', 'CDXI', 'CDXII', 'CDXIII', 'CDXIV', 'CDXV', 'CDXVI', 'CDXVII', 'CDXVIII', 'CDXIX', 'CDXX', 'CDXXI', 'CDXXII', 'CDXXIII', 'CDXXIV', 'CDXXV', 'CDXXVI', 'CDXXVII', 'CDXXVIII', 'CDXXIX', 'CDXXX', 'CDXXXI', 'CDXXXII', 'CDXXXIII', 'CDXXXIV', 'CDXXXV', 'CDXXXVI', 'CDXXXVII', 'CDXXXVIII', 'CDXXXIX', 'CDXL', 'CDXLI', 'CDXLII', 'CDXLIII', 'CDXLIV', 'CDVL', 'CDVLI', 'CDVLII', 'CDVLIII', 'CDIL', 'LD', 'LDI', 'LDII', 'LDIII', 'LDIV', 'LDV', 'LDVI', 'LDVII', 'LDVIII', 'LDIX', 'LDX', 'LDXI', 'LDXII', 'LDXIII', 'LDXIV', 'LDXV', 'LDXVI', 'LDXVII', 'LDXVIII', 'LDXIX', 'LDXX', 'LDXXI', 'LDXXII', 'LDXXIII', 'LDXXIV', 'LDXXV', 'LDXXVI', 'LDXXVII', 'LDXXVIII', 'LDXXIX', 'LDXXX', 'LDXXXI', 'LDXXXII', 'LDXXXIII', 'LDXXXIV', 'LDXXXV', 'LDXXXVI', 'LDXXXVII', 'LDXXXVIII', 'LDXXXIX', 'XD', 'XDI', 'XDII', 'XDIII', 'XDIV', 'VD', 'VDI', 'VDII', 'VDIII', 'VDIV', 'D', 'DI', 'DII', 'DIII', 'DIV', 'DV', 'DVI', 'DVII', 'DVIII', 'DIX', 'DX', 'DXI', 'DXII', 'DXIII', 'DXIV', 'DXV', 'DXVI', 'DXVII', 'DXVIII', 'DXIX', 'DXX', 'DXXI', 'DXXII', 'DXXIII', 'DXXIV', 'DXXV', 'DXXVI', 'DXXVII', 'DXXVIII', 'DXXIX', 'DXXX', 'DXXXI', 'DXXXII', 'DXXXIII', 'DXXXIV', 'DXXXV', 'DXXXVI', 'DXXXVII', 'DXXXVIII', 'DXXXIX', 'DXL', 'DXLI', 'DXLII', 'DXLIII', 'DXLIV', 'DVL', 'DVLI', 'DVLII', 'DVLIII', 'DIL', 'DL', 'DLI', 'DLII', 'DLIII', 'DLIV', 'DLV', 'DLVI', 'DLVII', 'DLVIII', 'DLIX', 'DLX', 'DLXI', 'DLXII', 'DLXIII', 'DLXIV', 'DLXV', 'DLXVI', 'DLXVII', 'DLXVIII', 'DLXIX', 'DLXX', 'DLXXI', 'DLXXII', 'DLXXIII', 'DLXXIV', 'DLXXV', 'DLXXVI', 'DLXXVII', 'DLXXVIII', 'DLXXIX', 'DLXXX', 'DLXXXI', 'DLXXXII', 'DLXXXIII', 'DLXXXIV', 'DLXXXV', 'DLXXXVI', 'DLXXXVII', 'DLXXXVIII', 'DLXXXIX', 'DXC', 'DXCI', 'DXCII', 'DXCIII', 'DXCIV', 'DVC', 'DVCI', 'DVCII', 'DVCIII', 'DIC', 'DC', 'DCI', 'DCII', 'DCIII', 'DCIV', 'DCV', 'DCVI', 'DCVII', 'DCVIII', 'DCIX', 'DCX', 'DCXI', 'DCXII', 'DCXIII', 'DCXIV', 'DCXV', 'DCXVI', 'DCXVII', 'DCXVIII', 'DCXIX', 'DCXX', 'DCXXI', 'DCXXII', 'DCXXIII', 'DCXXIV', 'DCXXV', 'DCXXVI', 'DCXXVII', 'DCXXVIII', 'DCXXIX', 'DCXXX', 'DCXXXI', 'DCXXXII', 'DCXXXIII', 'DCXXXIV', 'DCXXXV', 'DCXXXVI', 'DCXXXVII', 'DCXXXVIII', 'DCXXXIX', 'DCXL', 'DCXLI', 'DCXLII', 'DCXLIII', 'DCXLIV', 'DCVL', 'DCVLI', 'DCVLII', 'DCVLIII', 'DCIL', 'DCL', 'DCLI', 'DCLII', 'DCLIII', 'DCLIV', 'DCLV', 'DCLVI', 'DCLVII', 'DCLVIII', 'DCLIX', 'DCLX', 'DCLXI', 'DCLXII', 'DCLXIII', 'DCLXIV', 'DCLXV', 'DCLXVI', 'DCLXVII', 'DCLXVIII', 'DCLXIX', 'DCLXX', 'DCLXXI', 'DCLXXII', 'DCLXXIII', 'DCLXXIV', 'DCLXXV', 'DCLXXVI', 'DCLXXVII', 'DCLXXVIII', 'DCLXXIX', 'DCLXXX', 'DCLXXXI', 'DCLXXXII', 'DCLXXXIII', 'DCLXXXIV', 'DCLXXXV', 'DCLXXXVI', 'DCLXXXVII', 'DCLXXXVIII', 'DCLXXXIX', 'DCXC', 'DCXCI', 'DCXCII', 'DCXCIII', 'DCXCIV', 'DCVC', 'DCVCI', 'DCVCII', 'DCVCIII', 'DCIC', 'DCC', 'DCCI', 'DCCII', 'DCCIII', 'DCCIV', 'DCCV', 'DCCVI', 'DCCVII', 'DCCVIII', 'DCCIX', 'DCCX', 'DCCXI', 'DCCXII', 'DCCXIII', 'DCCXIV', 'DCCXV', 'DCCXVI', 'DCCXVII', 'DCCXVIII', 'DCCXIX', 'DCCXX', 'DCCXXI', 'DCCXXII', 'DCCXXIII', 'DCCXXIV', 'DCCXXV', 'DCCXXVI', 'DCCXXVII', 'DCCXXVIII', 'DCCXXIX', 'DCCXXX', 'DCCXXXI', 'DCCXXXII', 'DCCXXXIII', 'DCCXXXIV', 'DCCXXXV', 'DCCXXXVI', 'DCCXXXVII', 'DCCXXXVIII', 'DCCXXXIX', 'DCCXL', 'DCCXLI', 'DCCXLII', 'DCCXLIII', 'DCCXLIV', 'DCCVL', 'DCCVLI', 'DCCVLII', 'DCCVLIII', 'DCCIL', 'DCCL', 'DCCLI', 'DCCLII', 'DCCLIII', 'DCCLIV', 'DCCLV', 'DCCLVI', 'DCCLVII', 'DCCLVIII', 'DCCLIX', 'DCCLX', 'DCCLXI', 'DCCLXII', 'DCCLXIII', 'DCCLXIV', 'DCCLXV', 'DCCLXVI', 'DCCLXVII', 'DCCLXVIII', 'DCCLXIX', 'DCCLXX', 'DCCLXXI', 'DCCLXXII', 'DCCLXXIII', 'DCCLXXIV', 'DCCLXXV', 'DCCLXXVI', 'DCCLXXVII', 'DCCLXXVIII', 'DCCLXXIX', 'DCCLXXX', 'DCCLXXXI', 'DCCLXXXII', 'DCCLXXXIII', 'DCCLXXXIV', 'DCCLXXXV', 'DCCLXXXVI', 'DCCLXXXVII', 'DCCLXXXVIII', 'DCCLXXXIX', 'DCCXC', 'DCCXCI', 'DCCXCII', 'DCCXCIII', 'DCCXCIV', 'DCCVC', 'DCCVCI', 'DCCVCII', 'DCCVCIII', 'DCCIC', 'DCCC', 'DCCCI', 'DCCCII', 'DCCCIII', 'DCCCIV', 'DCCCV', 'DCCCVI', 'DCCCVII', 'DCCCVIII', 'DCCCIX', 'DCCCX', 'DCCCXI', 'DCCCXII', 'DCCCXIII', 'DCCCXIV', 'DCCCXV', 'DCCCXVI', 'DCCCXVII', 'DCCCXVIII', 'DCCCXIX', 'DCCCXX', 'DCCCXXI', 'DCCCXXII', 'DCCCXXIII', 'DCCCXXIV', 'DCCCXXV', 'DCCCXXVI', 'DCCCXXVII', 'DCCCXXVIII', 'DCCCXXIX', 'DCCCXXX', 'DCCCXXXI', 'DCCCXXXII', 'DCCCXXXIII', 'DCCCXXXIV', 'DCCCXXXV', 'DCCCXXXVI', 'DCCCXXXVII', 'DCCCXXXVIII', 'DCCCXXXIX', 'DCCCXL', 'DCCCXLI', 'DCCCXLII', 'DCCCXLIII', 'DCCCXLIV', 'DCCCVL', 'DCCCVLI', 'DCCCVLII', 'DCCCVLIII', 'DCCCIL', 'DCCCL', 'DCCCLI', 'DCCCLII', 'DCCCLIII', 'DCCCLIV', 'DCCCLV', 'DCCCLVI', 'DCCCLVII', 'DCCCLVIII', 'DCCCLIX', 'DCCCLX', 'DCCCLXI', 'DCCCLXII', 'DCCCLXIII', 'DCCCLXIV', 'DCCCLXV', 'DCCCLXVI', 'DCCCLXVII', 'DCCCLXVIII', 'DCCCLXIX', 'DCCCLXX', 'DCCCLXXI', 'DCCCLXXII', 'DCCCLXXIII', 'DCCCLXXIV', 'DCCCLXXV', 'DCCCLXXVI', 'DCCCLXXVII', 'DCCCLXXVIII', 'DCCCLXXIX', 'DCCCLXXX', 'DCCCLXXXI', 'DCCCLXXXII', 'DCCCLXXXIII', 'DCCCLXXXIV', 'DCCCLXXXV', 'DCCCLXXXVI', 'DCCCLXXXVII', 'DCCCLXXXVIII', 'DCCCLXXXIX', 'DCCCXC', 'DCCCXCI', 'DCCCXCII', 'DCCCXCIII', 'DCCCXCIV', 'DCCCVC', 'DCCCVCI', 'DCCCVCII', 'DCCCVCIII', 'DCCCIC', 'CM', 'CMI', 'CMII', 'CMIII', 'CMIV', 'CMV', 'CMVI', 'CMVII', 'CMVIII', 'CMIX', 'CMX', 'CMXI', 'CMXII', 'CMXIII', 'CMXIV', 'CMXV', 'CMXVI', 'CMXVII', 'CMXVIII', 'CMXIX', 'CMXX', 'CMXXI', 'CMXXII', 'CMXXIII', 'CMXXIV', 'CMXXV', 'CMXXVI', 'CMXXVII', 'CMXXVIII', 'CMXXIX', 'CMXXX', 'CMXXXI', 'CMXXXII', 'CMXXXIII', 'CMXXXIV', 'CMXXXV', 'CMXXXVI', 'CMXXXVII', 'CMXXXVIII', 'CMXXXIX', 'CMXL', 'CMXLI', 'CMXLII', 'CMXLIII', 'CMXLIV', 'CMVL', 'CMVLI', 'CMVLII', 'CMVLIII', 'CMIL', 'LM', 'LMI', 'LMII', 'LMIII', 'LMIV', 'LMV', 'LMVI', 'LMVII', 'LMVIII', 'LMIX', 'LMX', 'LMXI', 'LMXII', 'LMXIII', 'LMXIV', 'LMXV', 'LMXVI', 'LMXVII', 'LMXVIII', 'LMXIX', 'LMXX', 'LMXXI', 'LMXXII', 'LMXXIII', 'LMXXIV', 'LMXXV', 'LMXXVI', 'LMXXVII', 'LMXXVIII', 'LMXXIX', 'LMXXX', 'LMXXXI', 'LMXXXII', 'LMXXXIII', 'LMXXXIV', 'LMXXXV', 'LMXXXVI', 'LMXXXVII', 'LMXXXVIII', 'LMXXXIX', 'XM', 'XMI', 'XMII', 'XMIII', 'XMIV', 'VM', 'VMI', 'VMII', 'VMIII', 'VMIV', 'M', 'MI', 'MII', 'MIII', 'MIV', 'MV', 'MVI', 'MVII', 'MVIII', 'MIX', 'MX', 'MXI', 'MXII', 'MXIII', 'MXIV', 'MXV', 'MXVI', 'MXVII', 'MXVIII', 'MXIX', 'MXX', 'MXXI', 'MXXII', 'MXXIII', 'MXXIV', 'MXXV', 'MXXVI', 'MXXVII', 'MXXVIII', 'MXXIX', 'MXXX', 'MXXXI', 'MXXXII', 'MXXXIII', 'MXXXIV', 'MXXXV', 'MXXXVI', 'MXXXVII', 'MXXXVIII', 'MXXXIX', 'MXL', 'MXLI', 'MXLII', 'MXLIII', 'MXLIV', 'MVL', 'MVLI', 'MVLII', 'MVLIII', 'MIL', 'ML', 'MLI', 'MLII', 'MLIII', 'MLIV', 'MLV', 'MLVI', 'MLVII', 'MLVIII', 'MLIX', 'MLX', 'MLXI', 'MLXII', 'MLXIII', 'MLXIV', 'MLXV', 'MLXVI', 'MLXVII', 'MLXVIII', 'MLXIX', 'MLXX', 'MLXXI', 'MLXXII', 'MLXXIII', 'MLXXIV', 'MLXXV', 'MLXXVI', 'MLXXVII', 'MLXXVIII', 'MLXXIX', 'MLXXX', 'MLXXXI', 'MLXXXII', 'MLXXXIII', 'MLXXXIV', 'MLXXXV', 'MLXXXVI', 'MLXXXVII', 'MLXXXVIII', 'MLXXXIX', 'MXC', 'MXCI', 'MXCII', 'MXCIII', 'MXCIV', 'MVC', 'MVCI', 'MVCII', 'MVCIII', 'MIC', 'MC', 'MCI', 'MCII', 'MCIII', 'MCIV', 'MCV', 'MCVI', 'MCVII', 'MCVIII', 'MCIX', 'MCX', 'MCXI', 'MCXII', 'MCXIII', 'MCXIV', 'MCXV', 'MCXVI', 'MCXVII', 'MCXVIII', 'MCXIX', 'MCXX', 'MCXXI', 'MCXXII', 'MCXXIII', 'MCXXIV', 'MCXXV', 'MCXXVI', 'MCXXVII', 'MCXXVIII', 'MCXXIX', 'MCXXX', 'MCXXXI', 'MCXXXII', 'MCXXXIII', 'MCXXXIV', 'MCXXXV', 'MCXXXVI', 'MCXXXVII', 'MCXXXVIII', 'MCXXXIX', 'MCXL', 'MCXLI', 'MCXLII', 'MCXLIII', 'MCXLIV', 'MCVL', 'MCVLI', 'MCVLII', 'MCVLIII', 'MCIL', 'MCL', 'MCLI', 'MCLII', 'MCLIII', 'MCLIV', 'MCLV', 'MCLVI', 'MCLVII', 'MCLVIII', 'MCLIX', 'MCLX', 'MCLXI', 'MCLXII', 'MCLXIII', 'MCLXIV', 'MCLXV', 'MCLXVI', 'MCLXVII', 'MCLXVIII', 'MCLXIX', 'MCLXX', 'MCLXXI', 'MCLXXII', 'MCLXXIII', 'MCLXXIV', 'MCLXXV', 'MCLXXVI', 'MCLXXVII', 'MCLXXVIII', 'MCLXXIX', 'MCLXXX', 'MCLXXXI', 'MCLXXXII', 'MCLXXXIII', 'MCLXXXIV', 'MCLXXXV', 'MCLXXXVI', 'MCLXXXVII', 'MCLXXXVIII', 'MCLXXXIX', 'MCXC', 'MCXCI', 'MCXCII', 'MCXCIII', 'MCXCIV', 'MCVC', 'MCVCI', 'MCVCII', 'MCVCIII', 'MCIC', 'MCC', 'MCCI', 'MCCII', 'MCCIII', 'MCCIV', 'MCCV', 'MCCVI', 'MCCVII', 'MCCVIII', 'MCCIX', 'MCCX', 'MCCXI', 'MCCXII', 'MCCXIII', 'MCCXIV', 'MCCXV', 'MCCXVI', 'MCCXVII', 'MCCXVIII', 'MCCXIX', 'MCCXX', 'MCCXXI', 'MCCXXII', 'MCCXXIII', 'MCCXXIV', 'MCCXXV', 'MCCXXVI', 'MCCXXVII', 'MCCXXVIII', 'MCCXXIX', 'MCCXXX', 'MCCXXXI', 'MCCXXXII', 'MCCXXXIII', 'MCCXXXIV', 'MCCXXXV', 'MCCXXXVI', 'MCCXXXVII', 'MCCXXXVIII', 'MCCXXXIX', 'MCCXL', 'MCCXLI', 'MCCXLII', 'MCCXLIII', 'MCCXLIV', 'MCCVL', 'MCCVLI', 'MCCVLII', 'MCCVLIII', 'MCCIL', 'MCCL', 'MCCLI', 'MCCLII', 'MCCLIII', 'MCCLIV', 'MCCLV', 'MCCLVI', 'MCCLVII', 'MCCLVIII', 'MCCLIX', 'MCCLX', 'MCCLXI', 'MCCLXII', 'MCCLXIII', 'MCCLXIV', 'MCCLXV', 'MCCLXVI', 'MCCLXVII', 'MCCLXVIII', 'MCCLXIX', 'MCCLXX', 'MCCLXXI', 'MCCLXXII', 'MCCLXXIII', 'MCCLXXIV', 'MCCLXXV', 'MCCLXXVI', 'MCCLXXVII', 'MCCLXXVIII', 'MCCLXXIX', 'MCCLXXX', 'MCCLXXXI', 'MCCLXXXII', 'MCCLXXXIII', 'MCCLXXXIV', 'MCCLXXXV', 'MCCLXXXVI', 'MCCLXXXVII', 'MCCLXXXVIII', 'MCCLXXXIX', 'MCCXC', 'MCCXCI', 'MCCXCII', 'MCCXCIII', 'MCCXCIV', 'MCCVC', 'MCCVCI', 'MCCVCII', 'MCCVCIII', 'MCCIC', 'MCCC', 'MCCCI', 'MCCCII', 'MCCCIII', 'MCCCIV', 'MCCCV', 'MCCCVI', 'MCCCVII', 'MCCCVIII', 'MCCCIX', 'MCCCX', 'MCCCXI', 'MCCCXII', 'MCCCXIII', 'MCCCXIV', 'MCCCXV', 'MCCCXVI', 'MCCCXVII', 'MCCCXVIII', 'MCCCXIX', 'MCCCXX', 'MCCCXXI', 'MCCCXXII', 'MCCCXXIII', 'MCCCXXIV', 'MCCCXXV', 'MCCCXXVI', 'MCCCXXVII', 'MCCCXXVIII', 'MCCCXXIX', 'MCCCXXX', 'MCCCXXXI', 'MCCCXXXII', 'MCCCXXXIII', 'MCCCXXXIV', 'MCCCXXXV', 'MCCCXXXVI', 'MCCCXXXVII', 'MCCCXXXVIII', 'MCCCXXXIX', 'MCCCXL', 'MCCCXLI', 'MCCCXLII', 'MCCCXLIII', 'MCCCXLIV', 'MCCCVL', 'MCCCVLI', 'MCCCVLII', 'MCCCVLIII', 'MCCCIL', 'MCCCL', 'MCCCLI', 'MCCCLII', 'MCCCLIII', 'MCCCLIV', 'MCCCLV', 'MCCCLVI', 'MCCCLVII', 'MCCCLVIII', 'MCCCLIX', 'MCCCLX', 'MCCCLXI', 'MCCCLXII', 'MCCCLXIII', 'MCCCLXIV', 'MCCCLXV', 'MCCCLXVI', 'MCCCLXVII', 'MCCCLXVIII', 'MCCCLXIX', 'MCCCLXX', 'MCCCLXXI', 'MCCCLXXII', 'MCCCLXXIII', 'MCCCLXXIV', 'MCCCLXXV', 'MCCCLXXVI', 'MCCCLXXVII', 'MCCCLXXVIII', 'MCCCLXXIX', 'MCCCLXXX', 'MCCCLXXXI', 'MCCCLXXXII', 'MCCCLXXXIII', 'MCCCLXXXIV', 'MCCCLXXXV', 'MCCCLXXXVI', 'MCCCLXXXVII', 'MCCCLXXXVIII', 'MCCCLXXXIX', 'MCCCXC', 'MCCCXCI', 'MCCCXCII', 'MCCCXCIII', 'MCCCXCIV', 'MCCCVC', 'MCCCVCI', 'MCCCVCII', 'MCCCVCIII', 'MCCCIC', 'MCD', 'MCDI', 'MCDII', 'MCDIII', 'MCDIV', 'MCDV', 'MCDVI', 'MCDVII', 'MCDVIII', 'MCDIX', 'MCDX', 'MCDXI', 'MCDXII', 'MCDXIII', 'MCDXIV', 'MCDXV', 'MCDXVI', 'MCDXVII', 'MCDXVIII', 'MCDXIX', 'MCDXX', 'MCDXXI', 'MCDXXII', 'MCDXXIII', 'MCDXXIV', 'MCDXXV', 'MCDXXVI', 'MCDXXVII', 'MCDXXVIII', 'MCDXXIX', 'MCDXXX', 'MCDXXXI', 'MCDXXXII', 'MCDXXXIII', 'MCDXXXIV', 'MCDXXXV', 'MCDXXXVI', 'MCDXXXVII', 'MCDXXXVIII', 'MCDXXXIX', 'MCDXL', 'MCDXLI', 'MCDXLII', 'MCDXLIII', 'MCDXLIV', 'MCDVL', 'MCDVLI', 'MCDVLII', 'MCDVLIII', 'MCDIL', 'MLD', 'MLDI', 'MLDII', 'MLDIII', 'MLDIV', 'MLDV', 'MLDVI', 'MLDVII', 'MLDVIII', 'MLDIX', 'MLDX', 'MLDXI', 'MLDXII', 'MLDXIII', 'MLDXIV', 'MLDXV', 'MLDXVI', 'MLDXVII', 'MLDXVIII', 'MLDXIX', 'MLDXX', 'MLDXXI', 'MLDXXII', 'MLDXXIII', 'MLDXXIV', 'MLDXXV', 'MLDXXVI', 'MLDXXVII', 'MLDXXVIII', 'MLDXXIX', 'MLDXXX', 'MLDXXXI', 'MLDXXXII', 'MLDXXXIII', 'MLDXXXIV', 'MLDXXXV', 'MLDXXXVI', 'MLDXXXVII', 'MLDXXXVIII', 'MLDXXXIX', 'MXD', 'MXDI', 'MXDII', 'MXDIII', 'MXDIV', 'MVD', 'MVDI', 'MVDII', 'MVDIII', 'MVDIV', 'MD', 'MDI', 'MDII', 'MDIII', 'MDIV', 'MDV', 'MDVI', 'MDVII', 'MDVIII', 'MDIX', 'MDX', 'MDXI', 'MDXII', 'MDXIII', 'MDXIV', 'MDXV', 'MDXVI', 'MDXVII', 'MDXVIII', 'MDXIX', 'MDXX', 'MDXXI', 'MDXXII', 'MDXXIII', 'MDXXIV', 'MDXXV', 'MDXXVI', 'MDXXVII', 'MDXXVIII', 'MDXXIX', 'MDXXX', 'MDXXXI', 'MDXXXII', 'MDXXXIII', 'MDXXXIV', 'MDXXXV', 'MDXXXVI', 'MDXXXVII', 'MDXXXVIII', 'MDXXXIX', 'MDXL', 'MDXLI', 'MDXLII', 'MDXLIII', 'MDXLIV', 'MDVL', 'MDVLI', 'MDVLII', 'MDVLIII', 'MDIL', 'MDL', 'MDLI', 'MDLII', 'MDLIII', 'MDLIV', 'MDLV', 'MDLVI', 'MDLVII', 'MDLVIII', 'MDLIX', 'MDLX', 'MDLXI', 'MDLXII', 'MDLXIII', 'MDLXIV', 'MDLXV', 'MDLXVI', 'MDLXVII', 'MDLXVIII', 'MDLXIX', 'MDLXX', 'MDLXXI', 'MDLXXII', 'MDLXXIII', 'MDLXXIV', 'MDLXXV', 'MDLXXVI', 'MDLXXVII', 'MDLXXVIII', 'MDLXXIX', 'MDLXXX', 'MDLXXXI', 'MDLXXXII', 'MDLXXXIII', 'MDLXXXIV', 'MDLXXXV', 'MDLXXXVI', 'MDLXXXVII', 'MDLXXXVIII', 'MDLXXXIX', 'MDXC', 'MDXCI', 'MDXCII', 'MDXCIII', 'MDXCIV', 'MDVC', 'MDVCI', 'MDVCII', 'MDVCIII', 'MDIC', 'MDC', 'MDCI', 'MDCII', 'MDCIII', 'MDCIV', 'MDCV', 'MDCVI', 'MDCVII', 'MDCVIII', 'MDCIX', 'MDCX', 'MDCXI', 'MDCXII', 'MDCXIII', 'MDCXIV', 'MDCXV', 'MDCXVI', 'MDCXVII', 'MDCXVIII', 'MDCXIX', 'MDCXX', 'MDCXXI', 'MDCXXII', 'MDCXXIII', 'MDCXXIV', 'MDCXXV', 'MDCXXVI', 'MDCXXVII', 'MDCXXVIII', 'MDCXXIX', 'MDCXXX', 'MDCXXXI', 'MDCXXXII', 'MDCXXXIII', 'MDCXXXIV', 'MDCXXXV', 'MDCXXXVI', 'MDCXXXVII', 'MDCXXXVIII', 'MDCXXXIX', 'MDCXL', 'MDCXLI', 'MDCXLII', 'MDCXLIII', 'MDCXLIV', 'MDCVL', 'MDCVLI', 'MDCVLII', 'MDCVLIII', 'MDCIL', 'MDCL', 'MDCLI', 'MDCLII', 'MDCLIII', 'MDCLIV', 'MDCLV', 'MDCLVI', 'MDCLVII', 'MDCLVIII', 'MDCLIX', 'MDCLX', 'MDCLXI', 'MDCLXII', 'MDCLXIII', 'MDCLXIV', 'MDCLXV', 'MDCLXVI', 'MDCLXVII', 'MDCLXVIII', 'MDCLXIX', 'MDCLXX', 'MDCLXXI', 'MDCLXXII', 'MDCLXXIII', 'MDCLXXIV', 'MDCLXXV', 'MDCLXXVI', 'MDCLXXVII', 'MDCLXXVIII', 'MDCLXXIX', 'MDCLXXX', 'MDCLXXXI', 'MDCLXXXII', 'MDCLXXXIII', 'MDCLXXXIV', 'MDCLXXXV', 'MDCLXXXVI', 'MDCLXXXVII', 'MDCLXXXVIII', 'MDCLXXXIX', 'MDCXC', 'MDCXCI', 'MDCXCII', 'MDCXCIII', 'MDCXCIV', 'MDCVC', 'MDCVCI', 'MDCVCII', 'MDCVCIII', 'MDCIC', 'MDCC', 'MDCCI', 'MDCCII', 'MDCCIII', 'MDCCIV', 'MDCCV', 'MDCCVI', 'MDCCVII', 'MDCCVIII', 'MDCCIX', 'MDCCX', 'MDCCXI', 'MDCCXII', 'MDCCXIII', 'MDCCXIV', 'MDCCXV', 'MDCCXVI', 'MDCCXVII', 'MDCCXVIII', 'MDCCXIX', 'MDCCXX', 'MDCCXXI', 'MDCCXXII', 'MDCCXXIII', 'MDCCXXIV', 'MDCCXXV', 'MDCCXXVI', 'MDCCXXVII', 'MDCCXXVIII', 'MDCCXXIX', 'MDCCXXX', 'MDCCXXXI', 'MDCCXXXII', 'MDCCXXXIII', 'MDCCXXXIV', 'MDCCXXXV', 'MDCCXXXVI', 'MDCCXXXVII', 'MDCCXXXVIII', 'MDCCXXXIX', 'MDCCXL', 'MDCCXLI', 'MDCCXLII', 'MDCCXLIII', 'MDCCXLIV', 'MDCCVL', 'MDCCVLI', 'MDCCVLII', 'MDCCVLIII', 'MDCCIL', 'MDCCL', 'MDCCLI', 'MDCCLII', 'MDCCLIII', 'MDCCLIV', 'MDCCLV', 'MDCCLVI', 'MDCCLVII', 'MDCCLVIII', 'MDCCLIX', 'MDCCLX', 'MDCCLXI', 'MDCCLXII', 'MDCCLXIII', 'MDCCLXIV', 'MDCCLXV', 'MDCCLXVI', 'MDCCLXVII', 'MDCCLXVIII', 'MDCCLXIX', 'MDCCLXX', 'MDCCLXXI', 'MDCCLXXII', 'MDCCLXXIII', 'MDCCLXXIV', 'MDCCLXXV', 'MDCCLXXVI', 'MDCCLXXVII', 'MDCCLXXVIII', 'MDCCLXXIX', 'MDCCLXXX', 'MDCCLXXXI', 'MDCCLXXXII', 'MDCCLXXXIII', 'MDCCLXXXIV', 'MDCCLXXXV', 'MDCCLXXXVI', 'MDCCLXXXVII', 'MDCCLXXXVIII', 'MDCCLXXXIX', 'MDCCXC', 'MDCCXCI', 'MDCCXCII', 'MDCCXCIII', 'MDCCXCIV', 'MDCCVC', 'MDCCVCI', 'MDCCVCII', 'MDCCVCIII', 'MDCCIC', 'MDCCC', 'MDCCCI', 'MDCCCII', 'MDCCCIII', 'MDCCCIV', 'MDCCCV', 'MDCCCVI', 'MDCCCVII', 'MDCCCVIII', 'MDCCCIX', 'MDCCCX', 'MDCCCXI', 'MDCCCXII', 'MDCCCXIII', 'MDCCCXIV', 'MDCCCXV', 'MDCCCXVI', 'MDCCCXVII', 'MDCCCXVIII', 'MDCCCXIX', 'MDCCCXX', 'MDCCCXXI', 'MDCCCXXII', 'MDCCCXXIII', 'MDCCCXXIV', 'MDCCCXXV', 'MDCCCXXVI', 'MDCCCXXVII', 'MDCCCXXVIII', 'MDCCCXXIX', 'MDCCCXXX', 'MDCCCXXXI', 'MDCCCXXXII', 'MDCCCXXXIII', 'MDCCCXXXIV', 'MDCCCXXXV', 'MDCCCXXXVI', 'MDCCCXXXVII', 'MDCCCXXXVIII', 'MDCCCXXXIX', 'MDCCCXL', 'MDCCCXLI', 'MDCCCXLII', 'MDCCCXLIII', 'MDCCCXLIV', 'MDCCCVL', 'MDCCCVLI', 'MDCCCVLII', 'MDCCCVLIII', 'MDCCCIL', 'MDCCCL', 'MDCCCLI', 'MDCCCLII', 'MDCCCLIII', 'MDCCCLIV', 'MDCCCLV', 'MDCCCLVI', 'MDCCCLVII', 'MDCCCLVIII', 'MDCCCLIX', 'MDCCCLX', 'MDCCCLXI', 'MDCCCLXII', 'MDCCCLXIII', 'MDCCCLXIV', 'MDCCCLXV', 'MDCCCLXVI', 'MDCCCLXVII', 'MDCCCLXVIII', 'MDCCCLXIX', 'MDCCCLXX', 'MDCCCLXXI', 'MDCCCLXXII', 'MDCCCLXXIII', 'MDCCCLXXIV', 'MDCCCLXXV', 'MDCCCLXXVI', 'MDCCCLXXVII', 'MDCCCLXXVIII', 'MDCCCLXXIX', 'MDCCCLXXX', 'MDCCCLXXXI', 'MDCCCLXXXII', 'MDCCCLXXXIII', 'MDCCCLXXXIV', 'MDCCCLXXXV', 'MDCCCLXXXVI', 'MDCCCLXXXVII', 'MDCCCLXXXVIII', 'MDCCCLXXXIX', 'MDCCCXC', 'MDCCCXCI', 'MDCCCXCII', 'MDCCCXCIII', 'MDCCCXCIV', 'MDCCCVC', 'MDCCCVCI', 'MDCCCVCII', 'MDCCCVCIII', 'MDCCCIC', 'MCM', 'MCMI', 'MCMII', 'MCMIII', 'MCMIV', 'MCMV', 'MCMVI', 'MCMVII', 'MCMVIII', 'MCMIX', 'MCMX', 'MCMXI', 'MCMXII', 'MCMXIII', 'MCMXIV', 'MCMXV', 'MCMXVI', 'MCMXVII', 'MCMXVIII', 'MCMXIX', 'MCMXX', 'MCMXXI', 'MCMXXII', 'MCMXXIII', 'MCMXXIV', 'MCMXXV', 'MCMXXVI', 'MCMXXVII', 'MCMXXVIII', 'MCMXXIX', 'MCMXXX', 'MCMXXXI', 'MCMXXXII', 'MCMXXXIII', 'MCMXXXIV', 'MCMXXXV', 'MCMXXXVI', 'MCMXXXVII', 'MCMXXXVIII', 'MCMXXXIX', 'MCMXL', 'MCMXLI', 'MCMXLII', 'MCMXLIII', 'MCMXLIV', 'MCMVL', 'MCMVLI', 'MCMVLII', 'MCMVLIII', 'MCMIL', 'MLM', 'MLMI', 'MLMII', 'MLMIII', 'MLMIV', 'MLMV', 'MLMVI', 'MLMVII', 'MLMVIII', 'MLMIX', 'MLMX', 'MLMXI', 'MLMXII', 'MLMXIII', 'MLMXIV', 'MLMXV', 'MLMXVI', 'MLMXVII', 'MLMXVIII', 'MLMXIX', 'MLMXX', 'MLMXXI', 'MLMXXII', 'MLMXXIII', 'MLMXXIV', 'MLMXXV', 'MLMXXVI', 'MLMXXVII', 'MLMXXVIII', 'MLMXXIX', 'MLMXXX', 'MLMXXXI', 'MLMXXXII', 'MLMXXXIII', 'MLMXXXIV', 'MLMXXXV', 'MLMXXXVI', 'MLMXXXVII', 'MLMXXXVIII', 'MLMXXXIX', 'MXM', 'MXMI', 'MXMII', 'MXMIII', 'MXMIV', 'MVM', 'MVMI', 'MVMII', 'MVMIII', 'MVMIV', 'MM', 'MMI', 'MMII', 'MMIII', 'MMIV', 'MMV', 'MMVI', 'MMVII', 'MMVIII', 'MMIX', 'MMX', 'MMXI', 'MMXII', 'MMXIII', 'MMXIV', 'MMXV', 'MMXVI', 'MMXVII', 'MMXVIII', 'MMXIX', 'MMXX', 'MMXXI', 'MMXXII', 'MMXXIII', 'MMXXIV', 'MMXXV', 'MMXXVI', 'MMXXVII', 'MMXXVIII', 'MMXXIX', 'MMXXX', 'MMXXXI', 'MMXXXII', 'MMXXXIII', 'MMXXXIV', 'MMXXXV', 'MMXXXVI', 'MMXXXVII', 'MMXXXVIII', 'MMXXXIX', 'MMXL', 'MMXLI', 'MMXLII', 'MMXLIII', 'MMXLIV', 'MMVL', 'MMVLI', 'MMVLII', 'MMVLIII', 'MMIL', 'MML', 'MMLI', 'MMLII', 'MMLIII', 'MMLIV', 'MMLV', 'MMLVI', 'MMLVII', 'MMLVIII', 'MMLIX', 'MMLX', 'MMLXI', 'MMLXII', 'MMLXIII', 'MMLXIV', 'MMLXV', 'MMLXVI', 'MMLXVII', 'MMLXVIII', 'MMLXIX', 'MMLXX', 'MMLXXI', 'MMLXXII', 'MMLXXIII', 'MMLXXIV', 'MMLXXV', 'MMLXXVI', 'MMLXXVII', 'MMLXXVIII', 'MMLXXIX', 'MMLXXX', 'MMLXXXI', 'MMLXXXII', 'MMLXXXIII', 'MMLXXXIV', 'MMLXXXV', 'MMLXXXVI', 'MMLXXXVII', 'MMLXXXVIII', 'MMLXXXIX', 'MMXC', 'MMXCI', 'MMXCII', 'MMXCIII', 'MMXCIV', 'MMVC', 'MMVCI', 'MMVCII', 'MMVCIII', 'MMIC', 'MMC', 'MMCI', 'MMCII', 'MMCIII', 'MMCIV', 'MMCV', 'MMCVI', 'MMCVII', 'MMCVIII', 'MMCIX', 'MMCX', 'MMCXI', 'MMCXII', 'MMCXIII', 'MMCXIV', 'MMCXV', 'MMCXVI', 'MMCXVII', 'MMCXVIII', 'MMCXIX', 'MMCXX', 'MMCXXI', 'MMCXXII', 'MMCXXIII', 'MMCXXIV', 'MMCXXV', 'MMCXXVI', 'MMCXXVII', 'MMCXXVIII', 'MMCXXIX', 'MMCXXX', 'MMCXXXI', 'MMCXXXII', 'MMCXXXIII', 'MMCXXXIV', 'MMCXXXV', 'MMCXXXVI', 'MMCXXXVII', 'MMCXXXVIII', 'MMCXXXIX', 'MMCXL', 'MMCXLI', 'MMCXLII', 'MMCXLIII', 'MMCXLIV', 'MMCVL', 'MMCVLI', 'MMCVLII', 'MMCVLIII', 'MMCIL', 'MMCL', 'MMCLI', 'MMCLII', 'MMCLIII', 'MMCLIV', 'MMCLV', 'MMCLVI', 'MMCLVII', 'MMCLVIII', 'MMCLIX', 'MMCLX', 'MMCLXI', 'MMCLXII', 'MMCLXIII', 'MMCLXIV', 'MMCLXV', 'MMCLXVI', 'MMCLXVII', 'MMCLXVIII', 'MMCLXIX', 'MMCLXX', 'MMCLXXI', 'MMCLXXII', 'MMCLXXIII', 'MMCLXXIV', 'MMCLXXV', 'MMCLXXVI', 'MMCLXXVII', 'MMCLXXVIII', 'MMCLXXIX', 'MMCLXXX', 'MMCLXXXI', 'MMCLXXXII', 'MMCLXXXIII', 'MMCLXXXIV', 'MMCLXXXV', 'MMCLXXXVI', 'MMCLXXXVII', 'MMCLXXXVIII', 'MMCLXXXIX', 'MMCXC', 'MMCXCI', 'MMCXCII', 'MMCXCIII', 'MMCXCIV', 'MMCVC', 'MMCVCI', 'MMCVCII', 'MMCVCIII', 'MMCIC', 'MMCC', 'MMCCI', 'MMCCII', 'MMCCIII', 'MMCCIV', 'MMCCV', 'MMCCVI', 'MMCCVII', 'MMCCVIII', 'MMCCIX', 'MMCCX', 'MMCCXI', 'MMCCXII', 'MMCCXIII', 'MMCCXIV', 'MMCCXV', 'MMCCXVI', 'MMCCXVII', 'MMCCXVIII', 'MMCCXIX', 'MMCCXX', 'MMCCXXI', 'MMCCXXII', 'MMCCXXIII', 'MMCCXXIV', 'MMCCXXV', 'MMCCXXVI', 'MMCCXXVII', 'MMCCXXVIII', 'MMCCXXIX', 'MMCCXXX', 'MMCCXXXI', 'MMCCXXXII', 'MMCCXXXIII', 'MMCCXXXIV', 'MMCCXXXV', 'MMCCXXXVI', 'MMCCXXXVII', 'MMCCXXXVIII', 'MMCCXXXIX', 'MMCCXL', 'MMCCXLI', 'MMCCXLII', 'MMCCXLIII', 'MMCCXLIV', 'MMCCVL', 'MMCCVLI', 'MMCCVLII', 'MMCCVLIII', 'MMCCIL', 'MMCCL', 'MMCCLI', 'MMCCLII', 'MMCCLIII', 'MMCCLIV', 'MMCCLV', 'MMCCLVI', 'MMCCLVII', 'MMCCLVIII', 'MMCCLIX', 'MMCCLX', 'MMCCLXI', 'MMCCLXII', 'MMCCLXIII', 'MMCCLXIV', 'MMCCLXV', 'MMCCLXVI', 'MMCCLXVII', 'MMCCLXVIII', 'MMCCLXIX', 'MMCCLXX', 'MMCCLXXI', 'MMCCLXXII', 'MMCCLXXIII', 'MMCCLXXIV', 'MMCCLXXV', 'MMCCLXXVI', 'MMCCLXXVII', 'MMCCLXXVIII', 'MMCCLXXIX', 'MMCCLXXX', 'MMCCLXXXI', 'MMCCLXXXII', 'MMCCLXXXIII', 'MMCCLXXXIV', 'MMCCLXXXV', 'MMCCLXXXVI', 'MMCCLXXXVII', 'MMCCLXXXVIII', 'MMCCLXXXIX', 'MMCCXC', 'MMCCXCI', 'MMCCXCII', 'MMCCXCIII', 'MMCCXCIV', 'MMCCVC', 'MMCCVCI', 'MMCCVCII', 'MMCCVCIII', 'MMCCIC', 'MMCCC', 'MMCCCI', 'MMCCCII', 'MMCCCIII', 'MMCCCIV', 'MMCCCV', 'MMCCCVI', 'MMCCCVII', 'MMCCCVIII', 'MMCCCIX', 'MMCCCX', 'MMCCCXI', 'MMCCCXII', 'MMCCCXIII', 'MMCCCXIV', 'MMCCCXV', 'MMCCCXVI', 'MMCCCXVII', 'MMCCCXVIII', 'MMCCCXIX', 'MMCCCXX', 'MMCCCXXI', 'MMCCCXXII', 'MMCCCXXIII', 'MMCCCXXIV', 'MMCCCXXV', 'MMCCCXXVI', 'MMCCCXXVII', 'MMCCCXXVIII', 'MMCCCXXIX', 'MMCCCXXX', 'MMCCCXXXI', 'MMCCCXXXII', 'MMCCCXXXIII', 'MMCCCXXXIV', 'MMCCCXXXV', 'MMCCCXXXVI', 'MMCCCXXXVII', 'MMCCCXXXVIII', 'MMCCCXXXIX', 'MMCCCXL', 'MMCCCXLI', 'MMCCCXLII', 'MMCCCXLIII', 'MMCCCXLIV', 'MMCCCVL', 'MMCCCVLI', 'MMCCCVLII', 'MMCCCVLIII', 'MMCCCIL', 'MMCCCL', 'MMCCCLI', 'MMCCCLII', 'MMCCCLIII', 'MMCCCLIV', 'MMCCCLV', 'MMCCCLVI', 'MMCCCLVII', 'MMCCCLVIII', 'MMCCCLIX', 'MMCCCLX', 'MMCCCLXI', 'MMCCCLXII', 'MMCCCLXIII', 'MMCCCLXIV', 'MMCCCLXV', 'MMCCCLXVI', 'MMCCCLXVII', 'MMCCCLXVIII', 'MMCCCLXIX', 'MMCCCLXX', 'MMCCCLXXI', 'MMCCCLXXII', 'MMCCCLXXIII', 'MMCCCLXXIV', 'MMCCCLXXV', 'MMCCCLXXVI', 'MMCCCLXXVII', 'MMCCCLXXVIII', 'MMCCCLXXIX', 'MMCCCLXXX', 'MMCCCLXXXI', 'MMCCCLXXXII', 'MMCCCLXXXIII', 'MMCCCLXXXIV', 'MMCCCLXXXV', 'MMCCCLXXXVI', 'MMCCCLXXXVII', 'MMCCCLXXXVIII', 'MMCCCLXXXIX', 'MMCCCXC', 'MMCCCXCI', 'MMCCCXCII', 'MMCCCXCIII', 'MMCCCXCIV', 'MMCCCVC', 'MMCCCVCI', 'MMCCCVCII', 'MMCCCVCIII', 'MMCCCIC', 'MMCD', 'MMCDI', 'MMCDII', 'MMCDIII', 'MMCDIV', 'MMCDV', 'MMCDVI', 'MMCDVII', 'MMCDVIII', 'MMCDIX', 'MMCDX', 'MMCDXI', 'MMCDXII', 'MMCDXIII', 'MMCDXIV', 'MMCDXV', 'MMCDXVI', 'MMCDXVII', 'MMCDXVIII', 'MMCDXIX', 'MMCDXX', 'MMCDXXI', 'MMCDXXII', 'MMCDXXIII', 'MMCDXXIV', 'MMCDXXV', 'MMCDXXVI', 'MMCDXXVII', 'MMCDXXVIII', 'MMCDXXIX', 'MMCDXXX', 'MMCDXXXI', 'MMCDXXXII', 'MMCDXXXIII', 'MMCDXXXIV', 'MMCDXXXV', 'MMCDXXXVI', 'MMCDXXXVII', 'MMCDXXXVIII', 'MMCDXXXIX', 'MMCDXL', 'MMCDXLI', 'MMCDXLII', 'MMCDXLIII', 'MMCDXLIV', 'MMCDVL', 'MMCDVLI', 'MMCDVLII', 'MMCDVLIII', 'MMCDIL', 'MMLD', 'MMLDI', 'MMLDII', 'MMLDIII', 'MMLDIV', 'MMLDV', 'MMLDVI', 'MMLDVII', 'MMLDVIII', 'MMLDIX', 'MMLDX', 'MMLDXI', 'MMLDXII', 'MMLDXIII', 'MMLDXIV', 'MMLDXV', 'MMLDXVI', 'MMLDXVII', 'MMLDXVIII', 'MMLDXIX', 'MMLDXX', 'MMLDXXI', 'MMLDXXII', 'MMLDXXIII', 'MMLDXXIV', 'MMLDXXV', 'MMLDXXVI', 'MMLDXXVII', 'MMLDXXVIII', 'MMLDXXIX', 'MMLDXXX', 'MMLDXXXI', 'MMLDXXXII', 'MMLDXXXIII', 'MMLDXXXIV', 'MMLDXXXV', 'MMLDXXXVI', 'MMLDXXXVII', 'MMLDXXXVIII', 'MMLDXXXIX', 'MMXD', 'MMXDI', 'MMXDII', 'MMXDIII', 'MMXDIV', 'MMVD', 'MMVDI', 'MMVDII', 'MMVDIII', 'MMVDIV', 'MMD', 'MMDI', 'MMDII', 'MMDIII', 'MMDIV', 'MMDV', 'MMDVI', 'MMDVII', 'MMDVIII', 'MMDIX', 'MMDX', 'MMDXI', 'MMDXII', 'MMDXIII', 'MMDXIV', 'MMDXV', 'MMDXVI', 'MMDXVII', 'MMDXVIII', 'MMDXIX', 'MMDXX', 'MMDXXI', 'MMDXXII', 'MMDXXIII', 'MMDXXIV', 'MMDXXV', 'MMDXXVI', 'MMDXXVII', 'MMDXXVIII', 'MMDXXIX', 'MMDXXX', 'MMDXXXI', 'MMDXXXII', 'MMDXXXIII', 'MMDXXXIV', 'MMDXXXV', 'MMDXXXVI', 'MMDXXXVII', 'MMDXXXVIII', 'MMDXXXIX', 'MMDXL', 'MMDXLI', 'MMDXLII', 'MMDXLIII', 'MMDXLIV', 'MMDVL', 'MMDVLI', 'MMDVLII', 'MMDVLIII', 'MMDIL', 'MMDL', 'MMDLI', 'MMDLII', 'MMDLIII', 'MMDLIV', 'MMDLV', 'MMDLVI', 'MMDLVII', 'MMDLVIII', 'MMDLIX', 'MMDLX', 'MMDLXI', 'MMDLXII', 'MMDLXIII', 'MMDLXIV', 'MMDLXV', 'MMDLXVI', 'MMDLXVII', 'MMDLXVIII', 'MMDLXIX', 'MMDLXX', 'MMDLXXI', 'MMDLXXII', 'MMDLXXIII', 'MMDLXXIV', 'MMDLXXV', 'MMDLXXVI', 'MMDLXXVII', 'MMDLXXVIII', 'MMDLXXIX', 'MMDLXXX', 'MMDLXXXI', 'MMDLXXXII', 'MMDLXXXIII', 'MMDLXXXIV', 'MMDLXXXV', 'MMDLXXXVI', 'MMDLXXXVII', 'MMDLXXXVIII', 'MMDLXXXIX', 'MMDXC', 'MMDXCI', 'MMDXCII', 'MMDXCIII', 'MMDXCIV', 'MMDVC', 'MMDVCI', 'MMDVCII', 'MMDVCIII', 'MMDIC', 'MMDC', 'MMDCI', 'MMDCII', 'MMDCIII', 'MMDCIV', 'MMDCV', 'MMDCVI', 'MMDCVII', 'MMDCVIII', 'MMDCIX', 'MMDCX', 'MMDCXI', 'MMDCXII', 'MMDCXIII', 'MMDCXIV', 'MMDCXV', 'MMDCXVI', 'MMDCXVII', 'MMDCXVIII', 'MMDCXIX', 'MMDCXX', 'MMDCXXI', 'MMDCXXII', 'MMDCXXIII', 'MMDCXXIV', 'MMDCXXV', 'MMDCXXVI', 'MMDCXXVII', 'MMDCXXVIII', 'MMDCXXIX', 'MMDCXXX', 'MMDCXXXI', 'MMDCXXXII', 'MMDCXXXIII', 'MMDCXXXIV', 'MMDCXXXV', 'MMDCXXXVI', 'MMDCXXXVII', 'MMDCXXXVIII', 'MMDCXXXIX', 'MMDCXL', 'MMDCXLI', 'MMDCXLII', 'MMDCXLIII', 'MMDCXLIV', 'MMDCVL', 'MMDCVLI', 'MMDCVLII', 'MMDCVLIII', 'MMDCIL', 'MMDCL', 'MMDCLI', 'MMDCLII', 'MMDCLIII', 'MMDCLIV', 'MMDCLV', 'MMDCLVI', 'MMDCLVII', 'MMDCLVIII', 'MMDCLIX', 'MMDCLX', 'MMDCLXI', 'MMDCLXII', 'MMDCLXIII', 'MMDCLXIV', 'MMDCLXV', 'MMDCLXVI', 'MMDCLXVII', 'MMDCLXVIII', 'MMDCLXIX', 'MMDCLXX', 'MMDCLXXI', 'MMDCLXXII', 'MMDCLXXIII', 'MMDCLXXIV', 'MMDCLXXV', 'MMDCLXXVI', 'MMDCLXXVII', 'MMDCLXXVIII', 'MMDCLXXIX', 'MMDCLXXX', 'MMDCLXXXI', 'MMDCLXXXII', 'MMDCLXXXIII', 'MMDCLXXXIV', 'MMDCLXXXV', 'MMDCLXXXVI', 'MMDCLXXXVII', 'MMDCLXXXVIII', 'MMDCLXXXIX', 'MMDCXC', 'MMDCXCI', 'MMDCXCII', 'MMDCXCIII', 'MMDCXCIV', 'MMDCVC', 'MMDCVCI', 'MMDCVCII', 'MMDCVCIII', 'MMDCIC', 'MMDCC', 'MMDCCI', 'MMDCCII', 'MMDCCIII', 'MMDCCIV', 'MMDCCV', 'MMDCCVI', 'MMDCCVII', 'MMDCCVIII', 'MMDCCIX', 'MMDCCX', 'MMDCCXI', 'MMDCCXII', 'MMDCCXIII', 'MMDCCXIV', 'MMDCCXV', 'MMDCCXVI', 'MMDCCXVII', 'MMDCCXVIII', 'MMDCCXIX', 'MMDCCXX', 'MMDCCXXI', 'MMDCCXXII', 'MMDCCXXIII', 'MMDCCXXIV', 'MMDCCXXV', 'MMDCCXXVI', 'MMDCCXXVII', 'MMDCCXXVIII', 'MMDCCXXIX', 'MMDCCXXX', 'MMDCCXXXI', 'MMDCCXXXII', 'MMDCCXXXIII', 'MMDCCXXXIV', 'MMDCCXXXV', 'MMDCCXXXVI', 'MMDCCXXXVII', 'MMDCCXXXVIII', 'MMDCCXXXIX', 'MMDCCXL', 'MMDCCXLI', 'MMDCCXLII', 'MMDCCXLIII', 'MMDCCXLIV', 'MMDCCVL', 'MMDCCVLI', 'MMDCCVLII', 'MMDCCVLIII', 'MMDCCIL', 'MMDCCL', 'MMDCCLI', 'MMDCCLII', 'MMDCCLIII', 'MMDCCLIV', 'MMDCCLV', 'MMDCCLVI', 'MMDCCLVII', 'MMDCCLVIII', 'MMDCCLIX', 'MMDCCLX', 'MMDCCLXI', 'MMDCCLXII', 'MMDCCLXIII', 'MMDCCLXIV', 'MMDCCLXV', 'MMDCCLXVI', 'MMDCCLXVII', 'MMDCCLXVIII', 'MMDCCLXIX', 'MMDCCLXX', 'MMDCCLXXI', 'MMDCCLXXII', 'MMDCCLXXIII', 'MMDCCLXXIV', 'MMDCCLXXV', 'MMDCCLXXVI', 'MMDCCLXXVII', 'MMDCCLXXVIII', 'MMDCCLXXIX', 'MMDCCLXXX', 'MMDCCLXXXI', 'MMDCCLXXXII', 'MMDCCLXXXIII', 'MMDCCLXXXIV', 'MMDCCLXXXV', 'MMDCCLXXXVI', 'MMDCCLXXXVII', 'MMDCCLXXXVIII', 'MMDCCLXXXIX', 'MMDCCXC', 'MMDCCXCI', 'MMDCCXCII', 'MMDCCXCIII', 'MMDCCXCIV', 'MMDCCVC', 'MMDCCVCI', 'MMDCCVCII', 'MMDCCVCIII', 'MMDCCIC', 'MMDCCC', 'MMDCCCI', 'MMDCCCII', 'MMDCCCIII', 'MMDCCCIV', 'MMDCCCV', 'MMDCCCVI', 'MMDCCCVII', 'MMDCCCVIII', 'MMDCCCIX', 'MMDCCCX', 'MMDCCCXI', 'MMDCCCXII', 'MMDCCCXIII', 'MMDCCCXIV', 'MMDCCCXV', 'MMDCCCXVI', 'MMDCCCXVII', 'MMDCCCXVIII', 'MMDCCCXIX', 'MMDCCCXX', 'MMDCCCXXI', 'MMDCCCXXII', 'MMDCCCXXIII', 'MMDCCCXXIV', 'MMDCCCXXV', 'MMDCCCXXVI', 'MMDCCCXXVII', 'MMDCCCXXVIII', 'MMDCCCXXIX', 'MMDCCCXXX', 'MMDCCCXXXI', 'MMDCCCXXXII', 'MMDCCCXXXIII', 'MMDCCCXXXIV', 'MMDCCCXXXV', 'MMDCCCXXXVI', 'MMDCCCXXXVII', 'MMDCCCXXXVIII', 'MMDCCCXXXIX', 'MMDCCCXL', 'MMDCCCXLI', 'MMDCCCXLII', 'MMDCCCXLIII', 'MMDCCCXLIV', 'MMDCCCVL', 'MMDCCCVLI', 'MMDCCCVLII', 'MMDCCCVLIII', 'MMDCCCIL', 'MMDCCCL', 'MMDCCCLI', 'MMDCCCLII', 'MMDCCCLIII', 'MMDCCCLIV', 'MMDCCCLV', 'MMDCCCLVI', 'MMDCCCLVII', 'MMDCCCLVIII', 'MMDCCCLIX', 'MMDCCCLX', 'MMDCCCLXI', 'MMDCCCLXII', 'MMDCCCLXIII', 'MMDCCCLXIV', 'MMDCCCLXV', 'MMDCCCLXVI', 'MMDCCCLXVII', 'MMDCCCLXVIII', 'MMDCCCLXIX', 'MMDCCCLXX', 'MMDCCCLXXI', 'MMDCCCLXXII', 'MMDCCCLXXIII', 'MMDCCCLXXIV', 'MMDCCCLXXV', 'MMDCCCLXXVI', 'MMDCCCLXXVII', 'MMDCCCLXXVIII', 'MMDCCCLXXIX', 'MMDCCCLXXX', 'MMDCCCLXXXI', 'MMDCCCLXXXII', 'MMDCCCLXXXIII', 'MMDCCCLXXXIV', 'MMDCCCLXXXV', 'MMDCCCLXXXVI', 'MMDCCCLXXXVII', 'MMDCCCLXXXVIII', 'MMDCCCLXXXIX', 'MMDCCCXC', 'MMDCCCXCI', 'MMDCCCXCII', 'MMDCCCXCIII', 'MMDCCCXCIV', 'MMDCCCVC', 'MMDCCCVCI', 'MMDCCCVCII', 'MMDCCCVCIII', 'MMDCCCIC', 'MMCM', 'MMCMI', 'MMCMII', 'MMCMIII', 'MMCMIV', 'MMCMV', 'MMCMVI', 'MMCMVII', 'MMCMVIII', 'MMCMIX', 'MMCMX', 'MMCMXI', 'MMCMXII', 'MMCMXIII', 'MMCMXIV', 'MMCMXV', 'MMCMXVI', 'MMCMXVII', 'MMCMXVIII', 'MMCMXIX', 'MMCMXX', 'MMCMXXI', 'MMCMXXII', 'MMCMXXIII', 'MMCMXXIV', 'MMCMXXV', 'MMCMXXVI', 'MMCMXXVII', 'MMCMXXVIII', 'MMCMXXIX', 'MMCMXXX', 'MMCMXXXI', 'MMCMXXXII', 'MMCMXXXIII', 'MMCMXXXIV', 'MMCMXXXV', 'MMCMXXXVI', 'MMCMXXXVII', 'MMCMXXXVIII', 'MMCMXXXIX', 'MMCMXL', 'MMCMXLI', 'MMCMXLII', 'MMCMXLIII', 'MMCMXLIV', 'MMCMVL', 'MMCMVLI', 'MMCMVLII', 'MMCMVLIII', 'MMCMIL', 'MMLM', 'MMLMI', 'MMLMII', 'MMLMIII', 'MMLMIV', 'MMLMV', 'MMLMVI', 'MMLMVII', 'MMLMVIII', 'MMLMIX', 'MMLMX', 'MMLMXI', 'MMLMXII', 'MMLMXIII', 'MMLMXIV', 'MMLMXV', 'MMLMXVI', 'MMLMXVII', 'MMLMXVIII', 'MMLMXIX', 'MMLMXX', 'MMLMXXI', 'MMLMXXII', 'MMLMXXIII', 'MMLMXXIV', 'MMLMXXV', 'MMLMXXVI', 'MMLMXXVII', 'MMLMXXVIII', 'MMLMXXIX', 'MMLMXXX', 'MMLMXXXI', 'MMLMXXXII', 'MMLMXXXIII', 'MMLMXXXIV', 'MMLMXXXV', 'MMLMXXXVI', 'MMLMXXXVII', 'MMLMXXXVIII', 'MMLMXXXIX', 'MMXM', 'MMXMI', 'MMXMII', 'MMXMIII', 'MMXMIV', 'MMVM', 'MMVMI', 'MMVMII', 'MMVMIII', 'MMVMIV', 'MMM', 'MMMI', 'MMMII', 'MMMIII', 'MMMIV', 'MMMV', 'MMMVI', 'MMMVII', 'MMMVIII', 'MMMIX', 'MMMX', 'MMMXI', 'MMMXII', 'MMMXIII', 'MMMXIV', 'MMMXV', 'MMMXVI', 'MMMXVII', 'MMMXVIII', 'MMMXIX', 'MMMXX', 'MMMXXI', 'MMMXXII', 'MMMXXIII', 'MMMXXIV', 'MMMXXV', 'MMMXXVI', 'MMMXXVII', 'MMMXXVIII', 'MMMXXIX', 'MMMXXX', 'MMMXXXI', 'MMMXXXII', 'MMMXXXIII', 'MMMXXXIV', 'MMMXXXV', 'MMMXXXVI', 'MMMXXXVII', 'MMMXXXVIII', 'MMMXXXIX', 'MMMXL', 'MMMXLI', 'MMMXLII', 'MMMXLIII', 'MMMXLIV', 'MMMVL', 'MMMVLI', 'MMMVLII', 'MMMVLIII', 'MMMIL', 'MMML', 'MMMLI', 'MMMLII', 'MMMLIII', 'MMMLIV', 'MMMLV', 'MMMLVI', 'MMMLVII', 'MMMLVIII', 'MMMLIX', 'MMMLX', 'MMMLXI', 'MMMLXII', 'MMMLXIII', 'MMMLXIV', 'MMMLXV', 'MMMLXVI', 'MMMLXVII', 'MMMLXVIII', 'MMMLXIX', 'MMMLXX', 'MMMLXXI', 'MMMLXXII', 'MMMLXXIII', 'MMMLXXIV', 'MMMLXXV', 'MMMLXXVI', 'MMMLXXVII', 'MMMLXXVIII', 'MMMLXXIX', 'MMMLXXX', 'MMMLXXXI', 'MMMLXXXII', 'MMMLXXXIII', 'MMMLXXXIV', 'MMMLXXXV', 'MMMLXXXVI', 'MMMLXXXVII', 'MMMLXXXVIII', 'MMMLXXXIX', 'MMMXC', 'MMMXCI', 'MMMXCII', 'MMMXCIII', 'MMMXCIV', 'MMMVC', 'MMMVCI', 'MMMVCII', 'MMMVCIII', 'MMMIC', 'MMMC', 'MMMCI', 'MMMCII', 'MMMCIII', 'MMMCIV', 'MMMCV', 'MMMCVI', 'MMMCVII', 'MMMCVIII', 'MMMCIX', 'MMMCX', 'MMMCXI', 'MMMCXII', 'MMMCXIII', 'MMMCXIV', 'MMMCXV', 'MMMCXVI', 'MMMCXVII', 'MMMCXVIII', 'MMMCXIX', 'MMMCXX', 'MMMCXXI', 'MMMCXXII', 'MMMCXXIII', 'MMMCXXIV', 'MMMCXXV', 'MMMCXXVI', 'MMMCXXVII', 'MMMCXXVIII', 'MMMCXXIX', 'MMMCXXX', 'MMMCXXXI', 'MMMCXXXII', 'MMMCXXXIII', 'MMMCXXXIV', 'MMMCXXXV', 'MMMCXXXVI', 'MMMCXXXVII', 'MMMCXXXVIII', 'MMMCXXXIX', 'MMMCXL', 'MMMCXLI', 'MMMCXLII', 'MMMCXLIII', 'MMMCXLIV', 'MMMCVL', 'MMMCVLI', 'MMMCVLII', 'MMMCVLIII', 'MMMCIL', 'MMMCL', 'MMMCLI', 'MMMCLII', 'MMMCLIII', 'MMMCLIV', 'MMMCLV', 'MMMCLVI', 'MMMCLVII', 'MMMCLVIII', 'MMMCLIX', 'MMMCLX', 'MMMCLXI', 'MMMCLXII', 'MMMCLXIII', 'MMMCLXIV', 'MMMCLXV', 'MMMCLXVI', 'MMMCLXVII', 'MMMCLXVIII', 'MMMCLXIX', 'MMMCLXX', 'MMMCLXXI', 'MMMCLXXII', 'MMMCLXXIII', 'MMMCLXXIV', 'MMMCLXXV', 'MMMCLXXVI', 'MMMCLXXVII', 'MMMCLXXVIII', 'MMMCLXXIX', 'MMMCLXXX', 'MMMCLXXXI', 'MMMCLXXXII', 'MMMCLXXXIII', 'MMMCLXXXIV', 'MMMCLXXXV', 'MMMCLXXXVI', 'MMMCLXXXVII', 'MMMCLXXXVIII', 'MMMCLXXXIX', 'MMMCXC', 'MMMCXCI', 'MMMCXCII', 'MMMCXCIII', 'MMMCXCIV', 'MMMCVC', 'MMMCVCI', 'MMMCVCII', 'MMMCVCIII', 'MMMCIC', 'MMMCC', 'MMMCCI', 'MMMCCII', 'MMMCCIII', 'MMMCCIV', 'MMMCCV', 'MMMCCVI', 'MMMCCVII', 'MMMCCVIII', 'MMMCCIX', 'MMMCCX', 'MMMCCXI', 'MMMCCXII', 'MMMCCXIII', 'MMMCCXIV', 'MMMCCXV', 'MMMCCXVI', 'MMMCCXVII', 'MMMCCXVIII', 'MMMCCXIX', 'MMMCCXX', 'MMMCCXXI', 'MMMCCXXII', 'MMMCCXXIII', 'MMMCCXXIV', 'MMMCCXXV', 'MMMCCXXVI', 'MMMCCXXVII', 'MMMCCXXVIII', 'MMMCCXXIX', 'MMMCCXXX', 'MMMCCXXXI', 'MMMCCXXXII', 'MMMCCXXXIII', 'MMMCCXXXIV', 'MMMCCXXXV', 'MMMCCXXXVI', 'MMMCCXXXVII', 'MMMCCXXXVIII', 'MMMCCXXXIX', 'MMMCCXL', 'MMMCCXLI', 'MMMCCXLII', 'MMMCCXLIII', 'MMMCCXLIV', 'MMMCCVL', 'MMMCCVLI', 'MMMCCVLII', 'MMMCCVLIII', 'MMMCCIL', 'MMMCCL', 'MMMCCLI', 'MMMCCLII', 'MMMCCLIII', 'MMMCCLIV', 'MMMCCLV', 'MMMCCLVI', 'MMMCCLVII', 'MMMCCLVIII', 'MMMCCLIX', 'MMMCCLX', 'MMMCCLXI', 'MMMCCLXII', 'MMMCCLXIII', 'MMMCCLXIV', 'MMMCCLXV', 'MMMCCLXVI', 'MMMCCLXVII', 'MMMCCLXVIII', 'MMMCCLXIX', 'MMMCCLXX', 'MMMCCLXXI', 'MMMCCLXXII', 'MMMCCLXXIII', 'MMMCCLXXIV', 'MMMCCLXXV', 'MMMCCLXXVI', 'MMMCCLXXVII', 'MMMCCLXXVIII', 'MMMCCLXXIX', 'MMMCCLXXX', 'MMMCCLXXXI', 'MMMCCLXXXII', 'MMMCCLXXXIII', 'MMMCCLXXXIV', 'MMMCCLXXXV', 'MMMCCLXXXVI', 'MMMCCLXXXVII', 'MMMCCLXXXVIII', 'MMMCCLXXXIX', 'MMMCCXC', 'MMMCCXCI', 'MMMCCXCII', 'MMMCCXCIII', 'MMMCCXCIV', 'MMMCCVC', 'MMMCCVCI', 'MMMCCVCII', 'MMMCCVCIII', 'MMMCCIC', 'MMMCCC', 'MMMCCCI', 'MMMCCCII', 'MMMCCCIII', 'MMMCCCIV', 'MMMCCCV', 'MMMCCCVI', 'MMMCCCVII', 'MMMCCCVIII', 'MMMCCCIX', 'MMMCCCX', 'MMMCCCXI', 'MMMCCCXII', 'MMMCCCXIII', 'MMMCCCXIV', 'MMMCCCXV', 'MMMCCCXVI', 'MMMCCCXVII', 'MMMCCCXVIII', 'MMMCCCXIX', 'MMMCCCXX', 'MMMCCCXXI', 'MMMCCCXXII', 'MMMCCCXXIII', 'MMMCCCXXIV', 'MMMCCCXXV', 'MMMCCCXXVI', 'MMMCCCXXVII', 'MMMCCCXXVIII', 'MMMCCCXXIX', 'MMMCCCXXX', 'MMMCCCXXXI', 'MMMCCCXXXII', 'MMMCCCXXXIII', 'MMMCCCXXXIV', 'MMMCCCXXXV', 'MMMCCCXXXVI', 'MMMCCCXXXVII', 'MMMCCCXXXVIII', 'MMMCCCXXXIX', 'MMMCCCXL', 'MMMCCCXLI', 'MMMCCCXLII', 'MMMCCCXLIII', 'MMMCCCXLIV', 'MMMCCCVL', 'MMMCCCVLI', 'MMMCCCVLII', 'MMMCCCVLIII', 'MMMCCCIL', 'MMMCCCL', 'MMMCCCLI', 'MMMCCCLII', 'MMMCCCLIII', 'MMMCCCLIV', 'MMMCCCLV', 'MMMCCCLVI', 'MMMCCCLVII', 'MMMCCCLVIII', 'MMMCCCLIX', 'MMMCCCLX', 'MMMCCCLXI', 'MMMCCCLXII', 'MMMCCCLXIII', 'MMMCCCLXIV', 'MMMCCCLXV', 'MMMCCCLXVI', 'MMMCCCLXVII', 'MMMCCCLXVIII', 'MMMCCCLXIX', 'MMMCCCLXX', 'MMMCCCLXXI', 'MMMCCCLXXII', 'MMMCCCLXXIII', 'MMMCCCLXXIV', 'MMMCCCLXXV', 'MMMCCCLXXVI', 'MMMCCCLXXVII', 'MMMCCCLXXVIII', 'MMMCCCLXXIX', 'MMMCCCLXXX', 'MMMCCCLXXXI', 'MMMCCCLXXXII', 'MMMCCCLXXXIII', 'MMMCCCLXXXIV', 'MMMCCCLXXXV', 'MMMCCCLXXXVI', 'MMMCCCLXXXVII', 'MMMCCCLXXXVIII', 'MMMCCCLXXXIX', 'MMMCCCXC', 'MMMCCCXCI', 'MMMCCCXCII', 'MMMCCCXCIII', 'MMMCCCXCIV', 'MMMCCCVC', 'MMMCCCVCI', 'MMMCCCVCII', 'MMMCCCVCIII', 'MMMCCCIC', 'MMMCD', 'MMMCDI', 'MMMCDII', 'MMMCDIII', 'MMMCDIV', 'MMMCDV', 'MMMCDVI', 'MMMCDVII', 'MMMCDVIII', 'MMMCDIX', 'MMMCDX', 'MMMCDXI', 'MMMCDXII', 'MMMCDXIII', 'MMMCDXIV', 'MMMCDXV', 'MMMCDXVI', 'MMMCDXVII', 'MMMCDXVIII', 'MMMCDXIX', 'MMMCDXX', 'MMMCDXXI', 'MMMCDXXII', 'MMMCDXXIII', 'MMMCDXXIV', 'MMMCDXXV', 'MMMCDXXVI', 'MMMCDXXVII', 'MMMCDXXVIII', 'MMMCDXXIX', 'MMMCDXXX', 'MMMCDXXXI', 'MMMCDXXXII', 'MMMCDXXXIII', 'MMMCDXXXIV', 'MMMCDXXXV', 'MMMCDXXXVI', 'MMMCDXXXVII', 'MMMCDXXXVIII', 'MMMCDXXXIX', 'MMMCDXL', 'MMMCDXLI', 'MMMCDXLII', 'MMMCDXLIII', 'MMMCDXLIV', 'MMMCDVL', 'MMMCDVLI', 'MMMCDVLII', 'MMMCDVLIII', 'MMMCDIL', 'MMMLD', 'MMMLDI', 'MMMLDII', 'MMMLDIII', 'MMMLDIV', 'MMMLDV', 'MMMLDVI', 'MMMLDVII', 'MMMLDVIII', 'MMMLDIX', 'MMMLDX', 'MMMLDXI', 'MMMLDXII', 'MMMLDXIII', 'MMMLDXIV', 'MMMLDXV', 'MMMLDXVI', 'MMMLDXVII', 'MMMLDXVIII', 'MMMLDXIX', 'MMMLDXX', 'MMMLDXXI', 'MMMLDXXII', 'MMMLDXXIII', 'MMMLDXXIV', 'MMMLDXXV', 'MMMLDXXVI', 'MMMLDXXVII', 'MMMLDXXVIII', 'MMMLDXXIX', 'MMMLDXXX', 'MMMLDXXXI', 'MMMLDXXXII', 'MMMLDXXXIII', 'MMMLDXXXIV', 'MMMLDXXXV', 'MMMLDXXXVI', 'MMMLDXXXVII', 'MMMLDXXXVIII', 'MMMLDXXXIX', 'MMMXD', 'MMMXDI', 'MMMXDII', 'MMMXDIII', 'MMMXDIV', 'MMMVD', 'MMMVDI', 'MMMVDII', 'MMMVDIII', 'MMMVDIV', 'MMMD', 'MMMDI', 'MMMDII', 'MMMDIII', 'MMMDIV', 'MMMDV', 'MMMDVI', 'MMMDVII', 'MMMDVIII', 'MMMDIX', 'MMMDX', 'MMMDXI', 'MMMDXII', 'MMMDXIII', 'MMMDXIV', 'MMMDXV', 'MMMDXVI', 'MMMDXVII', 'MMMDXVIII', 'MMMDXIX', 'MMMDXX', 'MMMDXXI', 'MMMDXXII', 'MMMDXXIII', 'MMMDXXIV', 'MMMDXXV', 'MMMDXXVI', 'MMMDXXVII', 'MMMDXXVIII', 'MMMDXXIX', 'MMMDXXX', 'MMMDXXXI', 'MMMDXXXII', 'MMMDXXXIII', 'MMMDXXXIV', 'MMMDXXXV', 'MMMDXXXVI', 'MMMDXXXVII', 'MMMDXXXVIII', 'MMMDXXXIX', 'MMMDXL', 'MMMDXLI', 'MMMDXLII', 'MMMDXLIII', 'MMMDXLIV', 'MMMDVL', 'MMMDVLI', 'MMMDVLII', 'MMMDVLIII', 'MMMDIL', 'MMMDL', 'MMMDLI', 'MMMDLII', 'MMMDLIII', 'MMMDLIV', 'MMMDLV', 'MMMDLVI', 'MMMDLVII', 'MMMDLVIII', 'MMMDLIX', 'MMMDLX', 'MMMDLXI', 'MMMDLXII', 'MMMDLXIII', 'MMMDLXIV', 'MMMDLXV', 'MMMDLXVI', 'MMMDLXVII', 'MMMDLXVIII', 'MMMDLXIX', 'MMMDLXX', 'MMMDLXXI', 'MMMDLXXII', 'MMMDLXXIII', 'MMMDLXXIV', 'MMMDLXXV', 'MMMDLXXVI', 'MMMDLXXVII', 'MMMDLXXVIII', 'MMMDLXXIX', 'MMMDLXXX', 'MMMDLXXXI', 'MMMDLXXXII', 'MMMDLXXXIII', 'MMMDLXXXIV', 'MMMDLXXXV', 'MMMDLXXXVI', 'MMMDLXXXVII', 'MMMDLXXXVIII', 'MMMDLXXXIX', 'MMMDXC', 'MMMDXCI', 'MMMDXCII', 'MMMDXCIII', 'MMMDXCIV', 'MMMDVC', 'MMMDVCI', 'MMMDVCII', 'MMMDVCIII', 'MMMDIC', 'MMMDC', 'MMMDCI', 'MMMDCII', 'MMMDCIII', 'MMMDCIV', 'MMMDCV', 'MMMDCVI', 'MMMDCVII', 'MMMDCVIII', 'MMMDCIX', 'MMMDCX', 'MMMDCXI', 'MMMDCXII', 'MMMDCXIII', 'MMMDCXIV', 'MMMDCXV', 'MMMDCXVI', 'MMMDCXVII', 'MMMDCXVIII', 'MMMDCXIX', 'MMMDCXX', 'MMMDCXXI', 'MMMDCXXII', 'MMMDCXXIII', 'MMMDCXXIV', 'MMMDCXXV', 'MMMDCXXVI', 'MMMDCXXVII', 'MMMDCXXVIII', 'MMMDCXXIX', 'MMMDCXXX', 'MMMDCXXXI', 'MMMDCXXXII', 'MMMDCXXXIII', 'MMMDCXXXIV', 'MMMDCXXXV', 'MMMDCXXXVI', 'MMMDCXXXVII', 'MMMDCXXXVIII', 'MMMDCXXXIX', 'MMMDCXL', 'MMMDCXLI', 'MMMDCXLII', 'MMMDCXLIII', 'MMMDCXLIV', 'MMMDCVL', 'MMMDCVLI', 'MMMDCVLII', 'MMMDCVLIII', 'MMMDCIL', 'MMMDCL', 'MMMDCLI', 'MMMDCLII', 'MMMDCLIII', 'MMMDCLIV', 'MMMDCLV', 'MMMDCLVI', 'MMMDCLVII', 'MMMDCLVIII', 'MMMDCLIX', 'MMMDCLX', 'MMMDCLXI', 'MMMDCLXII', 'MMMDCLXIII', 'MMMDCLXIV', 'MMMDCLXV', 'MMMDCLXVI', 'MMMDCLXVII', 'MMMDCLXVIII', 'MMMDCLXIX', 'MMMDCLXX', 'MMMDCLXXI', 'MMMDCLXXII', 'MMMDCLXXIII', 'MMMDCLXXIV', 'MMMDCLXXV', 'MMMDCLXXVI', 'MMMDCLXXVII', 'MMMDCLXXVIII', 'MMMDCLXXIX', 'MMMDCLXXX', 'MMMDCLXXXI', 'MMMDCLXXXII', 'MMMDCLXXXIII', 'MMMDCLXXXIV', 'MMMDCLXXXV', 'MMMDCLXXXVI', 'MMMDCLXXXVII', 'MMMDCLXXXVIII', 'MMMDCLXXXIX', 'MMMDCXC', 'MMMDCXCI', 'MMMDCXCII', 'MMMDCXCIII', 'MMMDCXCIV', 'MMMDCVC', 'MMMDCVCI', 'MMMDCVCII', 'MMMDCVCIII', 'MMMDCIC', 'MMMDCC', 'MMMDCCI', 'MMMDCCII', 'MMMDCCIII', 'MMMDCCIV', 'MMMDCCV', 'MMMDCCVI', 'MMMDCCVII', 'MMMDCCVIII', 'MMMDCCIX', 'MMMDCCX', 'MMMDCCXI', 'MMMDCCXII', 'MMMDCCXIII', 'MMMDCCXIV', 'MMMDCCXV', 'MMMDCCXVI', 'MMMDCCXVII', 'MMMDCCXVIII', 'MMMDCCXIX', 'MMMDCCXX', 'MMMDCCXXI', 'MMMDCCXXII', 'MMMDCCXXIII', 'MMMDCCXXIV', 'MMMDCCXXV', 'MMMDCCXXVI', 'MMMDCCXXVII', 'MMMDCCXXVIII', 'MMMDCCXXIX', 'MMMDCCXXX', 'MMMDCCXXXI', 'MMMDCCXXXII', 'MMMDCCXXXIII', 'MMMDCCXXXIV', 'MMMDCCXXXV', 'MMMDCCXXXVI', 'MMMDCCXXXVII', 'MMMDCCXXXVIII', 'MMMDCCXXXIX', 'MMMDCCXL', 'MMMDCCXLI', 'MMMDCCXLII', 'MMMDCCXLIII', 'MMMDCCXLIV', 'MMMDCCVL', 'MMMDCCVLI', 'MMMDCCVLII', 'MMMDCCVLIII', 'MMMDCCIL', 'MMMDCCL', 'MMMDCCLI', 'MMMDCCLII', 'MMMDCCLIII', 'MMMDCCLIV', 'MMMDCCLV', 'MMMDCCLVI', 'MMMDCCLVII', 'MMMDCCLVIII', 'MMMDCCLIX', 'MMMDCCLX', 'MMMDCCLXI', 'MMMDCCLXII', 'MMMDCCLXIII', 'MMMDCCLXIV', 'MMMDCCLXV', 'MMMDCCLXVI', 'MMMDCCLXVII', 'MMMDCCLXVIII', 'MMMDCCLXIX', 'MMMDCCLXX', 'MMMDCCLXXI', 'MMMDCCLXXII', 'MMMDCCLXXIII', 'MMMDCCLXXIV', 'MMMDCCLXXV', 'MMMDCCLXXVI', 'MMMDCCLXXVII', 'MMMDCCLXXVIII', 'MMMDCCLXXIX', 'MMMDCCLXXX', 'MMMDCCLXXXI', 'MMMDCCLXXXII', 'MMMDCCLXXXIII', 'MMMDCCLXXXIV', 'MMMDCCLXXXV', 'MMMDCCLXXXVI', 'MMMDCCLXXXVII', 'MMMDCCLXXXVIII', 'MMMDCCLXXXIX', 'MMMDCCXC', 'MMMDCCXCI', 'MMMDCCXCII', 'MMMDCCXCIII', 'MMMDCCXCIV', 'MMMDCCVC', 'MMMDCCVCI', 'MMMDCCVCII', 'MMMDCCVCIII', 'MMMDCCIC', 'MMMDCCC', 'MMMDCCCI', 'MMMDCCCII', 'MMMDCCCIII', 'MMMDCCCIV', 'MMMDCCCV', 'MMMDCCCVI', 'MMMDCCCVII', 'MMMDCCCVIII', 'MMMDCCCIX', 'MMMDCCCX', 'MMMDCCCXI', 'MMMDCCCXII', 'MMMDCCCXIII', 'MMMDCCCXIV', 'MMMDCCCXV', 'MMMDCCCXVI', 'MMMDCCCXVII', 'MMMDCCCXVIII', 'MMMDCCCXIX', 'MMMDCCCXX', 'MMMDCCCXXI', 'MMMDCCCXXII', 'MMMDCCCXXIII', 'MMMDCCCXXIV', 'MMMDCCCXXV', 'MMMDCCCXXVI', 'MMMDCCCXXVII', 'MMMDCCCXXVIII', 'MMMDCCCXXIX', 'MMMDCCCXXX', 'MMMDCCCXXXI', 'MMMDCCCXXXII', 'MMMDCCCXXXIII', 'MMMDCCCXXXIV', 'MMMDCCCXXXV', 'MMMDCCCXXXVI', 'MMMDCCCXXXVII', 'MMMDCCCXXXVIII', 'MMMDCCCXXXIX', 'MMMDCCCXL', 'MMMDCCCXLI', 'MMMDCCCXLII', 'MMMDCCCXLIII', 'MMMDCCCXLIV', 'MMMDCCCVL', 'MMMDCCCVLI', 'MMMDCCCVLII', 'MMMDCCCVLIII', 'MMMDCCCIL', 'MMMDCCCL', 'MMMDCCCLI', 'MMMDCCCLII', 'MMMDCCCLIII', 'MMMDCCCLIV', 'MMMDCCCLV', 'MMMDCCCLVI', 'MMMDCCCLVII', 'MMMDCCCLVIII', 'MMMDCCCLIX', 'MMMDCCCLX', 'MMMDCCCLXI', 'MMMDCCCLXII', 'MMMDCCCLXIII', 'MMMDCCCLXIV', 'MMMDCCCLXV', 'MMMDCCCLXVI', 'MMMDCCCLXVII', 'MMMDCCCLXVIII', 'MMMDCCCLXIX', 'MMMDCCCLXX', 'MMMDCCCLXXI', 'MMMDCCCLXXII', 'MMMDCCCLXXIII', 'MMMDCCCLXXIV', 'MMMDCCCLXXV', 'MMMDCCCLXXVI', 'MMMDCCCLXXVII', 'MMMDCCCLXXVIII', 'MMMDCCCLXXIX', 'MMMDCCCLXXX', 'MMMDCCCLXXXI', 'MMMDCCCLXXXII', 'MMMDCCCLXXXIII', 'MMMDCCCLXXXIV', 'MMMDCCCLXXXV', 'MMMDCCCLXXXVI', 'MMMDCCCLXXXVII', 'MMMDCCCLXXXVIII', 'MMMDCCCLXXXIX', 'MMMDCCCXC', 'MMMDCCCXCI', 'MMMDCCCXCII', 'MMMDCCCXCIII', 'MMMDCCCXCIV', 'MMMDCCCVC', 'MMMDCCCVCI', 'MMMDCCCVCII', 'MMMDCCCVCIII', 'MMMDCCCIC', 'MMMCM', 'MMMCMI', 'MMMCMII', 'MMMCMIII', 'MMMCMIV', 'MMMCMV', 'MMMCMVI', 'MMMCMVII', 'MMMCMVIII', 'MMMCMIX', 'MMMCMX', 'MMMCMXI', 'MMMCMXII', 'MMMCMXIII', 'MMMCMXIV', 'MMMCMXV', 'MMMCMXVI', 'MMMCMXVII', 'MMMCMXVIII', 'MMMCMXIX', 'MMMCMXX', 'MMMCMXXI', 'MMMCMXXII', 'MMMCMXXIII', 'MMMCMXXIV', 'MMMCMXXV', 'MMMCMXXVI', 'MMMCMXXVII', 'MMMCMXXVIII', 'MMMCMXXIX', 'MMMCMXXX', 'MMMCMXXXI', 'MMMCMXXXII', 'MMMCMXXXIII', 'MMMCMXXXIV', 'MMMCMXXXV', 'MMMCMXXXVI', 'MMMCMXXXVII', 'MMMCMXXXVIII', 'MMMCMXXXIX', 'MMMCMXL', 'MMMCMXLI', 'MMMCMXLII', 'MMMCMXLIII', 'MMMCMXLIV', 'MMMCMVL', 'MMMCMVLI', 'MMMCMVLII', 'MMMCMVLIII', 'MMMCMIL', 'MMMLM', 'MMMLMI', 'MMMLMII', 'MMMLMIII', 'MMMLMIV', 'MMMLMV', 'MMMLMVI', 'MMMLMVII', 'MMMLMVIII', 'MMMLMIX', 'MMMLMX', 'MMMLMXI', 'MMMLMXII', 'MMMLMXIII', 'MMMLMXIV', 'MMMLMXV', 'MMMLMXVI', 'MMMLMXVII', 'MMMLMXVIII', 'MMMLMXIX', 'MMMLMXX', 'MMMLMXXI', 'MMMLMXXII', 'MMMLMXXIII', 'MMMLMXXIV', 'MMMLMXXV', 'MMMLMXXVI', 'MMMLMXXVII', 'MMMLMXXVIII', 'MMMLMXXIX', 'MMMLMXXX', 'MMMLMXXXI', 'MMMLMXXXII', 'MMMLMXXXIII', 'MMMLMXXXIV', 'MMMLMXXXV', 'MMMLMXXXVI', 'MMMLMXXXVII', 'MMMLMXXXVIII', 'MMMLMXXXIX', 'MMMXM', 'MMMXMI', 'MMMXMII', 'MMMXMIII', 'MMMXMIV', 'MMMVM', 'MMMVMI', 'MMMVMII', 'MMMVMIII', 'MMMVMIV', ] -const mode4 = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX', 'XXI', 'XXII', 'XXIII', 'XXIV', 'XXV', 'XXVI', 'XXVII', 'XXVIII', 'XXIX', 'XXX', 'XXXI', 'XXXII', 'XXXIII', 'XXXIV', 'XXXV', 'XXXVI', 'XXXVII', 'XXXVIII', 'XXXIX', 'XL', 'XLI', 'XLII', 'XLIII', 'XLIV', 'VL', 'VLI', 'VLII', 'VLIII', 'IL', 'L', 'LI', 'LII', 'LIII', 'LIV', 'LV', 'LVI', 'LVII', 'LVIII', 'LIX', 'LX', 'LXI', 'LXII', 'LXIII', 'LXIV', 'LXV', 'LXVI', 'LXVII', 'LXVIII', 'LXIX', 'LXX', 'LXXI', 'LXXII', 'LXXIII', 'LXXIV', 'LXXV', 'LXXVI', 'LXXVII', 'LXXVIII', 'LXXIX', 'LXXX', 'LXXXI', 'LXXXII', 'LXXXIII', 'LXXXIV', 'LXXXV', 'LXXXVI', 'LXXXVII', 'LXXXVIII', 'LXXXIX', 'XC', 'XCI', 'XCII', 'XCIII', 'XCIV', 'VC', 'VCI', 'VCII', 'VCIII', 'IC', 'C', 'CI', 'CII', 'CIII', 'CIV', 'CV', 'CVI', 'CVII', 'CVIII', 'CIX', 'CX', 'CXI', 'CXII', 'CXIII', 'CXIV', 'CXV', 'CXVI', 'CXVII', 'CXVIII', 'CXIX', 'CXX', 'CXXI', 'CXXII', 'CXXIII', 'CXXIV', 'CXXV', 'CXXVI', 'CXXVII', 'CXXVIII', 'CXXIX', 'CXXX', 'CXXXI', 'CXXXII', 'CXXXIII', 'CXXXIV', 'CXXXV', 'CXXXVI', 'CXXXVII', 'CXXXVIII', 'CXXXIX', 'CXL', 'CXLI', 'CXLII', 'CXLIII', 'CXLIV', 'CVL', 'CVLI', 'CVLII', 'CVLIII', 'CIL', 'CL', 'CLI', 'CLII', 'CLIII', 'CLIV', 'CLV', 'CLVI', 'CLVII', 'CLVIII', 'CLIX', 'CLX', 'CLXI', 'CLXII', 'CLXIII', 'CLXIV', 'CLXV', 'CLXVI', 'CLXVII', 'CLXVIII', 'CLXIX', 'CLXX', 'CLXXI', 'CLXXII', 'CLXXIII', 'CLXXIV', 'CLXXV', 'CLXXVI', 'CLXXVII', 'CLXXVIII', 'CLXXIX', 'CLXXX', 'CLXXXI', 'CLXXXII', 'CLXXXIII', 'CLXXXIV', 'CLXXXV', 'CLXXXVI', 'CLXXXVII', 'CLXXXVIII', 'CLXXXIX', 'CXC', 'CXCI', 'CXCII', 'CXCIII', 'CXCIV', 'CVC', 'CVCI', 'CVCII', 'CVCIII', 'CIC', 'CC', 'CCI', 'CCII', 'CCIII', 'CCIV', 'CCV', 'CCVI', 'CCVII', 'CCVIII', 'CCIX', 'CCX', 'CCXI', 'CCXII', 'CCXIII', 'CCXIV', 'CCXV', 'CCXVI', 'CCXVII', 'CCXVIII', 'CCXIX', 'CCXX', 'CCXXI', 'CCXXII', 'CCXXIII', 'CCXXIV', 'CCXXV', 'CCXXVI', 'CCXXVII', 'CCXXVIII', 'CCXXIX', 'CCXXX', 'CCXXXI', 'CCXXXII', 'CCXXXIII', 'CCXXXIV', 'CCXXXV', 'CCXXXVI', 'CCXXXVII', 'CCXXXVIII', 'CCXXXIX', 'CCXL', 'CCXLI', 'CCXLII', 'CCXLIII', 'CCXLIV', 'CCVL', 'CCVLI', 'CCVLII', 'CCVLIII', 'CCIL', 'CCL', 'CCLI', 'CCLII', 'CCLIII', 'CCLIV', 'CCLV', 'CCLVI', 'CCLVII', 'CCLVIII', 'CCLIX', 'CCLX', 'CCLXI', 'CCLXII', 'CCLXIII', 'CCLXIV', 'CCLXV', 'CCLXVI', 'CCLXVII', 'CCLXVIII', 'CCLXIX', 'CCLXX', 'CCLXXI', 'CCLXXII', 'CCLXXIII', 'CCLXXIV', 'CCLXXV', 'CCLXXVI', 'CCLXXVII', 'CCLXXVIII', 'CCLXXIX', 'CCLXXX', 'CCLXXXI', 'CCLXXXII', 'CCLXXXIII', 'CCLXXXIV', 'CCLXXXV', 'CCLXXXVI', 'CCLXXXVII', 'CCLXXXVIII', 'CCLXXXIX', 'CCXC', 'CCXCI', 'CCXCII', 'CCXCIII', 'CCXCIV', 'CCVC', 'CCVCI', 'CCVCII', 'CCVCIII', 'CCIC', 'CCC', 'CCCI', 'CCCII', 'CCCIII', 'CCCIV', 'CCCV', 'CCCVI', 'CCCVII', 'CCCVIII', 'CCCIX', 'CCCX', 'CCCXI', 'CCCXII', 'CCCXIII', 'CCCXIV', 'CCCXV', 'CCCXVI', 'CCCXVII', 'CCCXVIII', 'CCCXIX', 'CCCXX', 'CCCXXI', 'CCCXXII', 'CCCXXIII', 'CCCXXIV', 'CCCXXV', 'CCCXXVI', 'CCCXXVII', 'CCCXXVIII', 'CCCXXIX', 'CCCXXX', 'CCCXXXI', 'CCCXXXII', 'CCCXXXIII', 'CCCXXXIV', 'CCCXXXV', 'CCCXXXVI', 'CCCXXXVII', 'CCCXXXVIII', 'CCCXXXIX', 'CCCXL', 'CCCXLI', 'CCCXLII', 'CCCXLIII', 'CCCXLIV', 'CCCVL', 'CCCVLI', 'CCCVLII', 'CCCVLIII', 'CCCIL', 'CCCL', 'CCCLI', 'CCCLII', 'CCCLIII', 'CCCLIV', 'CCCLV', 'CCCLVI', 'CCCLVII', 'CCCLVIII', 'CCCLIX', 'CCCLX', 'CCCLXI', 'CCCLXII', 'CCCLXIII', 'CCCLXIV', 'CCCLXV', 'CCCLXVI', 'CCCLXVII', 'CCCLXVIII', 'CCCLXIX', 'CCCLXX', 'CCCLXXI', 'CCCLXXII', 'CCCLXXIII', 'CCCLXXIV', 'CCCLXXV', 'CCCLXXVI', 'CCCLXXVII', 'CCCLXXVIII', 'CCCLXXIX', 'CCCLXXX', 'CCCLXXXI', 'CCCLXXXII', 'CCCLXXXIII', 'CCCLXXXIV', 'CCCLXXXV', 'CCCLXXXVI', 'CCCLXXXVII', 'CCCLXXXVIII', 'CCCLXXXIX', 'CCCXC', 'CCCXCI', 'CCCXCII', 'CCCXCIII', 'CCCXCIV', 'CCCVC', 'CCCVCI', 'CCCVCII', 'CCCVCIII', 'CCCIC', 'CD', 'CDI', 'CDII', 'CDIII', 'CDIV', 'CDV', 'CDVI', 'CDVII', 'CDVIII', 'CDIX', 'CDX', 'CDXI', 'CDXII', 'CDXIII', 'CDXIV', 'CDXV', 'CDXVI', 'CDXVII', 'CDXVIII', 'CDXIX', 'CDXX', 'CDXXI', 'CDXXII', 'CDXXIII', 'CDXXIV', 'CDXXV', 'CDXXVI', 'CDXXVII', 'CDXXVIII', 'CDXXIX', 'CDXXX', 'CDXXXI', 'CDXXXII', 'CDXXXIII', 'CDXXXIV', 'CDXXXV', 'CDXXXVI', 'CDXXXVII', 'CDXXXVIII', 'CDXXXIX', 'CDXL', 'CDXLI', 'CDXLII', 'CDXLIII', 'CDXLIV', 'CDVL', 'CDVLI', 'CDVLII', 'CDVLIII', 'CDIL', 'LD', 'LDI', 'LDII', 'LDIII', 'LDIV', 'LDV', 'LDVI', 'LDVII', 'LDVIII', 'LDIX', 'LDX', 'LDXI', 'LDXII', 'LDXIII', 'LDXIV', 'LDXV', 'LDXVI', 'LDXVII', 'LDXVIII', 'LDXIX', 'LDXX', 'LDXXI', 'LDXXII', 'LDXXIII', 'LDXXIV', 'LDXXV', 'LDXXVI', 'LDXXVII', 'LDXXVIII', 'LDXXIX', 'LDXXX', 'LDXXXI', 'LDXXXII', 'LDXXXIII', 'LDXXXIV', 'LDXXXV', 'LDXXXVI', 'LDXXXVII', 'LDXXXVIII', 'LDXXXIX', 'XD', 'XDI', 'XDII', 'XDIII', 'XDIV', 'VD', 'VDI', 'VDII', 'VDIII', 'ID', 'D', 'DI', 'DII', 'DIII', 'DIV', 'DV', 'DVI', 'DVII', 'DVIII', 'DIX', 'DX', 'DXI', 'DXII', 'DXIII', 'DXIV', 'DXV', 'DXVI', 'DXVII', 'DXVIII', 'DXIX', 'DXX', 'DXXI', 'DXXII', 'DXXIII', 'DXXIV', 'DXXV', 'DXXVI', 'DXXVII', 'DXXVIII', 'DXXIX', 'DXXX', 'DXXXI', 'DXXXII', 'DXXXIII', 'DXXXIV', 'DXXXV', 'DXXXVI', 'DXXXVII', 'DXXXVIII', 'DXXXIX', 'DXL', 'DXLI', 'DXLII', 'DXLIII', 'DXLIV', 'DVL', 'DVLI', 'DVLII', 'DVLIII', 'DIL', 'DL', 'DLI', 'DLII', 'DLIII', 'DLIV', 'DLV', 'DLVI', 'DLVII', 'DLVIII', 'DLIX', 'DLX', 'DLXI', 'DLXII', 'DLXIII', 'DLXIV', 'DLXV', 'DLXVI', 'DLXVII', 'DLXVIII', 'DLXIX', 'DLXX', 'DLXXI', 'DLXXII', 'DLXXIII', 'DLXXIV', 'DLXXV', 'DLXXVI', 'DLXXVII', 'DLXXVIII', 'DLXXIX', 'DLXXX', 'DLXXXI', 'DLXXXII', 'DLXXXIII', 'DLXXXIV', 'DLXXXV', 'DLXXXVI', 'DLXXXVII', 'DLXXXVIII', 'DLXXXIX', 'DXC', 'DXCI', 'DXCII', 'DXCIII', 'DXCIV', 'DVC', 'DVCI', 'DVCII', 'DVCIII', 'DIC', 'DC', 'DCI', 'DCII', 'DCIII', 'DCIV', 'DCV', 'DCVI', 'DCVII', 'DCVIII', 'DCIX', 'DCX', 'DCXI', 'DCXII', 'DCXIII', 'DCXIV', 'DCXV', 'DCXVI', 'DCXVII', 'DCXVIII', 'DCXIX', 'DCXX', 'DCXXI', 'DCXXII', 'DCXXIII', 'DCXXIV', 'DCXXV', 'DCXXVI', 'DCXXVII', 'DCXXVIII', 'DCXXIX', 'DCXXX', 'DCXXXI', 'DCXXXII', 'DCXXXIII', 'DCXXXIV', 'DCXXXV', 'DCXXXVI', 'DCXXXVII', 'DCXXXVIII', 'DCXXXIX', 'DCXL', 'DCXLI', 'DCXLII', 'DCXLIII', 'DCXLIV', 'DCVL', 'DCVLI', 'DCVLII', 'DCVLIII', 'DCIL', 'DCL', 'DCLI', 'DCLII', 'DCLIII', 'DCLIV', 'DCLV', 'DCLVI', 'DCLVII', 'DCLVIII', 'DCLIX', 'DCLX', 'DCLXI', 'DCLXII', 'DCLXIII', 'DCLXIV', 'DCLXV', 'DCLXVI', 'DCLXVII', 'DCLXVIII', 'DCLXIX', 'DCLXX', 'DCLXXI', 'DCLXXII', 'DCLXXIII', 'DCLXXIV', 'DCLXXV', 'DCLXXVI', 'DCLXXVII', 'DCLXXVIII', 'DCLXXIX', 'DCLXXX', 'DCLXXXI', 'DCLXXXII', 'DCLXXXIII', 'DCLXXXIV', 'DCLXXXV', 'DCLXXXVI', 'DCLXXXVII', 'DCLXXXVIII', 'DCLXXXIX', 'DCXC', 'DCXCI', 'DCXCII', 'DCXCIII', 'DCXCIV', 'DCVC', 'DCVCI', 'DCVCII', 'DCVCIII', 'DCIC', 'DCC', 'DCCI', 'DCCII', 'DCCIII', 'DCCIV', 'DCCV', 'DCCVI', 'DCCVII', 'DCCVIII', 'DCCIX', 'DCCX', 'DCCXI', 'DCCXII', 'DCCXIII', 'DCCXIV', 'DCCXV', 'DCCXVI', 'DCCXVII', 'DCCXVIII', 'DCCXIX', 'DCCXX', 'DCCXXI', 'DCCXXII', 'DCCXXIII', 'DCCXXIV', 'DCCXXV', 'DCCXXVI', 'DCCXXVII', 'DCCXXVIII', 'DCCXXIX', 'DCCXXX', 'DCCXXXI', 'DCCXXXII', 'DCCXXXIII', 'DCCXXXIV', 'DCCXXXV', 'DCCXXXVI', 'DCCXXXVII', 'DCCXXXVIII', 'DCCXXXIX', 'DCCXL', 'DCCXLI', 'DCCXLII', 'DCCXLIII', 'DCCXLIV', 'DCCVL', 'DCCVLI', 'DCCVLII', 'DCCVLIII', 'DCCIL', 'DCCL', 'DCCLI', 'DCCLII', 'DCCLIII', 'DCCLIV', 'DCCLV', 'DCCLVI', 'DCCLVII', 'DCCLVIII', 'DCCLIX', 'DCCLX', 'DCCLXI', 'DCCLXII', 'DCCLXIII', 'DCCLXIV', 'DCCLXV', 'DCCLXVI', 'DCCLXVII', 'DCCLXVIII', 'DCCLXIX', 'DCCLXX', 'DCCLXXI', 'DCCLXXII', 'DCCLXXIII', 'DCCLXXIV', 'DCCLXXV', 'DCCLXXVI', 'DCCLXXVII', 'DCCLXXVIII', 'DCCLXXIX', 'DCCLXXX', 'DCCLXXXI', 'DCCLXXXII', 'DCCLXXXIII', 'DCCLXXXIV', 'DCCLXXXV', 'DCCLXXXVI', 'DCCLXXXVII', 'DCCLXXXVIII', 'DCCLXXXIX', 'DCCXC', 'DCCXCI', 'DCCXCII', 'DCCXCIII', 'DCCXCIV', 'DCCVC', 'DCCVCI', 'DCCVCII', 'DCCVCIII', 'DCCIC', 'DCCC', 'DCCCI', 'DCCCII', 'DCCCIII', 'DCCCIV', 'DCCCV', 'DCCCVI', 'DCCCVII', 'DCCCVIII', 'DCCCIX', 'DCCCX', 'DCCCXI', 'DCCCXII', 'DCCCXIII', 'DCCCXIV', 'DCCCXV', 'DCCCXVI', 'DCCCXVII', 'DCCCXVIII', 'DCCCXIX', 'DCCCXX', 'DCCCXXI', 'DCCCXXII', 'DCCCXXIII', 'DCCCXXIV', 'DCCCXXV', 'DCCCXXVI', 'DCCCXXVII', 'DCCCXXVIII', 'DCCCXXIX', 'DCCCXXX', 'DCCCXXXI', 'DCCCXXXII', 'DCCCXXXIII', 'DCCCXXXIV', 'DCCCXXXV', 'DCCCXXXVI', 'DCCCXXXVII', 'DCCCXXXVIII', 'DCCCXXXIX', 'DCCCXL', 'DCCCXLI', 'DCCCXLII', 'DCCCXLIII', 'DCCCXLIV', 'DCCCVL', 'DCCCVLI', 'DCCCVLII', 'DCCCVLIII', 'DCCCIL', 'DCCCL', 'DCCCLI', 'DCCCLII', 'DCCCLIII', 'DCCCLIV', 'DCCCLV', 'DCCCLVI', 'DCCCLVII', 'DCCCLVIII', 'DCCCLIX', 'DCCCLX', 'DCCCLXI', 'DCCCLXII', 'DCCCLXIII', 'DCCCLXIV', 'DCCCLXV', 'DCCCLXVI', 'DCCCLXVII', 'DCCCLXVIII', 'DCCCLXIX', 'DCCCLXX', 'DCCCLXXI', 'DCCCLXXII', 'DCCCLXXIII', 'DCCCLXXIV', 'DCCCLXXV', 'DCCCLXXVI', 'DCCCLXXVII', 'DCCCLXXVIII', 'DCCCLXXIX', 'DCCCLXXX', 'DCCCLXXXI', 'DCCCLXXXII', 'DCCCLXXXIII', 'DCCCLXXXIV', 'DCCCLXXXV', 'DCCCLXXXVI', 'DCCCLXXXVII', 'DCCCLXXXVIII', 'DCCCLXXXIX', 'DCCCXC', 'DCCCXCI', 'DCCCXCII', 'DCCCXCIII', 'DCCCXCIV', 'DCCCVC', 'DCCCVCI', 'DCCCVCII', 'DCCCVCIII', 'DCCCIC', 'CM', 'CMI', 'CMII', 'CMIII', 'CMIV', 'CMV', 'CMVI', 'CMVII', 'CMVIII', 'CMIX', 'CMX', 'CMXI', 'CMXII', 'CMXIII', 'CMXIV', 'CMXV', 'CMXVI', 'CMXVII', 'CMXVIII', 'CMXIX', 'CMXX', 'CMXXI', 'CMXXII', 'CMXXIII', 'CMXXIV', 'CMXXV', 'CMXXVI', 'CMXXVII', 'CMXXVIII', 'CMXXIX', 'CMXXX', 'CMXXXI', 'CMXXXII', 'CMXXXIII', 'CMXXXIV', 'CMXXXV', 'CMXXXVI', 'CMXXXVII', 'CMXXXVIII', 'CMXXXIX', 'CMXL', 'CMXLI', 'CMXLII', 'CMXLIII', 'CMXLIV', 'CMVL', 'CMVLI', 'CMVLII', 'CMVLIII', 'CMIL', 'LM', 'LMI', 'LMII', 'LMIII', 'LMIV', 'LMV', 'LMVI', 'LMVII', 'LMVIII', 'LMIX', 'LMX', 'LMXI', 'LMXII', 'LMXIII', 'LMXIV', 'LMXV', 'LMXVI', 'LMXVII', 'LMXVIII', 'LMXIX', 'LMXX', 'LMXXI', 'LMXXII', 'LMXXIII', 'LMXXIV', 'LMXXV', 'LMXXVI', 'LMXXVII', 'LMXXVIII', 'LMXXIX', 'LMXXX', 'LMXXXI', 'LMXXXII', 'LMXXXIII', 'LMXXXIV', 'LMXXXV', 'LMXXXVI', 'LMXXXVII', 'LMXXXVIII', 'LMXXXIX', 'XM', 'XMI', 'XMII', 'XMIII', 'XMIV', 'VM', 'VMI', 'VMII', 'VMIII', 'IM', 'M', 'MI', 'MII', 'MIII', 'MIV', 'MV', 'MVI', 'MVII', 'MVIII', 'MIX', 'MX', 'MXI', 'MXII', 'MXIII', 'MXIV', 'MXV', 'MXVI', 'MXVII', 'MXVIII', 'MXIX', 'MXX', 'MXXI', 'MXXII', 'MXXIII', 'MXXIV', 'MXXV', 'MXXVI', 'MXXVII', 'MXXVIII', 'MXXIX', 'MXXX', 'MXXXI', 'MXXXII', 'MXXXIII', 'MXXXIV', 'MXXXV', 'MXXXVI', 'MXXXVII', 'MXXXVIII', 'MXXXIX', 'MXL', 'MXLI', 'MXLII', 'MXLIII', 'MXLIV', 'MVL', 'MVLI', 'MVLII', 'MVLIII', 'MIL', 'ML', 'MLI', 'MLII', 'MLIII', 'MLIV', 'MLV', 'MLVI', 'MLVII', 'MLVIII', 'MLIX', 'MLX', 'MLXI', 'MLXII', 'MLXIII', 'MLXIV', 'MLXV', 'MLXVI', 'MLXVII', 'MLXVIII', 'MLXIX', 'MLXX', 'MLXXI', 'MLXXII', 'MLXXIII', 'MLXXIV', 'MLXXV', 'MLXXVI', 'MLXXVII', 'MLXXVIII', 'MLXXIX', 'MLXXX', 'MLXXXI', 'MLXXXII', 'MLXXXIII', 'MLXXXIV', 'MLXXXV', 'MLXXXVI', 'MLXXXVII', 'MLXXXVIII', 'MLXXXIX', 'MXC', 'MXCI', 'MXCII', 'MXCIII', 'MXCIV', 'MVC', 'MVCI', 'MVCII', 'MVCIII', 'MIC', 'MC', 'MCI', 'MCII', 'MCIII', 'MCIV', 'MCV', 'MCVI', 'MCVII', 'MCVIII', 'MCIX', 'MCX', 'MCXI', 'MCXII', 'MCXIII', 'MCXIV', 'MCXV', 'MCXVI', 'MCXVII', 'MCXVIII', 'MCXIX', 'MCXX', 'MCXXI', 'MCXXII', 'MCXXIII', 'MCXXIV', 'MCXXV', 'MCXXVI', 'MCXXVII', 'MCXXVIII', 'MCXXIX', 'MCXXX', 'MCXXXI', 'MCXXXII', 'MCXXXIII', 'MCXXXIV', 'MCXXXV', 'MCXXXVI', 'MCXXXVII', 'MCXXXVIII', 'MCXXXIX', 'MCXL', 'MCXLI', 'MCXLII', 'MCXLIII', 'MCXLIV', 'MCVL', 'MCVLI', 'MCVLII', 'MCVLIII', 'MCIL', 'MCL', 'MCLI', 'MCLII', 'MCLIII', 'MCLIV', 'MCLV', 'MCLVI', 'MCLVII', 'MCLVIII', 'MCLIX', 'MCLX', 'MCLXI', 'MCLXII', 'MCLXIII', 'MCLXIV', 'MCLXV', 'MCLXVI', 'MCLXVII', 'MCLXVIII', 'MCLXIX', 'MCLXX', 'MCLXXI', 'MCLXXII', 'MCLXXIII', 'MCLXXIV', 'MCLXXV', 'MCLXXVI', 'MCLXXVII', 'MCLXXVIII', 'MCLXXIX', 'MCLXXX', 'MCLXXXI', 'MCLXXXII', 'MCLXXXIII', 'MCLXXXIV', 'MCLXXXV', 'MCLXXXVI', 'MCLXXXVII', 'MCLXXXVIII', 'MCLXXXIX', 'MCXC', 'MCXCI', 'MCXCII', 'MCXCIII', 'MCXCIV', 'MCVC', 'MCVCI', 'MCVCII', 'MCVCIII', 'MCIC', 'MCC', 'MCCI', 'MCCII', 'MCCIII', 'MCCIV', 'MCCV', 'MCCVI', 'MCCVII', 'MCCVIII', 'MCCIX', 'MCCX', 'MCCXI', 'MCCXII', 'MCCXIII', 'MCCXIV', 'MCCXV', 'MCCXVI', 'MCCXVII', 'MCCXVIII', 'MCCXIX', 'MCCXX', 'MCCXXI', 'MCCXXII', 'MCCXXIII', 'MCCXXIV', 'MCCXXV', 'MCCXXVI', 'MCCXXVII', 'MCCXXVIII', 'MCCXXIX', 'MCCXXX', 'MCCXXXI', 'MCCXXXII', 'MCCXXXIII', 'MCCXXXIV', 'MCCXXXV', 'MCCXXXVI', 'MCCXXXVII', 'MCCXXXVIII', 'MCCXXXIX', 'MCCXL', 'MCCXLI', 'MCCXLII', 'MCCXLIII', 'MCCXLIV', 'MCCVL', 'MCCVLI', 'MCCVLII', 'MCCVLIII', 'MCCIL', 'MCCL', 'MCCLI', 'MCCLII', 'MCCLIII', 'MCCLIV', 'MCCLV', 'MCCLVI', 'MCCLVII', 'MCCLVIII', 'MCCLIX', 'MCCLX', 'MCCLXI', 'MCCLXII', 'MCCLXIII', 'MCCLXIV', 'MCCLXV', 'MCCLXVI', 'MCCLXVII', 'MCCLXVIII', 'MCCLXIX', 'MCCLXX', 'MCCLXXI', 'MCCLXXII', 'MCCLXXIII', 'MCCLXXIV', 'MCCLXXV', 'MCCLXXVI', 'MCCLXXVII', 'MCCLXXVIII', 'MCCLXXIX', 'MCCLXXX', 'MCCLXXXI', 'MCCLXXXII', 'MCCLXXXIII', 'MCCLXXXIV', 'MCCLXXXV', 'MCCLXXXVI', 'MCCLXXXVII', 'MCCLXXXVIII', 'MCCLXXXIX', 'MCCXC', 'MCCXCI', 'MCCXCII', 'MCCXCIII', 'MCCXCIV', 'MCCVC', 'MCCVCI', 'MCCVCII', 'MCCVCIII', 'MCCIC', 'MCCC', 'MCCCI', 'MCCCII', 'MCCCIII', 'MCCCIV', 'MCCCV', 'MCCCVI', 'MCCCVII', 'MCCCVIII', 'MCCCIX', 'MCCCX', 'MCCCXI', 'MCCCXII', 'MCCCXIII', 'MCCCXIV', 'MCCCXV', 'MCCCXVI', 'MCCCXVII', 'MCCCXVIII', 'MCCCXIX', 'MCCCXX', 'MCCCXXI', 'MCCCXXII', 'MCCCXXIII', 'MCCCXXIV', 'MCCCXXV', 'MCCCXXVI', 'MCCCXXVII', 'MCCCXXVIII', 'MCCCXXIX', 'MCCCXXX', 'MCCCXXXI', 'MCCCXXXII', 'MCCCXXXIII', 'MCCCXXXIV', 'MCCCXXXV', 'MCCCXXXVI', 'MCCCXXXVII', 'MCCCXXXVIII', 'MCCCXXXIX', 'MCCCXL', 'MCCCXLI', 'MCCCXLII', 'MCCCXLIII', 'MCCCXLIV', 'MCCCVL', 'MCCCVLI', 'MCCCVLII', 'MCCCVLIII', 'MCCCIL', 'MCCCL', 'MCCCLI', 'MCCCLII', 'MCCCLIII', 'MCCCLIV', 'MCCCLV', 'MCCCLVI', 'MCCCLVII', 'MCCCLVIII', 'MCCCLIX', 'MCCCLX', 'MCCCLXI', 'MCCCLXII', 'MCCCLXIII', 'MCCCLXIV', 'MCCCLXV', 'MCCCLXVI', 'MCCCLXVII', 'MCCCLXVIII', 'MCCCLXIX', 'MCCCLXX', 'MCCCLXXI', 'MCCCLXXII', 'MCCCLXXIII', 'MCCCLXXIV', 'MCCCLXXV', 'MCCCLXXVI', 'MCCCLXXVII', 'MCCCLXXVIII', 'MCCCLXXIX', 'MCCCLXXX', 'MCCCLXXXI', 'MCCCLXXXII', 'MCCCLXXXIII', 'MCCCLXXXIV', 'MCCCLXXXV', 'MCCCLXXXVI', 'MCCCLXXXVII', 'MCCCLXXXVIII', 'MCCCLXXXIX', 'MCCCXC', 'MCCCXCI', 'MCCCXCII', 'MCCCXCIII', 'MCCCXCIV', 'MCCCVC', 'MCCCVCI', 'MCCCVCII', 'MCCCVCIII', 'MCCCIC', 'MCD', 'MCDI', 'MCDII', 'MCDIII', 'MCDIV', 'MCDV', 'MCDVI', 'MCDVII', 'MCDVIII', 'MCDIX', 'MCDX', 'MCDXI', 'MCDXII', 'MCDXIII', 'MCDXIV', 'MCDXV', 'MCDXVI', 'MCDXVII', 'MCDXVIII', 'MCDXIX', 'MCDXX', 'MCDXXI', 'MCDXXII', 'MCDXXIII', 'MCDXXIV', 'MCDXXV', 'MCDXXVI', 'MCDXXVII', 'MCDXXVIII', 'MCDXXIX', 'MCDXXX', 'MCDXXXI', 'MCDXXXII', 'MCDXXXIII', 'MCDXXXIV', 'MCDXXXV', 'MCDXXXVI', 'MCDXXXVII', 'MCDXXXVIII', 'MCDXXXIX', 'MCDXL', 'MCDXLI', 'MCDXLII', 'MCDXLIII', 'MCDXLIV', 'MCDVL', 'MCDVLI', 'MCDVLII', 'MCDVLIII', 'MCDIL', 'MLD', 'MLDI', 'MLDII', 'MLDIII', 'MLDIV', 'MLDV', 'MLDVI', 'MLDVII', 'MLDVIII', 'MLDIX', 'MLDX', 'MLDXI', 'MLDXII', 'MLDXIII', 'MLDXIV', 'MLDXV', 'MLDXVI', 'MLDXVII', 'MLDXVIII', 'MLDXIX', 'MLDXX', 'MLDXXI', 'MLDXXII', 'MLDXXIII', 'MLDXXIV', 'MLDXXV', 'MLDXXVI', 'MLDXXVII', 'MLDXXVIII', 'MLDXXIX', 'MLDXXX', 'MLDXXXI', 'MLDXXXII', 'MLDXXXIII', 'MLDXXXIV', 'MLDXXXV', 'MLDXXXVI', 'MLDXXXVII', 'MLDXXXVIII', 'MLDXXXIX', 'MXD', 'MXDI', 'MXDII', 'MXDIII', 'MXDIV', 'MVD', 'MVDI', 'MVDII', 'MVDIII', 'MID', 'MD', 'MDI', 'MDII', 'MDIII', 'MDIV', 'MDV', 'MDVI', 'MDVII', 'MDVIII', 'MDIX', 'MDX', 'MDXI', 'MDXII', 'MDXIII', 'MDXIV', 'MDXV', 'MDXVI', 'MDXVII', 'MDXVIII', 'MDXIX', 'MDXX', 'MDXXI', 'MDXXII', 'MDXXIII', 'MDXXIV', 'MDXXV', 'MDXXVI', 'MDXXVII', 'MDXXVIII', 'MDXXIX', 'MDXXX', 'MDXXXI', 'MDXXXII', 'MDXXXIII', 'MDXXXIV', 'MDXXXV', 'MDXXXVI', 'MDXXXVII', 'MDXXXVIII', 'MDXXXIX', 'MDXL', 'MDXLI', 'MDXLII', 'MDXLIII', 'MDXLIV', 'MDVL', 'MDVLI', 'MDVLII', 'MDVLIII', 'MDIL', 'MDL', 'MDLI', 'MDLII', 'MDLIII', 'MDLIV', 'MDLV', 'MDLVI', 'MDLVII', 'MDLVIII', 'MDLIX', 'MDLX', 'MDLXI', 'MDLXII', 'MDLXIII', 'MDLXIV', 'MDLXV', 'MDLXVI', 'MDLXVII', 'MDLXVIII', 'MDLXIX', 'MDLXX', 'MDLXXI', 'MDLXXII', 'MDLXXIII', 'MDLXXIV', 'MDLXXV', 'MDLXXVI', 'MDLXXVII', 'MDLXXVIII', 'MDLXXIX', 'MDLXXX', 'MDLXXXI', 'MDLXXXII', 'MDLXXXIII', 'MDLXXXIV', 'MDLXXXV', 'MDLXXXVI', 'MDLXXXVII', 'MDLXXXVIII', 'MDLXXXIX', 'MDXC', 'MDXCI', 'MDXCII', 'MDXCIII', 'MDXCIV', 'MDVC', 'MDVCI', 'MDVCII', 'MDVCIII', 'MDIC', 'MDC', 'MDCI', 'MDCII', 'MDCIII', 'MDCIV', 'MDCV', 'MDCVI', 'MDCVII', 'MDCVIII', 'MDCIX', 'MDCX', 'MDCXI', 'MDCXII', 'MDCXIII', 'MDCXIV', 'MDCXV', 'MDCXVI', 'MDCXVII', 'MDCXVIII', 'MDCXIX', 'MDCXX', 'MDCXXI', 'MDCXXII', 'MDCXXIII', 'MDCXXIV', 'MDCXXV', 'MDCXXVI', 'MDCXXVII', 'MDCXXVIII', 'MDCXXIX', 'MDCXXX', 'MDCXXXI', 'MDCXXXII', 'MDCXXXIII', 'MDCXXXIV', 'MDCXXXV', 'MDCXXXVI', 'MDCXXXVII', 'MDCXXXVIII', 'MDCXXXIX', 'MDCXL', 'MDCXLI', 'MDCXLII', 'MDCXLIII', 'MDCXLIV', 'MDCVL', 'MDCVLI', 'MDCVLII', 'MDCVLIII', 'MDCIL', 'MDCL', 'MDCLI', 'MDCLII', 'MDCLIII', 'MDCLIV', 'MDCLV', 'MDCLVI', 'MDCLVII', 'MDCLVIII', 'MDCLIX', 'MDCLX', 'MDCLXI', 'MDCLXII', 'MDCLXIII', 'MDCLXIV', 'MDCLXV', 'MDCLXVI', 'MDCLXVII', 'MDCLXVIII', 'MDCLXIX', 'MDCLXX', 'MDCLXXI', 'MDCLXXII', 'MDCLXXIII', 'MDCLXXIV', 'MDCLXXV', 'MDCLXXVI', 'MDCLXXVII', 'MDCLXXVIII', 'MDCLXXIX', 'MDCLXXX', 'MDCLXXXI', 'MDCLXXXII', 'MDCLXXXIII', 'MDCLXXXIV', 'MDCLXXXV', 'MDCLXXXVI', 'MDCLXXXVII', 'MDCLXXXVIII', 'MDCLXXXIX', 'MDCXC', 'MDCXCI', 'MDCXCII', 'MDCXCIII', 'MDCXCIV', 'MDCVC', 'MDCVCI', 'MDCVCII', 'MDCVCIII', 'MDCIC', 'MDCC', 'MDCCI', 'MDCCII', 'MDCCIII', 'MDCCIV', 'MDCCV', 'MDCCVI', 'MDCCVII', 'MDCCVIII', 'MDCCIX', 'MDCCX', 'MDCCXI', 'MDCCXII', 'MDCCXIII', 'MDCCXIV', 'MDCCXV', 'MDCCXVI', 'MDCCXVII', 'MDCCXVIII', 'MDCCXIX', 'MDCCXX', 'MDCCXXI', 'MDCCXXII', 'MDCCXXIII', 'MDCCXXIV', 'MDCCXXV', 'MDCCXXVI', 'MDCCXXVII', 'MDCCXXVIII', 'MDCCXXIX', 'MDCCXXX', 'MDCCXXXI', 'MDCCXXXII', 'MDCCXXXIII', 'MDCCXXXIV', 'MDCCXXXV', 'MDCCXXXVI', 'MDCCXXXVII', 'MDCCXXXVIII', 'MDCCXXXIX', 'MDCCXL', 'MDCCXLI', 'MDCCXLII', 'MDCCXLIII', 'MDCCXLIV', 'MDCCVL', 'MDCCVLI', 'MDCCVLII', 'MDCCVLIII', 'MDCCIL', 'MDCCL', 'MDCCLI', 'MDCCLII', 'MDCCLIII', 'MDCCLIV', 'MDCCLV', 'MDCCLVI', 'MDCCLVII', 'MDCCLVIII', 'MDCCLIX', 'MDCCLX', 'MDCCLXI', 'MDCCLXII', 'MDCCLXIII', 'MDCCLXIV', 'MDCCLXV', 'MDCCLXVI', 'MDCCLXVII', 'MDCCLXVIII', 'MDCCLXIX', 'MDCCLXX', 'MDCCLXXI', 'MDCCLXXII', 'MDCCLXXIII', 'MDCCLXXIV', 'MDCCLXXV', 'MDCCLXXVI', 'MDCCLXXVII', 'MDCCLXXVIII', 'MDCCLXXIX', 'MDCCLXXX', 'MDCCLXXXI', 'MDCCLXXXII', 'MDCCLXXXIII', 'MDCCLXXXIV', 'MDCCLXXXV', 'MDCCLXXXVI', 'MDCCLXXXVII', 'MDCCLXXXVIII', 'MDCCLXXXIX', 'MDCCXC', 'MDCCXCI', 'MDCCXCII', 'MDCCXCIII', 'MDCCXCIV', 'MDCCVC', 'MDCCVCI', 'MDCCVCII', 'MDCCVCIII', 'MDCCIC', 'MDCCC', 'MDCCCI', 'MDCCCII', 'MDCCCIII', 'MDCCCIV', 'MDCCCV', 'MDCCCVI', 'MDCCCVII', 'MDCCCVIII', 'MDCCCIX', 'MDCCCX', 'MDCCCXI', 'MDCCCXII', 'MDCCCXIII', 'MDCCCXIV', 'MDCCCXV', 'MDCCCXVI', 'MDCCCXVII', 'MDCCCXVIII', 'MDCCCXIX', 'MDCCCXX', 'MDCCCXXI', 'MDCCCXXII', 'MDCCCXXIII', 'MDCCCXXIV', 'MDCCCXXV', 'MDCCCXXVI', 'MDCCCXXVII', 'MDCCCXXVIII', 'MDCCCXXIX', 'MDCCCXXX', 'MDCCCXXXI', 'MDCCCXXXII', 'MDCCCXXXIII', 'MDCCCXXXIV', 'MDCCCXXXV', 'MDCCCXXXVI', 'MDCCCXXXVII', 'MDCCCXXXVIII', 'MDCCCXXXIX', 'MDCCCXL', 'MDCCCXLI', 'MDCCCXLII', 'MDCCCXLIII', 'MDCCCXLIV', 'MDCCCVL', 'MDCCCVLI', 'MDCCCVLII', 'MDCCCVLIII', 'MDCCCIL', 'MDCCCL', 'MDCCCLI', 'MDCCCLII', 'MDCCCLIII', 'MDCCCLIV', 'MDCCCLV', 'MDCCCLVI', 'MDCCCLVII', 'MDCCCLVIII', 'MDCCCLIX', 'MDCCCLX', 'MDCCCLXI', 'MDCCCLXII', 'MDCCCLXIII', 'MDCCCLXIV', 'MDCCCLXV', 'MDCCCLXVI', 'MDCCCLXVII', 'MDCCCLXVIII', 'MDCCCLXIX', 'MDCCCLXX', 'MDCCCLXXI', 'MDCCCLXXII', 'MDCCCLXXIII', 'MDCCCLXXIV', 'MDCCCLXXV', 'MDCCCLXXVI', 'MDCCCLXXVII', 'MDCCCLXXVIII', 'MDCCCLXXIX', 'MDCCCLXXX', 'MDCCCLXXXI', 'MDCCCLXXXII', 'MDCCCLXXXIII', 'MDCCCLXXXIV', 'MDCCCLXXXV', 'MDCCCLXXXVI', 'MDCCCLXXXVII', 'MDCCCLXXXVIII', 'MDCCCLXXXIX', 'MDCCCXC', 'MDCCCXCI', 'MDCCCXCII', 'MDCCCXCIII', 'MDCCCXCIV', 'MDCCCVC', 'MDCCCVCI', 'MDCCCVCII', 'MDCCCVCIII', 'MDCCCIC', 'MCM', 'MCMI', 'MCMII', 'MCMIII', 'MCMIV', 'MCMV', 'MCMVI', 'MCMVII', 'MCMVIII', 'MCMIX', 'MCMX', 'MCMXI', 'MCMXII', 'MCMXIII', 'MCMXIV', 'MCMXV', 'MCMXVI', 'MCMXVII', 'MCMXVIII', 'MCMXIX', 'MCMXX', 'MCMXXI', 'MCMXXII', 'MCMXXIII', 'MCMXXIV', 'MCMXXV', 'MCMXXVI', 'MCMXXVII', 'MCMXXVIII', 'MCMXXIX', 'MCMXXX', 'MCMXXXI', 'MCMXXXII', 'MCMXXXIII', 'MCMXXXIV', 'MCMXXXV', 'MCMXXXVI', 'MCMXXXVII', 'MCMXXXVIII', 'MCMXXXIX', 'MCMXL', 'MCMXLI', 'MCMXLII', 'MCMXLIII', 'MCMXLIV', 'MCMVL', 'MCMVLI', 'MCMVLII', 'MCMVLIII', 'MCMIL', 'MLM', 'MLMI', 'MLMII', 'MLMIII', 'MLMIV', 'MLMV', 'MLMVI', 'MLMVII', 'MLMVIII', 'MLMIX', 'MLMX', 'MLMXI', 'MLMXII', 'MLMXIII', 'MLMXIV', 'MLMXV', 'MLMXVI', 'MLMXVII', 'MLMXVIII', 'MLMXIX', 'MLMXX', 'MLMXXI', 'MLMXXII', 'MLMXXIII', 'MLMXXIV', 'MLMXXV', 'MLMXXVI', 'MLMXXVII', 'MLMXXVIII', 'MLMXXIX', 'MLMXXX', 'MLMXXXI', 'MLMXXXII', 'MLMXXXIII', 'MLMXXXIV', 'MLMXXXV', 'MLMXXXVI', 'MLMXXXVII', 'MLMXXXVIII', 'MLMXXXIX', 'MXM', 'MXMI', 'MXMII', 'MXMIII', 'MXMIV', 'MVM', 'MVMI', 'MVMII', 'MVMIII', 'MIM', 'MM', 'MMI', 'MMII', 'MMIII', 'MMIV', 'MMV', 'MMVI', 'MMVII', 'MMVIII', 'MMIX', 'MMX', 'MMXI', 'MMXII', 'MMXIII', 'MMXIV', 'MMXV', 'MMXVI', 'MMXVII', 'MMXVIII', 'MMXIX', 'MMXX', 'MMXXI', 'MMXXII', 'MMXXIII', 'MMXXIV', 'MMXXV', 'MMXXVI', 'MMXXVII', 'MMXXVIII', 'MMXXIX', 'MMXXX', 'MMXXXI', 'MMXXXII', 'MMXXXIII', 'MMXXXIV', 'MMXXXV', 'MMXXXVI', 'MMXXXVII', 'MMXXXVIII', 'MMXXXIX', 'MMXL', 'MMXLI', 'MMXLII', 'MMXLIII', 'MMXLIV', 'MMVL', 'MMVLI', 'MMVLII', 'MMVLIII', 'MMIL', 'MML', 'MMLI', 'MMLII', 'MMLIII', 'MMLIV', 'MMLV', 'MMLVI', 'MMLVII', 'MMLVIII', 'MMLIX', 'MMLX', 'MMLXI', 'MMLXII', 'MMLXIII', 'MMLXIV', 'MMLXV', 'MMLXVI', 'MMLXVII', 'MMLXVIII', 'MMLXIX', 'MMLXX', 'MMLXXI', 'MMLXXII', 'MMLXXIII', 'MMLXXIV', 'MMLXXV', 'MMLXXVI', 'MMLXXVII', 'MMLXXVIII', 'MMLXXIX', 'MMLXXX', 'MMLXXXI', 'MMLXXXII', 'MMLXXXIII', 'MMLXXXIV', 'MMLXXXV', 'MMLXXXVI', 'MMLXXXVII', 'MMLXXXVIII', 'MMLXXXIX', 'MMXC', 'MMXCI', 'MMXCII', 'MMXCIII', 'MMXCIV', 'MMVC', 'MMVCI', 'MMVCII', 'MMVCIII', 'MMIC', 'MMC', 'MMCI', 'MMCII', 'MMCIII', 'MMCIV', 'MMCV', 'MMCVI', 'MMCVII', 'MMCVIII', 'MMCIX', 'MMCX', 'MMCXI', 'MMCXII', 'MMCXIII', 'MMCXIV', 'MMCXV', 'MMCXVI', 'MMCXVII', 'MMCXVIII', 'MMCXIX', 'MMCXX', 'MMCXXI', 'MMCXXII', 'MMCXXIII', 'MMCXXIV', 'MMCXXV', 'MMCXXVI', 'MMCXXVII', 'MMCXXVIII', 'MMCXXIX', 'MMCXXX', 'MMCXXXI', 'MMCXXXII', 'MMCXXXIII', 'MMCXXXIV', 'MMCXXXV', 'MMCXXXVI', 'MMCXXXVII', 'MMCXXXVIII', 'MMCXXXIX', 'MMCXL', 'MMCXLI', 'MMCXLII', 'MMCXLIII', 'MMCXLIV', 'MMCVL', 'MMCVLI', 'MMCVLII', 'MMCVLIII', 'MMCIL', 'MMCL', 'MMCLI', 'MMCLII', 'MMCLIII', 'MMCLIV', 'MMCLV', 'MMCLVI', 'MMCLVII', 'MMCLVIII', 'MMCLIX', 'MMCLX', 'MMCLXI', 'MMCLXII', 'MMCLXIII', 'MMCLXIV', 'MMCLXV', 'MMCLXVI', 'MMCLXVII', 'MMCLXVIII', 'MMCLXIX', 'MMCLXX', 'MMCLXXI', 'MMCLXXII', 'MMCLXXIII', 'MMCLXXIV', 'MMCLXXV', 'MMCLXXVI', 'MMCLXXVII', 'MMCLXXVIII', 'MMCLXXIX', 'MMCLXXX', 'MMCLXXXI', 'MMCLXXXII', 'MMCLXXXIII', 'MMCLXXXIV', 'MMCLXXXV', 'MMCLXXXVI', 'MMCLXXXVII', 'MMCLXXXVIII', 'MMCLXXXIX', 'MMCXC', 'MMCXCI', 'MMCXCII', 'MMCXCIII', 'MMCXCIV', 'MMCVC', 'MMCVCI', 'MMCVCII', 'MMCVCIII', 'MMCIC', 'MMCC', 'MMCCI', 'MMCCII', 'MMCCIII', 'MMCCIV', 'MMCCV', 'MMCCVI', 'MMCCVII', 'MMCCVIII', 'MMCCIX', 'MMCCX', 'MMCCXI', 'MMCCXII', 'MMCCXIII', 'MMCCXIV', 'MMCCXV', 'MMCCXVI', 'MMCCXVII', 'MMCCXVIII', 'MMCCXIX', 'MMCCXX', 'MMCCXXI', 'MMCCXXII', 'MMCCXXIII', 'MMCCXXIV', 'MMCCXXV', 'MMCCXXVI', 'MMCCXXVII', 'MMCCXXVIII', 'MMCCXXIX', 'MMCCXXX', 'MMCCXXXI', 'MMCCXXXII', 'MMCCXXXIII', 'MMCCXXXIV', 'MMCCXXXV', 'MMCCXXXVI', 'MMCCXXXVII', 'MMCCXXXVIII', 'MMCCXXXIX', 'MMCCXL', 'MMCCXLI', 'MMCCXLII', 'MMCCXLIII', 'MMCCXLIV', 'MMCCVL', 'MMCCVLI', 'MMCCVLII', 'MMCCVLIII', 'MMCCIL', 'MMCCL', 'MMCCLI', 'MMCCLII', 'MMCCLIII', 'MMCCLIV', 'MMCCLV', 'MMCCLVI', 'MMCCLVII', 'MMCCLVIII', 'MMCCLIX', 'MMCCLX', 'MMCCLXI', 'MMCCLXII', 'MMCCLXIII', 'MMCCLXIV', 'MMCCLXV', 'MMCCLXVI', 'MMCCLXVII', 'MMCCLXVIII', 'MMCCLXIX', 'MMCCLXX', 'MMCCLXXI', 'MMCCLXXII', 'MMCCLXXIII', 'MMCCLXXIV', 'MMCCLXXV', 'MMCCLXXVI', 'MMCCLXXVII', 'MMCCLXXVIII', 'MMCCLXXIX', 'MMCCLXXX', 'MMCCLXXXI', 'MMCCLXXXII', 'MMCCLXXXIII', 'MMCCLXXXIV', 'MMCCLXXXV', 'MMCCLXXXVI', 'MMCCLXXXVII', 'MMCCLXXXVIII', 'MMCCLXXXIX', 'MMCCXC', 'MMCCXCI', 'MMCCXCII', 'MMCCXCIII', 'MMCCXCIV', 'MMCCVC', 'MMCCVCI', 'MMCCVCII', 'MMCCVCIII', 'MMCCIC', 'MMCCC', 'MMCCCI', 'MMCCCII', 'MMCCCIII', 'MMCCCIV', 'MMCCCV', 'MMCCCVI', 'MMCCCVII', 'MMCCCVIII', 'MMCCCIX', 'MMCCCX', 'MMCCCXI', 'MMCCCXII', 'MMCCCXIII', 'MMCCCXIV', 'MMCCCXV', 'MMCCCXVI', 'MMCCCXVII', 'MMCCCXVIII', 'MMCCCXIX', 'MMCCCXX', 'MMCCCXXI', 'MMCCCXXII', 'MMCCCXXIII', 'MMCCCXXIV', 'MMCCCXXV', 'MMCCCXXVI', 'MMCCCXXVII', 'MMCCCXXVIII', 'MMCCCXXIX', 'MMCCCXXX', 'MMCCCXXXI', 'MMCCCXXXII', 'MMCCCXXXIII', 'MMCCCXXXIV', 'MMCCCXXXV', 'MMCCCXXXVI', 'MMCCCXXXVII', 'MMCCCXXXVIII', 'MMCCCXXXIX', 'MMCCCXL', 'MMCCCXLI', 'MMCCCXLII', 'MMCCCXLIII', 'MMCCCXLIV', 'MMCCCVL', 'MMCCCVLI', 'MMCCCVLII', 'MMCCCVLIII', 'MMCCCIL', 'MMCCCL', 'MMCCCLI', 'MMCCCLII', 'MMCCCLIII', 'MMCCCLIV', 'MMCCCLV', 'MMCCCLVI', 'MMCCCLVII', 'MMCCCLVIII', 'MMCCCLIX', 'MMCCCLX', 'MMCCCLXI', 'MMCCCLXII', 'MMCCCLXIII', 'MMCCCLXIV', 'MMCCCLXV', 'MMCCCLXVI', 'MMCCCLXVII', 'MMCCCLXVIII', 'MMCCCLXIX', 'MMCCCLXX', 'MMCCCLXXI', 'MMCCCLXXII', 'MMCCCLXXIII', 'MMCCCLXXIV', 'MMCCCLXXV', 'MMCCCLXXVI', 'MMCCCLXXVII', 'MMCCCLXXVIII', 'MMCCCLXXIX', 'MMCCCLXXX', 'MMCCCLXXXI', 'MMCCCLXXXII', 'MMCCCLXXXIII', 'MMCCCLXXXIV', 'MMCCCLXXXV', 'MMCCCLXXXVI', 'MMCCCLXXXVII', 'MMCCCLXXXVIII', 'MMCCCLXXXIX', 'MMCCCXC', 'MMCCCXCI', 'MMCCCXCII', 'MMCCCXCIII', 'MMCCCXCIV', 'MMCCCVC', 'MMCCCVCI', 'MMCCCVCII', 'MMCCCVCIII', 'MMCCCIC', 'MMCD', 'MMCDI', 'MMCDII', 'MMCDIII', 'MMCDIV', 'MMCDV', 'MMCDVI', 'MMCDVII', 'MMCDVIII', 'MMCDIX', 'MMCDX', 'MMCDXI', 'MMCDXII', 'MMCDXIII', 'MMCDXIV', 'MMCDXV', 'MMCDXVI', 'MMCDXVII', 'MMCDXVIII', 'MMCDXIX', 'MMCDXX', 'MMCDXXI', 'MMCDXXII', 'MMCDXXIII', 'MMCDXXIV', 'MMCDXXV', 'MMCDXXVI', 'MMCDXXVII', 'MMCDXXVIII', 'MMCDXXIX', 'MMCDXXX', 'MMCDXXXI', 'MMCDXXXII', 'MMCDXXXIII', 'MMCDXXXIV', 'MMCDXXXV', 'MMCDXXXVI', 'MMCDXXXVII', 'MMCDXXXVIII', 'MMCDXXXIX', 'MMCDXL', 'MMCDXLI', 'MMCDXLII', 'MMCDXLIII', 'MMCDXLIV', 'MMCDVL', 'MMCDVLI', 'MMCDVLII', 'MMCDVLIII', 'MMCDIL', 'MMLD', 'MMLDI', 'MMLDII', 'MMLDIII', 'MMLDIV', 'MMLDV', 'MMLDVI', 'MMLDVII', 'MMLDVIII', 'MMLDIX', 'MMLDX', 'MMLDXI', 'MMLDXII', 'MMLDXIII', 'MMLDXIV', 'MMLDXV', 'MMLDXVI', 'MMLDXVII', 'MMLDXVIII', 'MMLDXIX', 'MMLDXX', 'MMLDXXI', 'MMLDXXII', 'MMLDXXIII', 'MMLDXXIV', 'MMLDXXV', 'MMLDXXVI', 'MMLDXXVII', 'MMLDXXVIII', 'MMLDXXIX', 'MMLDXXX', 'MMLDXXXI', 'MMLDXXXII', 'MMLDXXXIII', 'MMLDXXXIV', 'MMLDXXXV', 'MMLDXXXVI', 'MMLDXXXVII', 'MMLDXXXVIII', 'MMLDXXXIX', 'MMXD', 'MMXDI', 'MMXDII', 'MMXDIII', 'MMXDIV', 'MMVD', 'MMVDI', 'MMVDII', 'MMVDIII', 'MMID', 'MMD', 'MMDI', 'MMDII', 'MMDIII', 'MMDIV', 'MMDV', 'MMDVI', 'MMDVII', 'MMDVIII', 'MMDIX', 'MMDX', 'MMDXI', 'MMDXII', 'MMDXIII', 'MMDXIV', 'MMDXV', 'MMDXVI', 'MMDXVII', 'MMDXVIII', 'MMDXIX', 'MMDXX', 'MMDXXI', 'MMDXXII', 'MMDXXIII', 'MMDXXIV', 'MMDXXV', 'MMDXXVI', 'MMDXXVII', 'MMDXXVIII', 'MMDXXIX', 'MMDXXX', 'MMDXXXI', 'MMDXXXII', 'MMDXXXIII', 'MMDXXXIV', 'MMDXXXV', 'MMDXXXVI', 'MMDXXXVII', 'MMDXXXVIII', 'MMDXXXIX', 'MMDXL', 'MMDXLI', 'MMDXLII', 'MMDXLIII', 'MMDXLIV', 'MMDVL', 'MMDVLI', 'MMDVLII', 'MMDVLIII', 'MMDIL', 'MMDL', 'MMDLI', 'MMDLII', 'MMDLIII', 'MMDLIV', 'MMDLV', 'MMDLVI', 'MMDLVII', 'MMDLVIII', 'MMDLIX', 'MMDLX', 'MMDLXI', 'MMDLXII', 'MMDLXIII', 'MMDLXIV', 'MMDLXV', 'MMDLXVI', 'MMDLXVII', 'MMDLXVIII', 'MMDLXIX', 'MMDLXX', 'MMDLXXI', 'MMDLXXII', 'MMDLXXIII', 'MMDLXXIV', 'MMDLXXV', 'MMDLXXVI', 'MMDLXXVII', 'MMDLXXVIII', 'MMDLXXIX', 'MMDLXXX', 'MMDLXXXI', 'MMDLXXXII', 'MMDLXXXIII', 'MMDLXXXIV', 'MMDLXXXV', 'MMDLXXXVI', 'MMDLXXXVII', 'MMDLXXXVIII', 'MMDLXXXIX', 'MMDXC', 'MMDXCI', 'MMDXCII', 'MMDXCIII', 'MMDXCIV', 'MMDVC', 'MMDVCI', 'MMDVCII', 'MMDVCIII', 'MMDIC', 'MMDC', 'MMDCI', 'MMDCII', 'MMDCIII', 'MMDCIV', 'MMDCV', 'MMDCVI', 'MMDCVII', 'MMDCVIII', 'MMDCIX', 'MMDCX', 'MMDCXI', 'MMDCXII', 'MMDCXIII', 'MMDCXIV', 'MMDCXV', 'MMDCXVI', 'MMDCXVII', 'MMDCXVIII', 'MMDCXIX', 'MMDCXX', 'MMDCXXI', 'MMDCXXII', 'MMDCXXIII', 'MMDCXXIV', 'MMDCXXV', 'MMDCXXVI', 'MMDCXXVII', 'MMDCXXVIII', 'MMDCXXIX', 'MMDCXXX', 'MMDCXXXI', 'MMDCXXXII', 'MMDCXXXIII', 'MMDCXXXIV', 'MMDCXXXV', 'MMDCXXXVI', 'MMDCXXXVII', 'MMDCXXXVIII', 'MMDCXXXIX', 'MMDCXL', 'MMDCXLI', 'MMDCXLII', 'MMDCXLIII', 'MMDCXLIV', 'MMDCVL', 'MMDCVLI', 'MMDCVLII', 'MMDCVLIII', 'MMDCIL', 'MMDCL', 'MMDCLI', 'MMDCLII', 'MMDCLIII', 'MMDCLIV', 'MMDCLV', 'MMDCLVI', 'MMDCLVII', 'MMDCLVIII', 'MMDCLIX', 'MMDCLX', 'MMDCLXI', 'MMDCLXII', 'MMDCLXIII', 'MMDCLXIV', 'MMDCLXV', 'MMDCLXVI', 'MMDCLXVII', 'MMDCLXVIII', 'MMDCLXIX', 'MMDCLXX', 'MMDCLXXI', 'MMDCLXXII', 'MMDCLXXIII', 'MMDCLXXIV', 'MMDCLXXV', 'MMDCLXXVI', 'MMDCLXXVII', 'MMDCLXXVIII', 'MMDCLXXIX', 'MMDCLXXX', 'MMDCLXXXI', 'MMDCLXXXII', 'MMDCLXXXIII', 'MMDCLXXXIV', 'MMDCLXXXV', 'MMDCLXXXVI', 'MMDCLXXXVII', 'MMDCLXXXVIII', 'MMDCLXXXIX', 'MMDCXC', 'MMDCXCI', 'MMDCXCII', 'MMDCXCIII', 'MMDCXCIV', 'MMDCVC', 'MMDCVCI', 'MMDCVCII', 'MMDCVCIII', 'MMDCIC', 'MMDCC', 'MMDCCI', 'MMDCCII', 'MMDCCIII', 'MMDCCIV', 'MMDCCV', 'MMDCCVI', 'MMDCCVII', 'MMDCCVIII', 'MMDCCIX', 'MMDCCX', 'MMDCCXI', 'MMDCCXII', 'MMDCCXIII', 'MMDCCXIV', 'MMDCCXV', 'MMDCCXVI', 'MMDCCXVII', 'MMDCCXVIII', 'MMDCCXIX', 'MMDCCXX', 'MMDCCXXI', 'MMDCCXXII', 'MMDCCXXIII', 'MMDCCXXIV', 'MMDCCXXV', 'MMDCCXXVI', 'MMDCCXXVII', 'MMDCCXXVIII', 'MMDCCXXIX', 'MMDCCXXX', 'MMDCCXXXI', 'MMDCCXXXII', 'MMDCCXXXIII', 'MMDCCXXXIV', 'MMDCCXXXV', 'MMDCCXXXVI', 'MMDCCXXXVII', 'MMDCCXXXVIII', 'MMDCCXXXIX', 'MMDCCXL', 'MMDCCXLI', 'MMDCCXLII', 'MMDCCXLIII', 'MMDCCXLIV', 'MMDCCVL', 'MMDCCVLI', 'MMDCCVLII', 'MMDCCVLIII', 'MMDCCIL', 'MMDCCL', 'MMDCCLI', 'MMDCCLII', 'MMDCCLIII', 'MMDCCLIV', 'MMDCCLV', 'MMDCCLVI', 'MMDCCLVII', 'MMDCCLVIII', 'MMDCCLIX', 'MMDCCLX', 'MMDCCLXI', 'MMDCCLXII', 'MMDCCLXIII', 'MMDCCLXIV', 'MMDCCLXV', 'MMDCCLXVI', 'MMDCCLXVII', 'MMDCCLXVIII', 'MMDCCLXIX', 'MMDCCLXX', 'MMDCCLXXI', 'MMDCCLXXII', 'MMDCCLXXIII', 'MMDCCLXXIV', 'MMDCCLXXV', 'MMDCCLXXVI', 'MMDCCLXXVII', 'MMDCCLXXVIII', 'MMDCCLXXIX', 'MMDCCLXXX', 'MMDCCLXXXI', 'MMDCCLXXXII', 'MMDCCLXXXIII', 'MMDCCLXXXIV', 'MMDCCLXXXV', 'MMDCCLXXXVI', 'MMDCCLXXXVII', 'MMDCCLXXXVIII', 'MMDCCLXXXIX', 'MMDCCXC', 'MMDCCXCI', 'MMDCCXCII', 'MMDCCXCIII', 'MMDCCXCIV', 'MMDCCVC', 'MMDCCVCI', 'MMDCCVCII', 'MMDCCVCIII', 'MMDCCIC', 'MMDCCC', 'MMDCCCI', 'MMDCCCII', 'MMDCCCIII', 'MMDCCCIV', 'MMDCCCV', 'MMDCCCVI', 'MMDCCCVII', 'MMDCCCVIII', 'MMDCCCIX', 'MMDCCCX', 'MMDCCCXI', 'MMDCCCXII', 'MMDCCCXIII', 'MMDCCCXIV', 'MMDCCCXV', 'MMDCCCXVI', 'MMDCCCXVII', 'MMDCCCXVIII', 'MMDCCCXIX', 'MMDCCCXX', 'MMDCCCXXI', 'MMDCCCXXII', 'MMDCCCXXIII', 'MMDCCCXXIV', 'MMDCCCXXV', 'MMDCCCXXVI', 'MMDCCCXXVII', 'MMDCCCXXVIII', 'MMDCCCXXIX', 'MMDCCCXXX', 'MMDCCCXXXI', 'MMDCCCXXXII', 'MMDCCCXXXIII', 'MMDCCCXXXIV', 'MMDCCCXXXV', 'MMDCCCXXXVI', 'MMDCCCXXXVII', 'MMDCCCXXXVIII', 'MMDCCCXXXIX', 'MMDCCCXL', 'MMDCCCXLI', 'MMDCCCXLII', 'MMDCCCXLIII', 'MMDCCCXLIV', 'MMDCCCVL', 'MMDCCCVLI', 'MMDCCCVLII', 'MMDCCCVLIII', 'MMDCCCIL', 'MMDCCCL', 'MMDCCCLI', 'MMDCCCLII', 'MMDCCCLIII', 'MMDCCCLIV', 'MMDCCCLV', 'MMDCCCLVI', 'MMDCCCLVII', 'MMDCCCLVIII', 'MMDCCCLIX', 'MMDCCCLX', 'MMDCCCLXI', 'MMDCCCLXII', 'MMDCCCLXIII', 'MMDCCCLXIV', 'MMDCCCLXV', 'MMDCCCLXVI', 'MMDCCCLXVII', 'MMDCCCLXVIII', 'MMDCCCLXIX', 'MMDCCCLXX', 'MMDCCCLXXI', 'MMDCCCLXXII', 'MMDCCCLXXIII', 'MMDCCCLXXIV', 'MMDCCCLXXV', 'MMDCCCLXXVI', 'MMDCCCLXXVII', 'MMDCCCLXXVIII', 'MMDCCCLXXIX', 'MMDCCCLXXX', 'MMDCCCLXXXI', 'MMDCCCLXXXII', 'MMDCCCLXXXIII', 'MMDCCCLXXXIV', 'MMDCCCLXXXV', 'MMDCCCLXXXVI', 'MMDCCCLXXXVII', 'MMDCCCLXXXVIII', 'MMDCCCLXXXIX', 'MMDCCCXC', 'MMDCCCXCI', 'MMDCCCXCII', 'MMDCCCXCIII', 'MMDCCCXCIV', 'MMDCCCVC', 'MMDCCCVCI', 'MMDCCCVCII', 'MMDCCCVCIII', 'MMDCCCIC', 'MMCM', 'MMCMI', 'MMCMII', 'MMCMIII', 'MMCMIV', 'MMCMV', 'MMCMVI', 'MMCMVII', 'MMCMVIII', 'MMCMIX', 'MMCMX', 'MMCMXI', 'MMCMXII', 'MMCMXIII', 'MMCMXIV', 'MMCMXV', 'MMCMXVI', 'MMCMXVII', 'MMCMXVIII', 'MMCMXIX', 'MMCMXX', 'MMCMXXI', 'MMCMXXII', 'MMCMXXIII', 'MMCMXXIV', 'MMCMXXV', 'MMCMXXVI', 'MMCMXXVII', 'MMCMXXVIII', 'MMCMXXIX', 'MMCMXXX', 'MMCMXXXI', 'MMCMXXXII', 'MMCMXXXIII', 'MMCMXXXIV', 'MMCMXXXV', 'MMCMXXXVI', 'MMCMXXXVII', 'MMCMXXXVIII', 'MMCMXXXIX', 'MMCMXL', 'MMCMXLI', 'MMCMXLII', 'MMCMXLIII', 'MMCMXLIV', 'MMCMVL', 'MMCMVLI', 'MMCMVLII', 'MMCMVLIII', 'MMCMIL', 'MMLM', 'MMLMI', 'MMLMII', 'MMLMIII', 'MMLMIV', 'MMLMV', 'MMLMVI', 'MMLMVII', 'MMLMVIII', 'MMLMIX', 'MMLMX', 'MMLMXI', 'MMLMXII', 'MMLMXIII', 'MMLMXIV', 'MMLMXV', 'MMLMXVI', 'MMLMXVII', 'MMLMXVIII', 'MMLMXIX', 'MMLMXX', 'MMLMXXI', 'MMLMXXII', 'MMLMXXIII', 'MMLMXXIV', 'MMLMXXV', 'MMLMXXVI', 'MMLMXXVII', 'MMLMXXVIII', 'MMLMXXIX', 'MMLMXXX', 'MMLMXXXI', 'MMLMXXXII', 'MMLMXXXIII', 'MMLMXXXIV', 'MMLMXXXV', 'MMLMXXXVI', 'MMLMXXXVII', 'MMLMXXXVIII', 'MMLMXXXIX', 'MMXM', 'MMXMI', 'MMXMII', 'MMXMIII', 'MMXMIV', 'MMVM', 'MMVMI', 'MMVMII', 'MMVMIII', 'MMIM', 'MMM', 'MMMI', 'MMMII', 'MMMIII', 'MMMIV', 'MMMV', 'MMMVI', 'MMMVII', 'MMMVIII', 'MMMIX', 'MMMX', 'MMMXI', 'MMMXII', 'MMMXIII', 'MMMXIV', 'MMMXV', 'MMMXVI', 'MMMXVII', 'MMMXVIII', 'MMMXIX', 'MMMXX', 'MMMXXI', 'MMMXXII', 'MMMXXIII', 'MMMXXIV', 'MMMXXV', 'MMMXXVI', 'MMMXXVII', 'MMMXXVIII', 'MMMXXIX', 'MMMXXX', 'MMMXXXI', 'MMMXXXII', 'MMMXXXIII', 'MMMXXXIV', 'MMMXXXV', 'MMMXXXVI', 'MMMXXXVII', 'MMMXXXVIII', 'MMMXXXIX', 'MMMXL', 'MMMXLI', 'MMMXLII', 'MMMXLIII', 'MMMXLIV', 'MMMVL', 'MMMVLI', 'MMMVLII', 'MMMVLIII', 'MMMIL', 'MMML', 'MMMLI', 'MMMLII', 'MMMLIII', 'MMMLIV', 'MMMLV', 'MMMLVI', 'MMMLVII', 'MMMLVIII', 'MMMLIX', 'MMMLX', 'MMMLXI', 'MMMLXII', 'MMMLXIII', 'MMMLXIV', 'MMMLXV', 'MMMLXVI', 'MMMLXVII', 'MMMLXVIII', 'MMMLXIX', 'MMMLXX', 'MMMLXXI', 'MMMLXXII', 'MMMLXXIII', 'MMMLXXIV', 'MMMLXXV', 'MMMLXXVI', 'MMMLXXVII', 'MMMLXXVIII', 'MMMLXXIX', 'MMMLXXX', 'MMMLXXXI', 'MMMLXXXII', 'MMMLXXXIII', 'MMMLXXXIV', 'MMMLXXXV', 'MMMLXXXVI', 'MMMLXXXVII', 'MMMLXXXVIII', 'MMMLXXXIX', 'MMMXC', 'MMMXCI', 'MMMXCII', 'MMMXCIII', 'MMMXCIV', 'MMMVC', 'MMMVCI', 'MMMVCII', 'MMMVCIII', 'MMMIC', 'MMMC', 'MMMCI', 'MMMCII', 'MMMCIII', 'MMMCIV', 'MMMCV', 'MMMCVI', 'MMMCVII', 'MMMCVIII', 'MMMCIX', 'MMMCX', 'MMMCXI', 'MMMCXII', 'MMMCXIII', 'MMMCXIV', 'MMMCXV', 'MMMCXVI', 'MMMCXVII', 'MMMCXVIII', 'MMMCXIX', 'MMMCXX', 'MMMCXXI', 'MMMCXXII', 'MMMCXXIII', 'MMMCXXIV', 'MMMCXXV', 'MMMCXXVI', 'MMMCXXVII', 'MMMCXXVIII', 'MMMCXXIX', 'MMMCXXX', 'MMMCXXXI', 'MMMCXXXII', 'MMMCXXXIII', 'MMMCXXXIV', 'MMMCXXXV', 'MMMCXXXVI', 'MMMCXXXVII', 'MMMCXXXVIII', 'MMMCXXXIX', 'MMMCXL', 'MMMCXLI', 'MMMCXLII', 'MMMCXLIII', 'MMMCXLIV', 'MMMCVL', 'MMMCVLI', 'MMMCVLII', 'MMMCVLIII', 'MMMCIL', 'MMMCL', 'MMMCLI', 'MMMCLII', 'MMMCLIII', 'MMMCLIV', 'MMMCLV', 'MMMCLVI', 'MMMCLVII', 'MMMCLVIII', 'MMMCLIX', 'MMMCLX', 'MMMCLXI', 'MMMCLXII', 'MMMCLXIII', 'MMMCLXIV', 'MMMCLXV', 'MMMCLXVI', 'MMMCLXVII', 'MMMCLXVIII', 'MMMCLXIX', 'MMMCLXX', 'MMMCLXXI', 'MMMCLXXII', 'MMMCLXXIII', 'MMMCLXXIV', 'MMMCLXXV', 'MMMCLXXVI', 'MMMCLXXVII', 'MMMCLXXVIII', 'MMMCLXXIX', 'MMMCLXXX', 'MMMCLXXXI', 'MMMCLXXXII', 'MMMCLXXXIII', 'MMMCLXXXIV', 'MMMCLXXXV', 'MMMCLXXXVI', 'MMMCLXXXVII', 'MMMCLXXXVIII', 'MMMCLXXXIX', 'MMMCXC', 'MMMCXCI', 'MMMCXCII', 'MMMCXCIII', 'MMMCXCIV', 'MMMCVC', 'MMMCVCI', 'MMMCVCII', 'MMMCVCIII', 'MMMCIC', 'MMMCC', 'MMMCCI', 'MMMCCII', 'MMMCCIII', 'MMMCCIV', 'MMMCCV', 'MMMCCVI', 'MMMCCVII', 'MMMCCVIII', 'MMMCCIX', 'MMMCCX', 'MMMCCXI', 'MMMCCXII', 'MMMCCXIII', 'MMMCCXIV', 'MMMCCXV', 'MMMCCXVI', 'MMMCCXVII', 'MMMCCXVIII', 'MMMCCXIX', 'MMMCCXX', 'MMMCCXXI', 'MMMCCXXII', 'MMMCCXXIII', 'MMMCCXXIV', 'MMMCCXXV', 'MMMCCXXVI', 'MMMCCXXVII', 'MMMCCXXVIII', 'MMMCCXXIX', 'MMMCCXXX', 'MMMCCXXXI', 'MMMCCXXXII', 'MMMCCXXXIII', 'MMMCCXXXIV', 'MMMCCXXXV', 'MMMCCXXXVI', 'MMMCCXXXVII', 'MMMCCXXXVIII', 'MMMCCXXXIX', 'MMMCCXL', 'MMMCCXLI', 'MMMCCXLII', 'MMMCCXLIII', 'MMMCCXLIV', 'MMMCCVL', 'MMMCCVLI', 'MMMCCVLII', 'MMMCCVLIII', 'MMMCCIL', 'MMMCCL', 'MMMCCLI', 'MMMCCLII', 'MMMCCLIII', 'MMMCCLIV', 'MMMCCLV', 'MMMCCLVI', 'MMMCCLVII', 'MMMCCLVIII', 'MMMCCLIX', 'MMMCCLX', 'MMMCCLXI', 'MMMCCLXII', 'MMMCCLXIII', 'MMMCCLXIV', 'MMMCCLXV', 'MMMCCLXVI', 'MMMCCLXVII', 'MMMCCLXVIII', 'MMMCCLXIX', 'MMMCCLXX', 'MMMCCLXXI', 'MMMCCLXXII', 'MMMCCLXXIII', 'MMMCCLXXIV', 'MMMCCLXXV', 'MMMCCLXXVI', 'MMMCCLXXVII', 'MMMCCLXXVIII', 'MMMCCLXXIX', 'MMMCCLXXX', 'MMMCCLXXXI', 'MMMCCLXXXII', 'MMMCCLXXXIII', 'MMMCCLXXXIV', 'MMMCCLXXXV', 'MMMCCLXXXVI', 'MMMCCLXXXVII', 'MMMCCLXXXVIII', 'MMMCCLXXXIX', 'MMMCCXC', 'MMMCCXCI', 'MMMCCXCII', 'MMMCCXCIII', 'MMMCCXCIV', 'MMMCCVC', 'MMMCCVCI', 'MMMCCVCII', 'MMMCCVCIII', 'MMMCCIC', 'MMMCCC', 'MMMCCCI', 'MMMCCCII', 'MMMCCCIII', 'MMMCCCIV', 'MMMCCCV', 'MMMCCCVI', 'MMMCCCVII', 'MMMCCCVIII', 'MMMCCCIX', 'MMMCCCX', 'MMMCCCXI', 'MMMCCCXII', 'MMMCCCXIII', 'MMMCCCXIV', 'MMMCCCXV', 'MMMCCCXVI', 'MMMCCCXVII', 'MMMCCCXVIII', 'MMMCCCXIX', 'MMMCCCXX', 'MMMCCCXXI', 'MMMCCCXXII', 'MMMCCCXXIII', 'MMMCCCXXIV', 'MMMCCCXXV', 'MMMCCCXXVI', 'MMMCCCXXVII', 'MMMCCCXXVIII', 'MMMCCCXXIX', 'MMMCCCXXX', 'MMMCCCXXXI', 'MMMCCCXXXII', 'MMMCCCXXXIII', 'MMMCCCXXXIV', 'MMMCCCXXXV', 'MMMCCCXXXVI', 'MMMCCCXXXVII', 'MMMCCCXXXVIII', 'MMMCCCXXXIX', 'MMMCCCXL', 'MMMCCCXLI', 'MMMCCCXLII', 'MMMCCCXLIII', 'MMMCCCXLIV', 'MMMCCCVL', 'MMMCCCVLI', 'MMMCCCVLII', 'MMMCCCVLIII', 'MMMCCCIL', 'MMMCCCL', 'MMMCCCLI', 'MMMCCCLII', 'MMMCCCLIII', 'MMMCCCLIV', 'MMMCCCLV', 'MMMCCCLVI', 'MMMCCCLVII', 'MMMCCCLVIII', 'MMMCCCLIX', 'MMMCCCLX', 'MMMCCCLXI', 'MMMCCCLXII', 'MMMCCCLXIII', 'MMMCCCLXIV', 'MMMCCCLXV', 'MMMCCCLXVI', 'MMMCCCLXVII', 'MMMCCCLXVIII', 'MMMCCCLXIX', 'MMMCCCLXX', 'MMMCCCLXXI', 'MMMCCCLXXII', 'MMMCCCLXXIII', 'MMMCCCLXXIV', 'MMMCCCLXXV', 'MMMCCCLXXVI', 'MMMCCCLXXVII', 'MMMCCCLXXVIII', 'MMMCCCLXXIX', 'MMMCCCLXXX', 'MMMCCCLXXXI', 'MMMCCCLXXXII', 'MMMCCCLXXXIII', 'MMMCCCLXXXIV', 'MMMCCCLXXXV', 'MMMCCCLXXXVI', 'MMMCCCLXXXVII', 'MMMCCCLXXXVIII', 'MMMCCCLXXXIX', 'MMMCCCXC', 'MMMCCCXCI', 'MMMCCCXCII', 'MMMCCCXCIII', 'MMMCCCXCIV', 'MMMCCCVC', 'MMMCCCVCI', 'MMMCCCVCII', 'MMMCCCVCIII', 'MMMCCCIC', 'MMMCD', 'MMMCDI', 'MMMCDII', 'MMMCDIII', 'MMMCDIV', 'MMMCDV', 'MMMCDVI', 'MMMCDVII', 'MMMCDVIII', 'MMMCDIX', 'MMMCDX', 'MMMCDXI', 'MMMCDXII', 'MMMCDXIII', 'MMMCDXIV', 'MMMCDXV', 'MMMCDXVI', 'MMMCDXVII', 'MMMCDXVIII', 'MMMCDXIX', 'MMMCDXX', 'MMMCDXXI', 'MMMCDXXII', 'MMMCDXXIII', 'MMMCDXXIV', 'MMMCDXXV', 'MMMCDXXVI', 'MMMCDXXVII', 'MMMCDXXVIII', 'MMMCDXXIX', 'MMMCDXXX', 'MMMCDXXXI', 'MMMCDXXXII', 'MMMCDXXXIII', 'MMMCDXXXIV', 'MMMCDXXXV', 'MMMCDXXXVI', 'MMMCDXXXVII', 'MMMCDXXXVIII', 'MMMCDXXXIX', 'MMMCDXL', 'MMMCDXLI', 'MMMCDXLII', 'MMMCDXLIII', 'MMMCDXLIV', 'MMMCDVL', 'MMMCDVLI', 'MMMCDVLII', 'MMMCDVLIII', 'MMMCDIL', 'MMMLD', 'MMMLDI', 'MMMLDII', 'MMMLDIII', 'MMMLDIV', 'MMMLDV', 'MMMLDVI', 'MMMLDVII', 'MMMLDVIII', 'MMMLDIX', 'MMMLDX', 'MMMLDXI', 'MMMLDXII', 'MMMLDXIII', 'MMMLDXIV', 'MMMLDXV', 'MMMLDXVI', 'MMMLDXVII', 'MMMLDXVIII', 'MMMLDXIX', 'MMMLDXX', 'MMMLDXXI', 'MMMLDXXII', 'MMMLDXXIII', 'MMMLDXXIV', 'MMMLDXXV', 'MMMLDXXVI', 'MMMLDXXVII', 'MMMLDXXVIII', 'MMMLDXXIX', 'MMMLDXXX', 'MMMLDXXXI', 'MMMLDXXXII', 'MMMLDXXXIII', 'MMMLDXXXIV', 'MMMLDXXXV', 'MMMLDXXXVI', 'MMMLDXXXVII', 'MMMLDXXXVIII', 'MMMLDXXXIX', 'MMMXD', 'MMMXDI', 'MMMXDII', 'MMMXDIII', 'MMMXDIV', 'MMMVD', 'MMMVDI', 'MMMVDII', 'MMMVDIII', 'MMMID', 'MMMD', 'MMMDI', 'MMMDII', 'MMMDIII', 'MMMDIV', 'MMMDV', 'MMMDVI', 'MMMDVII', 'MMMDVIII', 'MMMDIX', 'MMMDX', 'MMMDXI', 'MMMDXII', 'MMMDXIII', 'MMMDXIV', 'MMMDXV', 'MMMDXVI', 'MMMDXVII', 'MMMDXVIII', 'MMMDXIX', 'MMMDXX', 'MMMDXXI', 'MMMDXXII', 'MMMDXXIII', 'MMMDXXIV', 'MMMDXXV', 'MMMDXXVI', 'MMMDXXVII', 'MMMDXXVIII', 'MMMDXXIX', 'MMMDXXX', 'MMMDXXXI', 'MMMDXXXII', 'MMMDXXXIII', 'MMMDXXXIV', 'MMMDXXXV', 'MMMDXXXVI', 'MMMDXXXVII', 'MMMDXXXVIII', 'MMMDXXXIX', 'MMMDXL', 'MMMDXLI', 'MMMDXLII', 'MMMDXLIII', 'MMMDXLIV', 'MMMDVL', 'MMMDVLI', 'MMMDVLII', 'MMMDVLIII', 'MMMDIL', 'MMMDL', 'MMMDLI', 'MMMDLII', 'MMMDLIII', 'MMMDLIV', 'MMMDLV', 'MMMDLVI', 'MMMDLVII', 'MMMDLVIII', 'MMMDLIX', 'MMMDLX', 'MMMDLXI', 'MMMDLXII', 'MMMDLXIII', 'MMMDLXIV', 'MMMDLXV', 'MMMDLXVI', 'MMMDLXVII', 'MMMDLXVIII', 'MMMDLXIX', 'MMMDLXX', 'MMMDLXXI', 'MMMDLXXII', 'MMMDLXXIII', 'MMMDLXXIV', 'MMMDLXXV', 'MMMDLXXVI', 'MMMDLXXVII', 'MMMDLXXVIII', 'MMMDLXXIX', 'MMMDLXXX', 'MMMDLXXXI', 'MMMDLXXXII', 'MMMDLXXXIII', 'MMMDLXXXIV', 'MMMDLXXXV', 'MMMDLXXXVI', 'MMMDLXXXVII', 'MMMDLXXXVIII', 'MMMDLXXXIX', 'MMMDXC', 'MMMDXCI', 'MMMDXCII', 'MMMDXCIII', 'MMMDXCIV', 'MMMDVC', 'MMMDVCI', 'MMMDVCII', 'MMMDVCIII', 'MMMDIC', 'MMMDC', 'MMMDCI', 'MMMDCII', 'MMMDCIII', 'MMMDCIV', 'MMMDCV', 'MMMDCVI', 'MMMDCVII', 'MMMDCVIII', 'MMMDCIX', 'MMMDCX', 'MMMDCXI', 'MMMDCXII', 'MMMDCXIII', 'MMMDCXIV', 'MMMDCXV', 'MMMDCXVI', 'MMMDCXVII', 'MMMDCXVIII', 'MMMDCXIX', 'MMMDCXX', 'MMMDCXXI', 'MMMDCXXII', 'MMMDCXXIII', 'MMMDCXXIV', 'MMMDCXXV', 'MMMDCXXVI', 'MMMDCXXVII', 'MMMDCXXVIII', 'MMMDCXXIX', 'MMMDCXXX', 'MMMDCXXXI', 'MMMDCXXXII', 'MMMDCXXXIII', 'MMMDCXXXIV', 'MMMDCXXXV', 'MMMDCXXXVI', 'MMMDCXXXVII', 'MMMDCXXXVIII', 'MMMDCXXXIX', 'MMMDCXL', 'MMMDCXLI', 'MMMDCXLII', 'MMMDCXLIII', 'MMMDCXLIV', 'MMMDCVL', 'MMMDCVLI', 'MMMDCVLII', 'MMMDCVLIII', 'MMMDCIL', 'MMMDCL', 'MMMDCLI', 'MMMDCLII', 'MMMDCLIII', 'MMMDCLIV', 'MMMDCLV', 'MMMDCLVI', 'MMMDCLVII', 'MMMDCLVIII', 'MMMDCLIX', 'MMMDCLX', 'MMMDCLXI', 'MMMDCLXII', 'MMMDCLXIII', 'MMMDCLXIV', 'MMMDCLXV', 'MMMDCLXVI', 'MMMDCLXVII', 'MMMDCLXVIII', 'MMMDCLXIX', 'MMMDCLXX', 'MMMDCLXXI', 'MMMDCLXXII', 'MMMDCLXXIII', 'MMMDCLXXIV', 'MMMDCLXXV', 'MMMDCLXXVI', 'MMMDCLXXVII', 'MMMDCLXXVIII', 'MMMDCLXXIX', 'MMMDCLXXX', 'MMMDCLXXXI', 'MMMDCLXXXII', 'MMMDCLXXXIII', 'MMMDCLXXXIV', 'MMMDCLXXXV', 'MMMDCLXXXVI', 'MMMDCLXXXVII', 'MMMDCLXXXVIII', 'MMMDCLXXXIX', 'MMMDCXC', 'MMMDCXCI', 'MMMDCXCII', 'MMMDCXCIII', 'MMMDCXCIV', 'MMMDCVC', 'MMMDCVCI', 'MMMDCVCII', 'MMMDCVCIII', 'MMMDCIC', 'MMMDCC', 'MMMDCCI', 'MMMDCCII', 'MMMDCCIII', 'MMMDCCIV', 'MMMDCCV', 'MMMDCCVI', 'MMMDCCVII', 'MMMDCCVIII', 'MMMDCCIX', 'MMMDCCX', 'MMMDCCXI', 'MMMDCCXII', 'MMMDCCXIII', 'MMMDCCXIV', 'MMMDCCXV', 'MMMDCCXVI', 'MMMDCCXVII', 'MMMDCCXVIII', 'MMMDCCXIX', 'MMMDCCXX', 'MMMDCCXXI', 'MMMDCCXXII', 'MMMDCCXXIII', 'MMMDCCXXIV', 'MMMDCCXXV', 'MMMDCCXXVI', 'MMMDCCXXVII', 'MMMDCCXXVIII', 'MMMDCCXXIX', 'MMMDCCXXX', 'MMMDCCXXXI', 'MMMDCCXXXII', 'MMMDCCXXXIII', 'MMMDCCXXXIV', 'MMMDCCXXXV', 'MMMDCCXXXVI', 'MMMDCCXXXVII', 'MMMDCCXXXVIII', 'MMMDCCXXXIX', 'MMMDCCXL', 'MMMDCCXLI', 'MMMDCCXLII', 'MMMDCCXLIII', 'MMMDCCXLIV', 'MMMDCCVL', 'MMMDCCVLI', 'MMMDCCVLII', 'MMMDCCVLIII', 'MMMDCCIL', 'MMMDCCL', 'MMMDCCLI', 'MMMDCCLII', 'MMMDCCLIII', 'MMMDCCLIV', 'MMMDCCLV', 'MMMDCCLVI', 'MMMDCCLVII', 'MMMDCCLVIII', 'MMMDCCLIX', 'MMMDCCLX', 'MMMDCCLXI', 'MMMDCCLXII', 'MMMDCCLXIII', 'MMMDCCLXIV', 'MMMDCCLXV', 'MMMDCCLXVI', 'MMMDCCLXVII', 'MMMDCCLXVIII', 'MMMDCCLXIX', 'MMMDCCLXX', 'MMMDCCLXXI', 'MMMDCCLXXII', 'MMMDCCLXXIII', 'MMMDCCLXXIV', 'MMMDCCLXXV', 'MMMDCCLXXVI', 'MMMDCCLXXVII', 'MMMDCCLXXVIII', 'MMMDCCLXXIX', 'MMMDCCLXXX', 'MMMDCCLXXXI', 'MMMDCCLXXXII', 'MMMDCCLXXXIII', 'MMMDCCLXXXIV', 'MMMDCCLXXXV', 'MMMDCCLXXXVI', 'MMMDCCLXXXVII', 'MMMDCCLXXXVIII', 'MMMDCCLXXXIX', 'MMMDCCXC', 'MMMDCCXCI', 'MMMDCCXCII', 'MMMDCCXCIII', 'MMMDCCXCIV', 'MMMDCCVC', 'MMMDCCVCI', 'MMMDCCVCII', 'MMMDCCVCIII', 'MMMDCCIC', 'MMMDCCC', 'MMMDCCCI', 'MMMDCCCII', 'MMMDCCCIII', 'MMMDCCCIV', 'MMMDCCCV', 'MMMDCCCVI', 'MMMDCCCVII', 'MMMDCCCVIII', 'MMMDCCCIX', 'MMMDCCCX', 'MMMDCCCXI', 'MMMDCCCXII', 'MMMDCCCXIII', 'MMMDCCCXIV', 'MMMDCCCXV', 'MMMDCCCXVI', 'MMMDCCCXVII', 'MMMDCCCXVIII', 'MMMDCCCXIX', 'MMMDCCCXX', 'MMMDCCCXXI', 'MMMDCCCXXII', 'MMMDCCCXXIII', 'MMMDCCCXXIV', 'MMMDCCCXXV', 'MMMDCCCXXVI', 'MMMDCCCXXVII', 'MMMDCCCXXVIII', 'MMMDCCCXXIX', 'MMMDCCCXXX', 'MMMDCCCXXXI', 'MMMDCCCXXXII', 'MMMDCCCXXXIII', 'MMMDCCCXXXIV', 'MMMDCCCXXXV', 'MMMDCCCXXXVI', 'MMMDCCCXXXVII', 'MMMDCCCXXXVIII', 'MMMDCCCXXXIX', 'MMMDCCCXL', 'MMMDCCCXLI', 'MMMDCCCXLII', 'MMMDCCCXLIII', 'MMMDCCCXLIV', 'MMMDCCCVL', 'MMMDCCCVLI', 'MMMDCCCVLII', 'MMMDCCCVLIII', 'MMMDCCCIL', 'MMMDCCCL', 'MMMDCCCLI', 'MMMDCCCLII', 'MMMDCCCLIII', 'MMMDCCCLIV', 'MMMDCCCLV', 'MMMDCCCLVI', 'MMMDCCCLVII', 'MMMDCCCLVIII', 'MMMDCCCLIX', 'MMMDCCCLX', 'MMMDCCCLXI', 'MMMDCCCLXII', 'MMMDCCCLXIII', 'MMMDCCCLXIV', 'MMMDCCCLXV', 'MMMDCCCLXVI', 'MMMDCCCLXVII', 'MMMDCCCLXVIII', 'MMMDCCCLXIX', 'MMMDCCCLXX', 'MMMDCCCLXXI', 'MMMDCCCLXXII', 'MMMDCCCLXXIII', 'MMMDCCCLXXIV', 'MMMDCCCLXXV', 'MMMDCCCLXXVI', 'MMMDCCCLXXVII', 'MMMDCCCLXXVIII', 'MMMDCCCLXXIX', 'MMMDCCCLXXX', 'MMMDCCCLXXXI', 'MMMDCCCLXXXII', 'MMMDCCCLXXXIII', 'MMMDCCCLXXXIV', 'MMMDCCCLXXXV', 'MMMDCCCLXXXVI', 'MMMDCCCLXXXVII', 'MMMDCCCLXXXVIII', 'MMMDCCCLXXXIX', 'MMMDCCCXC', 'MMMDCCCXCI', 'MMMDCCCXCII', 'MMMDCCCXCIII', 'MMMDCCCXCIV', 'MMMDCCCVC', 'MMMDCCCVCI', 'MMMDCCCVCII', 'MMMDCCCVCIII', 'MMMDCCCIC', 'MMMCM', 'MMMCMI', 'MMMCMII', 'MMMCMIII', 'MMMCMIV', 'MMMCMV', 'MMMCMVI', 'MMMCMVII', 'MMMCMVIII', 'MMMCMIX', 'MMMCMX', 'MMMCMXI', 'MMMCMXII', 'MMMCMXIII', 'MMMCMXIV', 'MMMCMXV', 'MMMCMXVI', 'MMMCMXVII', 'MMMCMXVIII', 'MMMCMXIX', 'MMMCMXX', 'MMMCMXXI', 'MMMCMXXII', 'MMMCMXXIII', 'MMMCMXXIV', 'MMMCMXXV', 'MMMCMXXVI', 'MMMCMXXVII', 'MMMCMXXVIII', 'MMMCMXXIX', 'MMMCMXXX', 'MMMCMXXXI', 'MMMCMXXXII', 'MMMCMXXXIII', 'MMMCMXXXIV', 'MMMCMXXXV', 'MMMCMXXXVI', 'MMMCMXXXVII', 'MMMCMXXXVIII', 'MMMCMXXXIX', 'MMMCMXL', 'MMMCMXLI', 'MMMCMXLII', 'MMMCMXLIII', 'MMMCMXLIV', 'MMMCMVL', 'MMMCMVLI', 'MMMCMVLII', 'MMMCMVLIII', 'MMMCMIL', 'MMMLM', 'MMMLMI', 'MMMLMII', 'MMMLMIII', 'MMMLMIV', 'MMMLMV', 'MMMLMVI', 'MMMLMVII', 'MMMLMVIII', 'MMMLMIX', 'MMMLMX', 'MMMLMXI', 'MMMLMXII', 'MMMLMXIII', 'MMMLMXIV', 'MMMLMXV', 'MMMLMXVI', 'MMMLMXVII', 'MMMLMXVIII', 'MMMLMXIX', 'MMMLMXX', 'MMMLMXXI', 'MMMLMXXII', 'MMMLMXXIII', 'MMMLMXXIV', 'MMMLMXXV', 'MMMLMXXVI', 'MMMLMXXVII', 'MMMLMXXVIII', 'MMMLMXXIX', 'MMMLMXXX', 'MMMLMXXXI', 'MMMLMXXXII', 'MMMLMXXXIII', 'MMMLMXXXIV', 'MMMLMXXXV', 'MMMLMXXXVI', 'MMMLMXXXVII', 'MMMLMXXXVIII', 'MMMLMXXXIX', 'MMMXM', 'MMMXMI', 'MMMXMII', 'MMMXMIII', 'MMMXMIV', 'MMMVM', 'MMMVMI', 'MMMVMII', 'MMMVMIII', 'MMMIM', ] diff --git a/test/unit/interpreter/function-array_constrain.spec.ts b/test/unit/interpreter/function-array_constrain.spec.ts deleted file mode 100644 index b17ec7ba70..0000000000 --- a/test/unit/interpreter/function-array_constrain.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Interpreter - function ARRAY_CONSTRAIN', () => { - it('works #1', () => { - const engine = HyperFormula.buildFromArray([ - ['=ARRAY_CONSTRAIN(1, 1, 1)'], - ]) - expect(engine.getSheetValues(0)).toEqual([[1]]) - }) - - it('works #2', () => { - const engine = HyperFormula.buildFromArray([ - ['=ARRAY_CONSTRAIN(1, 2, 2)'], - ]) - expect(engine.getSheetValues(0)).toEqual([[1]]) - }) - - it('validates args', () => { - const engine = HyperFormula.buildFromArray([ - ['=ARRAY_CONSTRAIN(1, 0, 1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('works #3', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=ARRAY_CONSTRAIN(Sheet2!A1:C3, 2, 2)'], - ], - Sheet2: [ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ] - }) - expect(engine.getSheetValues(0)).toEqual([[1, 2], [4, 5]]) - }) - - it('works #4', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['=ARRAY_CONSTRAIN(Sheet2!A1:C3, 2, 4)'], - ], - Sheet2: [ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ] - }) - expect(engine.getSheetValues(0)).toEqual([[1, 2, 3], [4, 5, 6]]) - }) - - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ARRAY_CONSTRAIN(1, 2)'], - ['=ARRAY_CONSTRAIN(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) -}) diff --git a/test/unit/interpreter/function-arrayformula.spec.ts b/test/unit/interpreter/function-arrayformula.spec.ts deleted file mode 100644 index bd25faf935..0000000000 --- a/test/unit/interpreter/function-arrayformula.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Interpreter - function ARRAYFORMULA', () => { - it('works #1', () => { - const engine = HyperFormula.buildFromArray([ - ['=ARRAYFORMULA(1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('works #2', () => { - const engine = HyperFormula.buildFromArray([ - ['=ARRAYFORMULA(1/0)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('enables arrayformulas', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(ARRAYFORMULA(A2:C2+A2:C2))'], - [1, 2, 3] - ], {useArrayArithmetic: false}) - expect(engine.getCellValue(adr('A1'))).toEqual(12) - }) - - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ARRAYFORMULA()'], - ['=ARRAYFORMULA(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) -}) diff --git a/test/unit/interpreter/function-asin.spec.ts b/test/unit/interpreter/function-asin.spec.ts deleted file mode 100644 index d1ac087e49..0000000000 --- a/test/unit/interpreter/function-asin.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ASIN', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=ASIN(0)', '=ASIN(0.5)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.523598775598299) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=ASIN("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('for 1 (edge)', () => { - const engine = HyperFormula.buildFromArray([['=ASIN(1)']], {smartRounding: false}) - - expect(engine.getCellValue(adr('A1'))).toBe(Math.PI / 2) - }) - - it('for -1 (edge)', () => { - const engine = HyperFormula.buildFromArray([['=ASIN(-1)']], {smartRounding: false}) - - expect(engine.getCellValue(adr('A1'))).toEqual(-Math.PI / 2) - }) - - it('when value too large', () => { - const engine = HyperFormula.buildFromArray([['=ASIN(1.1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('when value too small', () => { - const engine = HyperFormula.buildFromArray([['=ASIN(-1.1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=ASIN()', '=ASIN(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=ASIN(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-Math.PI / 2) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=ASIN(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-asinh.spec.ts b/test/unit/interpreter/function-asinh.spec.ts deleted file mode 100644 index fc63d67680..0000000000 --- a/test/unit/interpreter/function-asinh.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ASINH', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=ASINH(0)', '=ASINH(0.5)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.481211825059604) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=ASINH("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=ASINH()', '=ASINH(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=ASINH(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-0.881373587019543) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=ASINH(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-atan.spec.ts b/test/unit/interpreter/function-atan.spec.ts deleted file mode 100644 index a9ce9fa24e..0000000000 --- a/test/unit/interpreter/function-atan.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ATAN', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=ATAN(1)']], {smartRounding: false}) - - expect(engine.getCellValue(adr('A1'))).toBe(0.7853981633974483) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=ATAN("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=ATAN()', '=ATAN(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=ATAN(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-0.785398163397448) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=ATAN(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-atan2.spec.ts b/test/unit/interpreter/function-atan2.spec.ts deleted file mode 100644 index d29dedfaae..0000000000 --- a/test/unit/interpreter/function-atan2.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ATAN2', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=ATAN2(1, 2)']], {smartRounding: false}) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.107148718, 6) - }) - - it('validates error', () => { - const engine = HyperFormula.buildFromArray([['=ATAN2(0, 0)']], {smartRounding: false}) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=ATAN2(1,"foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=ATAN2()', '=ATAN2(1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '="1"', '=ATAN2(A1,B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(2.35619449019234) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=ATAN2(4/0, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-atanh.spec.ts b/test/unit/interpreter/function-atanh.spec.ts deleted file mode 100644 index b67a2cafb5..0000000000 --- a/test/unit/interpreter/function-atanh.spec.ts +++ /dev/null @@ -1,53 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ATANH', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=ATANH(0)', '=ATANH(0.5)']], {smartRounding: false}) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.5493061443340548) - }) - - it('error for 1', () => { - const engine = HyperFormula.buildFromArray([['=ATANH(1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('error for -1', () => { - const engine = HyperFormula.buildFromArray([['=ATANH(-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=ATANH("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=ATANH()', '=ATANH(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="0"', '=ATANH(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(0) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=ATANH(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-avedev.spec.ts b/test/unit/interpreter/function-avedev.spec.ts deleted file mode 100644 index 71cc7cced3..0000000000 --- a/test/unit/interpreter/function-avedev.spec.ts +++ /dev/null @@ -1,88 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {adr, detailedError} from '../testUtils' - -describe('Function AVEDEV', () => { - it('single number', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVEDEV(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) - - it('two numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVEDEV(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.5) - }) - - it('more numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVEDEV(3, 1, 2, 4, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1.2) - }) - - it('works with ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '9', '0'], - ['=AVEDEV(A1:C1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(4) - }) - - it('propagates error from regular argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=3/0', '=AVEDEV(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('propagates first error from range argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=3/0', '=FOO(', '=AVEDEV(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('returns error for empty ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVEDEV(A2:A3)'], - [null], - [null], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - /** - * Product #1 returns 0 - */ - it('does coercions of nonnumeric explicit arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVEDEV(TRUE(),FALSE(),)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.444444444444444, 6) - }) - - /** - * Product #2 returns 0.2: - * average is computed from numbers, but sum of distances to avg is divided by the original range size. - */ - it('ignores nonnumeric values in ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVEDEV(A2:E2)'], - [0, 1, false, null, '\'0'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.5) - }) -}) diff --git a/test/unit/interpreter/function-average.spec.ts b/test/unit/interpreter/function-average.spec.ts deleted file mode 100644 index f82b381ecc..0000000000 --- a/test/unit/interpreter/function-average.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('AVERAGE', () => { - it('AVERAGE with empty args', () => { - const engine = HyperFormula.buildFromArray([['=AVERAGE()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('AVERAGE with args', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGE(1, B1)', '4'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2.5) - }) - - it('AVERAGE with range', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['4'], - ['=AVERAGE(A1:A3)'] - ]) - - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(2.333333333) - }) - - it('AVERAGE ignores all nonnumeric arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ['foo'], - [null], - ['=TRUE()'], - ['=AVERAGE(A1:A4)'] - ]) - - expect(engine.getCellValue(adr('A5'))).toEqual(42) - }) - - it('error when no meaningful arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['foo'], - [null], - ['=AVERAGE(A1:A2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=AVERAGE(MMULT(A1:B2, A1:B2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(13.5) - }) - - it('does propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=4/0'], - ['=FOOBAR()', '4'], - ['=AVERAGE(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-averagea.spec.ts b/test/unit/interpreter/function-averagea.spec.ts deleted file mode 100644 index 53b384de4f..0000000000 --- a/test/unit/interpreter/function-averagea.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('AVERAGEA', () => { - it('AVERAGEA with empty args', () => { - const engine = HyperFormula.buildFromArray([['=AVERAGEA()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('AVERAGEA with args', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGEA(1, B1)', '4'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2.5) - }) - - it('AVERAGEA with range', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['4'], - ['=AVERAGEA(A1:A3)'] - ]) - - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(2.333333333) - }) - - it('AVERAGEA converts non-blank values to numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['39', '="1"', '=AVERAGEA(A1:B1)'], - ['39', '=TRUE()', '=AVERAGEA(A2:B2)'], - ['39', null, '=AVERAGEA(A3:B3)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(19.5) - expect(engine.getCellValue(adr('C2'))).toEqual(20) - expect(engine.getCellValue(adr('C3'))).toEqual(39) - }) - - it('error when no meaningful arguments', () => { - const engine = HyperFormula.buildFromArray([ - [null, 'foo'], - [null, null], - ['=AVERAGEA(A1:A2)', '=AVERAGEA(B1:B2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('B3'))).toEqual(0) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=AVERAGEA(MMULT(A1:B2, A1:B2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(13.5) - }) - - it('does propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=4/0'], - ['=FOOBAR()', '4'], - ['=AVERAGEA(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-averageif.spec.ts b/test/unit/interpreter/function-averageif.spec.ts deleted file mode 100644 index b6b9fe09d3..0000000000 --- a/test/unit/interpreter/function-averageif.spec.ts +++ /dev/null @@ -1,147 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function AVERAGEIF - argument validations and combinations', () => { - it('requires 2 or 3 arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGEIF(C1)'], - ['=AVERAGEIF(C1, ">0", C1, C1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('error when criterion unparsable', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGEIF(B1:B2, "> { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGEIF(B1:C1, ">0", B2:D2)'], - ['=AVERAGEIF(B1, ">0", B2:D2)'], - ['=AVERAGEIF(B1:D1, ">0", B2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - }) - - it('error when different height dimension of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGEIF(B1:B2, ">0", C1:C3)'], - ['=AVERAGEIF(B1, ">0", C1:C2)'], - ['=AVERAGEIF(B1:B2, ">0", C1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - }) - - it('error when number of elements match but dimensions doesnt', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGEIF(B1:B2, ">0", B1:C1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - }) - - it('scalars are treated like singular arrays', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGEIF(10, ">1", 42)'], - ['=AVERAGEIF(0, ">1", 42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(42) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('no coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="1"'], - ['="foo"'], - [null], - ['=AVERAGEIF(A1:A3, "<>42")'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('error propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=AVERAGEIF(4/0, ">1", 42)'], - ['=AVERAGEIF(0, 4/0, 42)'], - ['=AVERAGEIF(0, ">1", 4/0)'], - ['=AVERAGEIF(0, 4/0, FOOBAR())'], - ['=AVERAGEIF(4/0, FOOBAR(), 42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('works when arguments are just references', () => { - const engine = HyperFormula.buildFromArray([ - ['2', '3'], - ['=AVERAGEIF(A1, ">1", B1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(3) - }) - - it('works with range values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1', '3', '5'], - ['1', '1', '7', '9'], - ['=AVERAGEIF(MMULT(A1:B2, A1:B2), "=2", MMULT(C1:D2, C1:D2))'], - ['=AVERAGEIF(A1:B2, "=1", MMULT(C1:D2, C1:D2))'], - ['=AVERAGEIF(MMULT(A1:B2, A1:B2), "=2", C1:D2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(76) - expect(engine.getCellValue(adr('A4'))).toEqual(76) - expect(engine.getCellValue(adr('A5'))).toEqual(6) - }) - - it('works for mixed reference/range arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['2', '3'], - ['=AVERAGEIF(A1:A1, ">1", B1)'], - ['=AVERAGEIF(A1, ">1", B1:B1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(3) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - }) - - it('works for 2 arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['10', '20', '30'], - ['=AVERAGEIF(A1:C1, ">15")'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(25) - }) - - it('works for matrices', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - [], - ['=AVERAGEIF(A2:A3, ">0", A2:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(1.5) - }) -}) diff --git a/test/unit/interpreter/function-base.spec.ts b/test/unit/interpreter/function-base.spec.ts deleted file mode 100644 index f3d7eadaa7..0000000000 --- a/test/unit/interpreter/function-base.spec.ts +++ /dev/null @@ -1,102 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function BASE', () => { - it('should return error when argument of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BASE("foo", 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return error when wrong number of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=BASE("foo")'], - ['=BASE("foo", 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=BASE(1, 2, 3)'], - ['=BASE(2, 5)'], - ['=BASE(23, "10")'], - ['=BASE(254, 15, "9")'], - ['=BASE(634, 33)'], - ['=BASE(789, 36)'], - ['=BASE(1234123412341230, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('001') - expect(engine.getCellValue(adr('A2'))).toEqual('2') - expect(engine.getCellValue(adr('A3'))).toEqual('23') - expect(engine.getCellValue(adr('A4'))).toEqual('00000011E') - expect(engine.getCellValue(adr('A5'))).toEqual('J7') - expect(engine.getCellValue(adr('A6'))).toEqual('LX') - expect(engine.getCellValue(adr('A7'))).toEqual('100011000100110110110111111100110100000000111101110') - }) - - it('should work for numeric strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=BASE("123", 4)'], - ['=BASE("1234", 16)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1323') - expect(engine.getCellValue(adr('A2'))).toEqual('4D2') - }) - - it('should return string value', () => { - const engine = HyperFormula.buildFromArray([ - ['=BASE(123, 2)'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.STRING) - }) - - it('should respect third argument and fill with zeros', () => { - const engine = HyperFormula.buildFromArray([ - ['=BASE(2, 8, 3)'], - ['=BASE(94862, "33", 16)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('002') - expect(engine.getCellValue(adr('A2'))).toEqual('0000000000002L3K') - }) - - it('should return result as is if padding shorter than result', () => { - const engine = HyperFormula.buildFromArray([ - ['=BASE(123, 2, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1111011') - }) - - it('should return error for negative values', () => { - const engine = HyperFormula.buildFromArray([ - ['=BASE(-2, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('should allow base from 2 to 36', () => { - const engine = HyperFormula.buildFromArray([ - ['=BASE(2, 1)'], - ['=BASE(2, 2)'], - ['=BASE(2, 36)'], - ['=BASE(2, 37)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqual('10') - expect(engine.getCellValue(adr('A3'))).toEqual('2') - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-besseli.spec.ts b/test/unit/interpreter/function-besseli.spec.ts deleted file mode 100644 index 38739e0474..0000000000 --- a/test/unit/interpreter/function-besseli.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function BESSELI', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELI(1)'], - ['=BESSELI(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELI("foo", 1)'], - ['=BESSELI(2, "foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELI(-1,0)'], - ['=BESSELI(0,0)'], - ['=BESSELI(5,0)'], - ['=BESSELI(-1,1)'], - ['=BESSELI(0,1)'], - ['=BESSELI(5,1)'], - ['=BESSELI(-1,3)'], - ['=BESSELI(0,3)'], - ['=BESSELI(5,3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.26606584803426, 6) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(27.2398718943949, 6) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(-0.565159097581944, 6) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(24.3356418457055, 6) - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(-0.0221684244039833, 6) - expect(engine.getCellValue(adr('A8'))).toEqual(0) - expect(engine.getCellValue(adr('A9'))).toBeCloseTo(10.3311501959992, 6) - }) - - it('should check bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELI(1, -0.001)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - - }) - - it('should truncate second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELI(-1,0.9)'], - ['=BESSELI(0,0.9)'], - ['=BESSELI(5,0.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.26606584803426, 6) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(27.2398718943949, 6) - }) -}) diff --git a/test/unit/interpreter/function-besselj.spec.ts b/test/unit/interpreter/function-besselj.spec.ts deleted file mode 100644 index 2503d9eaf3..0000000000 --- a/test/unit/interpreter/function-besselj.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function BESSELJ', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELJ(1)'], - ['=BESSELJ(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELJ("foo", 1)'], - ['=BESSELJ(2, "foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELJ(-1,0)'], - ['=BESSELJ(0,0)'], - ['=BESSELJ(5,0)'], - ['=BESSELJ(-1,1)'], - ['=BESSELJ(0,1)'], - ['=BESSELJ(5,1)'], - ['=BESSELJ(-1,3)'], - ['=BESSELJ(0,3)'], - ['=BESSELJ(5,3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.765197683754859, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(1.00000000283141, 9) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(-0.177596774112343, 6) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(-0.44005058567713, 6) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(-0.327579138566363, 6) - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(-0.019563353982688, 6) - expect(engine.getCellValue(adr('A8'))).toEqual(0) - expect(engine.getCellValue(adr('A9'))).toBeCloseTo(0.364831233515002, 6) - }) - - it('should check bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELJ(1, -0.001)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - - }) - - it('should truncate second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELJ(-1,0.9)'], - ['=BESSELJ(0,0.9)'], - ['=BESSELJ(5,0.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.765197683754859, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(1.00000000283141, 9) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(-0.177596774112343, 6) - }) -}) diff --git a/test/unit/interpreter/function-besselk.spec.ts b/test/unit/interpreter/function-besselk.spec.ts deleted file mode 100644 index 6f0c1861d8..0000000000 --- a/test/unit/interpreter/function-besselk.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function BESSELK', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELK(1)'], - ['=BESSELK(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELK("foo", 1)'], - ['=BESSELK(2, "foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELK(0.1,0)'], - ['=BESSELK(1,0)'], - ['=BESSELK(5,0)'], - ['=BESSELK(0.1,1)'], - ['=BESSELK(1,1)'], - ['=BESSELK(5,1)'], - ['=BESSELK(0.1,3)'], - ['=BESSELK(1,3)'], - ['=BESSELK(5,3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(2.42706902485802, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.421024421083418, 6) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.00369109838196031, 6) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(9.85384478360091, 6) - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(0.601907231666906, 6) - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(0.00404461338320827, 6) - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(7990.01243265865, 6) - expect(engine.getCellValue(adr('A8'))).toBeCloseTo(7.10126276933582, 6) - expect(engine.getCellValue(adr('A9'))).toBeCloseTo(0.00829176837140317, 6) - }) - - it('should check bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELK(1, -0.001)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - - }) - - it('should truncate second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELK(0.1,0.9)'], - ['=BESSELK(1,0.9)'], - ['=BESSELK(5,0.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(2.42706902485802, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.421024421083418, 6) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.00369109838196031, 6) - }) -}) diff --git a/test/unit/interpreter/function-bessely.spec.ts b/test/unit/interpreter/function-bessely.spec.ts deleted file mode 100644 index 4e490a1004..0000000000 --- a/test/unit/interpreter/function-bessely.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function BESSELY', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELY(1)'], - ['=BESSELY(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELY("foo", 1)'], - ['=BESSELY(2, "foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELY(0.1,0)'], - ['=BESSELY(1,0)'], - ['=BESSELY(5,0)'], - ['=BESSELY(0.1,1)'], - ['=BESSELY(1,1)'], - ['=BESSELY(5,1)'], - ['=BESSELY(0.1,3)'], - ['=BESSELY(1,3)'], - ['=BESSELY(5,3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-1.53423866134966, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0882569713977081, 6) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(-0.308517623032057, 6) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(-6.45895109099111, 6) - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(-0.78121282095312, 6) - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(0.147863139887343, 6) - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(-5099.33237524791, 6) - expect(engine.getCellValue(adr('A8'))).toBeCloseTo(-5.82151763226267, 6) - expect(engine.getCellValue(adr('A9'))).toBeCloseTo(0.146267163302253, 6) - }) - - it('should check bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELY(1, -0.001)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - - }) - - it('should truncate second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=BESSELY(0.1,0.9)'], - ['=BESSELY(1,0.9)'], - ['=BESSELY(5,0.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-1.53423866134966, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0882569713977081, 6) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(-0.308517623032057, 6) - }) -}) diff --git a/test/unit/interpreter/function-beta.dist.spec.ts b/test/unit/interpreter/function-beta.dist.spec.ts deleted file mode 100644 index cc50b26b0b..0000000000 --- a/test/unit/interpreter/function-beta.dist.spec.ts +++ /dev/null @@ -1,82 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function BETA.DIST', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BETA.DIST(1, 2, 3)'], - ['=BETA.DIST(1, 2, 3, 4, 5, 6, 7)'], - ]) - - //product #1 returns 1 - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BETA.DIST("foo", 2, 3, TRUE())'], - ['=BETA.DIST(1, "baz", 3, TRUE())'], - ['=BETA.DIST(1, 2, "baz", TRUE())'], - ['=BETA.DIST(1, 2, 3, "abcd")'], - ['=BETA.DIST(1, 2, 3, TRUE(), "a", 2)'], - ['=BETA.DIST(1, 2, 3, TRUE(), 1, "b")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=BETA.DIST(0.1, 1, 2, TRUE())'], - ['=BETA.DIST(0.5, 2, 4, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.19, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.8125, 6) - }) - - it('should work as pdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=BETA.DIST(0.1, 1, 2, FALSE())'], - ['=BETA.DIST(0.5, 2, 4, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.8, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(1.25, 6) - }) - - it('scaling works', () => { - const engine = HyperFormula.buildFromArray([ - ['=BETA.DIST(1.2, 1, 2, TRUE(), 1, 3)'], - ['=BETA.DIST(15, 2, 4, TRUE(), 10, 20)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.19, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.8125, 6) - }) - - //product #1 returns 0 for tests 1,2,4,5 - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=BETA.DIST(0, 1, 1, FALSE())'], - ['=BETA.DIST(1, 0, 1, FALSE())'], - ['=BETA.DIST(1, 1, 0, FALSE())'], - ['=BETA.DIST(0.6, 1, 1, FALSE(), 0.6, 0.7)'], - ['=BETA.DIST(0.7, 1, 1, FALSE(), 0.6, 0.7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-beta.inv.spec.ts b/test/unit/interpreter/function-beta.inv.spec.ts deleted file mode 100644 index dabab842cc..0000000000 --- a/test/unit/interpreter/function-beta.inv.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function BETA.INV', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BETA.INV(1, 2)'], - ['=BETA.INV(1, 2, 3, 4, 5, 6)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BETA.INV("foo", 2, 3)'], - ['=BETA.INV(1, "baz", 3)'], - ['=BETA.INV(1, 2, "baz")'], - ['=BETA.INV(1, 2, 3, "a", 2)'], - ['=BETA.INV(1, 2, 3, 1, "b")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=BETA.INV(0.1, 1, 2)'], - ['=BETA.INV(0.5, 2, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0513167019494862, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.313810170455698, 6) - }) - - it('scaling works', () => { - const engine = HyperFormula.buildFromArray([ - ['=BETA.INV(0.1, 1, 2, 2, 10)'], - ['=BETA.INV(0.5, 2, 4, -1, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(2.41053361559589, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(-0.686189829544302, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=BETA.INV(0, 1, 1)'], - ['=BETA.INV(0.5, 0, 1)'], - ['=BETA.INV(0.5, 1, 0)'], - ['=BETA.INV(1, 1, 1)'], - ['=BETA.INV(1.0001, 1, 1)'], - ['=BETA.INV(0.6, 1, 1, 0.7, 0.6)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A4'))).toEqual(1) //product #2 returns NUM - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.WrongOrder)) - }) -}) diff --git a/test/unit/interpreter/function-bin2dec.spec.ts b/test/unit/interpreter/function-bin2dec.spec.ts deleted file mode 100644 index d9874410d8..0000000000 --- a/test/unit/interpreter/function-bin2dec.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function BIN2DEC', () => { - it('should work only for one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2DEC(101)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(5) - }) - - it('should not work for non-binary arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2DEC("foo")'], - ['=BIN2DEC(1234)'], - ['=BIN2DEC(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotBinary)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotBinary)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotBinary)) - }) - - it('should work only for 10 bits', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2DEC(10101010101010)'], - ['=BIN2DEC(1010101010)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotBinary)) - expect(engine.getCellValue(adr('A2'))).toEqual(-342) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2DEC(1111111111)'], - ['=BIN2DEC(1000000000)'], - ['=BIN2DEC(111111111)'], - ['=BIN2DEC(101)'], - ['=BIN2DEC(000101)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(-1) - expect(engine.getCellValue(adr('A2'))).toEqual(-512) - expect(engine.getCellValue(adr('A3'))).toEqual(511) - expect(engine.getCellValue(adr('A4'))).toEqual(5) - expect(engine.getCellValue(adr('A5'))).toEqual(5) - - }) - - it('should work with references', () => { - const engine = HyperFormula.buildFromArray([ - ['1101'], - ['=BIN2DEC(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(13) - }) - - it('should return numeric type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2DEC(101)'], - ]) - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.NUMBER) - }) -}) diff --git a/test/unit/interpreter/function-bin2hex.spec.ts b/test/unit/interpreter/function-bin2hex.spec.ts deleted file mode 100644 index e37793d263..0000000000 --- a/test/unit/interpreter/function-bin2hex.spec.ts +++ /dev/null @@ -1,111 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function BIN2HEX', () => { - it('should return error when wrong number of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2HEX("foo", 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for non-binary arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2HEX("foo")'], - ['=BIN2HEX(1234)'], - ['=BIN2HEX(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotBinary)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotBinary)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotBinary)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2HEX(1)'], - ['=BIN2HEX(10)'], - ['=BIN2HEX(010)'], - ['=BIN2HEX(101110)'], - ['=BIN2HEX(1000000000)'], - ['=BIN2HEX(1111111111)'], - ['=BIN2HEX(111111111)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - expect(engine.getCellValue(adr('A2'))).toEqual('2') - expect(engine.getCellValue(adr('A3'))).toEqual('2') - expect(engine.getCellValue(adr('A4'))).toEqual('2E') - expect(engine.getCellValue(adr('A5'))).toEqual('FFFFFFFE00') - expect(engine.getCellValue(adr('A6'))).toEqual('FFFFFFFFFF') - expect(engine.getCellValue(adr('A7'))).toEqual('1FF') - }) - - it('should work for binary strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2HEX("1101")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('D') - }) - - it('should work for reference', () => { - const engine = HyperFormula.buildFromArray([ - ['="1011"'], - ['=BIN2HEX(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual('B') - }) - - it('should return string value', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2HEX(10111)'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.STRING) - }) - - it('should work only for 10 bits', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2HEX(10101010101010)'], - ['=BIN2HEX(1010101010)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotBinary)) - expect(engine.getCellValue(adr('A2'))).toEqual('FFFFFFFEAA') - }) - - it('should respect second argument and fill with zeros for positive arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2HEX(10, 8)'], - ['=BIN2HEX(101, "4")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('00000002') - expect(engine.getCellValue(adr('A2'))).toEqual('0005') - }) - - it('second argument should not affect negative results', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2HEX(1110110100, 1)'], - ['=BIN2HEX(1110110100, 10)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('FFFFFFFFB4') - expect(engine.getCellValue(adr('A2'))).toEqual('FFFFFFFFB4') - }) - - it('should allow for numbers from 1 to 10 as second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2HEX(2, 0)'], - ['=BIN2HEX(-2, 12)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotBinary)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-bin2oct.spec.ts b/test/unit/interpreter/function-bin2oct.spec.ts deleted file mode 100644 index 0bc8e3ed65..0000000000 --- a/test/unit/interpreter/function-bin2oct.spec.ts +++ /dev/null @@ -1,111 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function BIN2OCT', () => { - it('should return error when wrong number of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2OCT("foo", 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for non-binary arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2OCT("foo")'], - ['=BIN2OCT(1234)'], - ['=BIN2OCT(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotBinary)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotBinary)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotBinary)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2OCT(1)'], - ['=BIN2OCT(10)'], - ['=BIN2OCT(010)'], - ['=BIN2OCT(101110)'], - ['=BIN2OCT(1000000000)'], - ['=BIN2OCT(1111111111)'], - ['=BIN2OCT(111111111)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - expect(engine.getCellValue(adr('A2'))).toEqual('2') - expect(engine.getCellValue(adr('A3'))).toEqual('2') - expect(engine.getCellValue(adr('A4'))).toEqual('56') - expect(engine.getCellValue(adr('A5'))).toEqual('7777777000') - expect(engine.getCellValue(adr('A6'))).toEqual('7777777777') - expect(engine.getCellValue(adr('A7'))).toEqual('777') - }) - - it('should work for binary strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2OCT("1101")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('15') - }) - - it('should work for reference', () => { - const engine = HyperFormula.buildFromArray([ - ['="1011"'], - ['=BIN2OCT(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual('13') - }) - - it('should return string value', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2OCT(10111)'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.STRING) - }) - - it('should work only for 10 bits', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2OCT(10101010101010)'], - ['=BIN2OCT(1010101010)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotBinary)) - expect(engine.getCellValue(adr('A2'))).toEqual('7777777252') - }) - - it('should respect second argument and fill with zeros for positive arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2OCT(10, 8)'], - ['=BIN2OCT(101, "4")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('00000002') - expect(engine.getCellValue(adr('A2'))).toEqual('0005') - }) - - it('second argument should not affect negative results', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2OCT(1110110100, 1)'], - ['=BIN2OCT(1110110100, 10)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('7777777664') - expect(engine.getCellValue(adr('A2'))).toEqual('7777777664') - }) - - it('should allow for numbers from 1 to 10 as second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=BIN2OCT(2, 0)'], - ['=BIN2OCT(-2, 12)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotBinary)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-binom.dist.spec.ts b/test/unit/interpreter/function-binom.dist.spec.ts deleted file mode 100644 index c41ebe8b20..0000000000 --- a/test/unit/interpreter/function-binom.dist.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function BINOM.DIST', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BINOM.DIST(1, 2, 3)'], - ['=BINOM.DIST(1, 2, 3, 4, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BINOM.DIST("foo", 2, 3, TRUE())'], - ['=BINOM.DIST(1, "baz", 3, TRUE())'], - ['=BINOM.DIST(1, 2, "baz", TRUE())'], - ['=BINOM.DIST(1, 1, 1, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=BINOM.DIST(1, 1, 0.1, TRUE())'], - ['=BINOM.DIST(10, 20, 0.7, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0479618973, 6) - }) - - it('should work as pdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=BINOM.DIST(1, 1, 0.1, FALSE())'], - ['=BINOM.DIST(10, 20, 0.7, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.1) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0308170809000851, 6) - }) - - it('truncation works', () => { - const engine = HyperFormula.buildFromArray([ - ['=BINOM.DIST(1.9, 1.99, 0.1, FALSE())'], - ['=BINOM.DIST(10.5, 20.2, 0.7, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.1) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0308170809000851, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=BINOM.DIST(-0.00001, 1, 1, FALSE())'], - ['=BINOM.DIST(0.5, -0.01, 1, FALSE())'], - ['=BINOM.DIST(0.5, 0.4, 1, FALSE())'], - ['=BINOM.DIST(1, 1, -0.01, FALSE())'], - ['=BINOM.DIST(1, 1, 1.01, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - //product #2 returns 1 for following test - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.WrongOrder)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-binom.inv.spec.ts b/test/unit/interpreter/function-binom.inv.spec.ts deleted file mode 100644 index 6f4420c5e5..0000000000 --- a/test/unit/interpreter/function-binom.inv.spec.ts +++ /dev/null @@ -1,168 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function BINOM.INV', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BINOM.INV(1, 2)'], - ['=BINOM.INV(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BINOM.INV("foo", 0.5, 3)'], - ['=BINOM.INV(1, "baz", 3)'], - ['=BINOM.INV(1, 0.5, "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=BINOM.INV(10, 0.5, 0.001)', - '=BINOM.INV(10, 0.5, 0.01)', - '=BINOM.INV(10, 0.5, 0.025)', - '=BINOM.INV(10, 0.5, 0.05)', - '=BINOM.INV(10, 0.5, 0.1)', - '=BINOM.INV(10, 0.5, 0.2)', - '=BINOM.INV(10, 0.5, 0.3)', - '=BINOM.INV(10, 0.5, 0.4)', - '=BINOM.INV(10, 0.5, 0.5)', - '=BINOM.INV(10, 0.5, 0.6)', - '=BINOM.INV(10, 0.5, 0.7)', - '=BINOM.INV(10, 0.5, 0.8)', - '=BINOM.INV(10, 0.5, 0.9)', - '=BINOM.INV(10, 0.5, 0.95)', - '=BINOM.INV(10, 0.5, 0.975)', - '=BINOM.INV(10, 0.5, 0.99)', - '=BINOM.INV(10, 0.5, 0.999)'], - ]) - - expect(engine.getSheetValues(0)).toEqual([[1, 1, 2, 2, 3, 4, 4, 5, 5, 5, 6, 6, 7, 8, 8, 9, 9]]) - }) - - it('should work, different p-value', () => { - const engine = HyperFormula.buildFromArray([ - ['=BINOM.INV(10, 0.8, 0.001)', - '=BINOM.INV(10, 0.8, 0.1)', - '=BINOM.INV(10, 0.8, 0.2)', - '=BINOM.INV(10, 0.8, 0.3)', - '=BINOM.INV(10, 0.8, 0.4)', - '=BINOM.INV(10, 0.8, 0.5)', - '=BINOM.INV(10, 0.8, 0.6)', - '=BINOM.INV(10, 0.8, 0.7)', - '=BINOM.INV(10, 0.8, 0.8)', - '=BINOM.INV(10, 0.8, 0.9)', - '=BINOM.INV(10, 0.8, 0.999)'], - ]) - - expect(engine.getSheetValues(0)).toEqual([[4, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10]]) - }) - - it('should work, small number of trials', () => { - const engine = HyperFormula.buildFromArray([ - ['=BINOM.INV(0, 0.8, 0.001)', - '=BINOM.INV(0, 0.8, 0.1)', - '=BINOM.INV(0, 0.8, 0.2)', - '=BINOM.INV(0, 0.8, 0.3)', - '=BINOM.INV(0, 0.8, 0.4)', - '=BINOM.INV(0, 0.8, 0.5)', - '=BINOM.INV(0, 0.8, 0.6)', - '=BINOM.INV(0, 0.8, 0.7)', - '=BINOM.INV(0, 0.8, 0.8)', - '=BINOM.INV(0, 0.8, 0.9)', - '=BINOM.INV(0, 0.8, 0.999)'], - ]) - - expect(engine.getSheetValues(0)).toEqual([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) - }) - - it('should work, another small number of trials', () => { - const engine = HyperFormula.buildFromArray([ - ['=BINOM.INV(1, 0.8, 0.001)', - '=BINOM.INV(1, 0.8, 0.1)', - '=BINOM.INV(1, 0.8, 0.2)', - '=BINOM.INV(1, 0.8, 0.3)', - '=BINOM.INV(1, 0.8, 0.4)', - '=BINOM.INV(1, 0.8, 0.5)', - '=BINOM.INV(1, 0.8, 0.6)', - '=BINOM.INV(1, 0.8, 0.7)', - '=BINOM.INV(1, 0.8, 0.8)', - '=BINOM.INV(1, 0.8, 0.9)', - '=BINOM.INV(1, 0.8, 0.999)'], - ]) - - //both products #1 and #2 return 1 for '=BINOM.INV(1, 0.8, 0.2)', which is incorrect - expect(engine.getSheetValues(0)).toEqual([[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]]) - }) - - it('should work, large number of trials', () => { - const engine = HyperFormula.buildFromArray([ - ['=BINOM.INV(1000, 0.8, 0.001)', - '=BINOM.INV(1000, 0.8, 0.1)', - '=BINOM.INV(1000, 0.8, 0.2)', - '=BINOM.INV(1000, 0.8, 0.3)', - '=BINOM.INV(1000, 0.8, 0.4)', - '=BINOM.INV(1000, 0.8, 0.5)', - '=BINOM.INV(1000, 0.8, 0.6)', - '=BINOM.INV(1000, 0.8, 0.7)', - '=BINOM.INV(1000, 0.8, 0.8)', - '=BINOM.INV(1000, 0.8, 0.9)', - '=BINOM.INV(1000, 0.8, 0.999)'], - ]) - - expect(engine.getSheetValues(0)).toEqual([[760, 784, 789, 793, 797, 800, 803, 807, 811, 816, 838]]) - }) - - it('truncation works', () => { - const engine = HyperFormula.buildFromArray([ - ['=BINOM.INV(1000.1, 0.8, 0.001)', - '=BINOM.INV(1000.2, 0.8, 0.1)', - '=BINOM.INV(1000.3, 0.8, 0.2)', - '=BINOM.INV(1000.4, 0.8, 0.3)', - '=BINOM.INV(1000.5, 0.8, 0.4)', - '=BINOM.INV(1000.6, 0.8, 0.5)', - '=BINOM.INV(1000.7, 0.8, 0.6)', - '=BINOM.INV(1000.8, 0.8, 0.7)', - '=BINOM.INV(1000.9, 0.8, 0.8)', - '=BINOM.INV(1000.99, 0.8, 0.9)', - '=BINOM.INV(1000.999, 0.8, 0.999)'], - ]) - - expect(engine.getSheetValues(0)).toEqual([[760, 784, 789, 793, 797, 800, 803, 807, 811, 816, 838]]) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=BINOM.INV(0, 0.5, 0.5)'], - ['=BINOM.INV(-0.001, 0.5, 0.5)'], - ['=BINOM.INV(10, 0, 0.5)'], - ['=BINOM.INV(10, 1, 0.5)'], - ['=BINOM.INV(10, -0.001, 0.5)'], - ['=BINOM.INV(10, 1.001, 0.5)'], - ['=BINOM.INV(10, 0.5, 0)'], - ['=BINOM.INV(10, 0.5, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - //product #1 returns 0 for the following test - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - //both products #1 and #2 return NUM for '=BINOM.INV(10, 0, 0.5)', which is incorrect - expect(engine.getCellValue(adr('A3'))).toEqual(0) - //both products #1 and #2 return NUM for '=BINOM.INV(10, 1, 0.5)', which is incorrect - expect(engine.getCellValue(adr('A4'))).toEqual(10) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A7'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A8'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-bitand.spec.ts b/test/unit/interpreter/function-bitand.spec.ts deleted file mode 100644 index 77daf61d6c..0000000000 --- a/test/unit/interpreter/function-bitand.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function BITAND', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITAND(101)'], - ['=BITAND(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITAND(1, "foo")'], - ['=BITAND("bar", 4)'], - ['=BITAND("foo", "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should not work for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITAND(1, -2)'], - ['=BITAND(-1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('should not work for non-integers', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITAND(1.2, 2)'], - ['=BITAND(3.14, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.IntegerExpected)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.IntegerExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITAND(1, 5)'], - ['=BITAND(457, 111)'], - ['=BITAND(BIN2DEC(101), BIN2DEC(1))'], - ['=BITAND(256, 123)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(73) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(0) - }) - - it('should return numeric type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITAND(1, 5)'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toEqual(CellValueType.NUMBER) - }) -}) diff --git a/test/unit/interpreter/function-bitlshift.spec.ts b/test/unit/interpreter/function-bitlshift.spec.ts deleted file mode 100644 index f5e29b4860..0000000000 --- a/test/unit/interpreter/function-bitlshift.spec.ts +++ /dev/null @@ -1,95 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function BITLSHIFT', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITLSHIFT(101)'], - ['=BITLSHIFT(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITLSHIFT(1, "foo")'], - ['=BITLSHIFT("bar", 4)'], - ['=BITLSHIFT("foo", "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should not work for negative value', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITLSHIFT(-5, -2)'], - ['=BITLSHIFT(-1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('should work for positive positions', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITLSHIFT(0, 0)'], - ['=BITLSHIFT(0, 2)'], - ['=BITLSHIFT(2, 2)'], - ['=BITLSHIFT(123, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(8) - expect(engine.getCellValue(adr('A4'))).toEqual(984) - }) - - it('should work for negative positions', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITLSHIFT(0, -2)', '=BITRSHIFT(0, 2)'], - ['=BITLSHIFT(2, -5)', '=BITRSHIFT(2, 5)'], - ['=BITLSHIFT(123, -2)', '=BITRSHIFT(123, 2)'], - ['=BITLSHIFT(4786, -3)', '=BITRSHIFT(4786, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(30) - expect(engine.getCellValue(adr('A4'))).toEqual(598) - - expect(engine.getCellValue(adr('A1'))).toEqual(engine.getCellValue(adr('B1'))) - expect(engine.getCellValue(adr('A2'))).toEqual(engine.getCellValue(adr('B2'))) - expect(engine.getCellValue(adr('A3'))).toEqual(engine.getCellValue(adr('B3'))) - expect(engine.getCellValue(adr('A4'))).toEqual(engine.getCellValue(adr('B4'))) - }) - - it('works only for 48 bit results', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITLSHIFT(2, 46)'], - ['=BITLSHIFT(2, 47)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(140737488355328, -4) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.BitshiftLong)) - }) - - it('works only for positions from -53 to 53', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITLSHIFT(0, -54)'], - ['=BITLSHIFT(0, -53)'], - ['=BITLSHIFT(0, 53)'], - ['=BITLSHIFT(0, 54)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(0) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-bitor.spec.ts b/test/unit/interpreter/function-bitor.spec.ts deleted file mode 100644 index d8b5cfb770..0000000000 --- a/test/unit/interpreter/function-bitor.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function BITOR', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITOR(101)'], - ['=BITOR(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITOR(1, "foo")'], - ['=BITOR("bar", 4)'], - ['=BITOR("foo", "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should not work for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITOR(1, -2)'], - ['=BITOR(-1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('should not work for non-integers', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITOR(1.2, 2)'], - ['=BITOR(3.14, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.IntegerExpected)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.IntegerExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITOR(1, 5)'], - ['=BITOR(457, 111)'], - ['=BITOR(BIN2DEC(101), BIN2DEC(1))'], - ['=BITOR(256, 123)'], - ['=BITOR(0, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(5) - expect(engine.getCellValue(adr('A2'))).toEqual(495) - expect(engine.getCellValue(adr('A3'))).toEqual(5) - expect(engine.getCellValue(adr('A4'))).toEqual(379) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - }) - - it('should return numeric type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITOR(1, 5)'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toEqual(CellValueType.NUMBER) - }) -}) diff --git a/test/unit/interpreter/function-bitrshift.spec.ts b/test/unit/interpreter/function-bitrshift.spec.ts deleted file mode 100644 index 77602fc6fb..0000000000 --- a/test/unit/interpreter/function-bitrshift.spec.ts +++ /dev/null @@ -1,95 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function BITRSHIFT', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITRSHIFT(101)'], - ['=BITRSHIFT(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITRSHIFT(1, "foo")'], - ['=BITRSHIFT("bar", 4)'], - ['=BITRSHIFT("foo", "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should not work for negative value', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITRSHIFT(-5, -2)'], - ['=BITRSHIFT(-1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('should work for positive positions', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITRSHIFT(0, 0)'], - ['=BITRSHIFT(0, 2)'], - ['=BITRSHIFT(50, 2)'], - ['=BITRSHIFT(123, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(12) - expect(engine.getCellValue(adr('A4'))).toEqual(15) - }) - - it('should work for negative positions', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITRSHIFT(0, -2)', '=BITLSHIFT(0, 2)'], - ['=BITRSHIFT(2, -5)', '=BITLSHIFT(2, 5)'], - ['=BITRSHIFT(123, -2)', '=BITLSHIFT(123, 2)'], - ['=BITRSHIFT(4786, -3)', '=BITLSHIFT(4786, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(64) - expect(engine.getCellValue(adr('A3'))).toEqual(492) - expect(engine.getCellValue(adr('A4'))).toEqual(38288) - - expect(engine.getCellValue(adr('A1'))).toEqual(engine.getCellValue(adr('B1'))) - expect(engine.getCellValue(adr('A2'))).toEqual(engine.getCellValue(adr('B2'))) - expect(engine.getCellValue(adr('A3'))).toEqual(engine.getCellValue(adr('B3'))) - expect(engine.getCellValue(adr('A4'))).toEqual(engine.getCellValue(adr('B4'))) - }) - - it('works only for 48 bit results', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITRSHIFT(2, -46)'], - ['=BITRSHIFT(2, -47)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(140737488355328, -4) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.BitshiftLong)) - }) - - it('works only for positions from -53 to 53', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITRSHIFT(0, -54)'], - ['=BITRSHIFT(0, -53)'], - ['=BITRSHIFT(0, 53)'], - ['=BITRSHIFT(0, 54)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(0) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-bitxor.spec.ts b/test/unit/interpreter/function-bitxor.spec.ts deleted file mode 100644 index 522e7fc203..0000000000 --- a/test/unit/interpreter/function-bitxor.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function BITXOR', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITXOR(101)'], - ['=BITXOR(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITXOR(1, "foo")'], - ['=BITXOR("bar", 4)'], - ['=BITXOR("foo", "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should not work for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITXOR(1, -2)'], - ['=BITXOR(-1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('should not work for non-integers', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITXOR(1.2, 2)'], - ['=BITXOR(3.14, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.IntegerExpected)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.IntegerExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITXOR(1, 5)'], - ['=BITXOR(457, 111)'], - ['=BITXOR(BIN2DEC(101), BIN2DEC(1))'], - ['=BITXOR(256, 123)'], - ['=BITXOR(0, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - expect(engine.getCellValue(adr('A2'))).toEqual(422) - expect(engine.getCellValue(adr('A3'))).toEqual(4) - expect(engine.getCellValue(adr('A4'))).toEqual(379) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - }) - - it('should return numeric type', () => { - const engine = HyperFormula.buildFromArray([ - ['=BITXOR(1, 5)'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toEqual(CellValueType.NUMBER) - }) -}) diff --git a/test/unit/interpreter/function-ceiling.math.spec.ts b/test/unit/interpreter/function-ceiling.math.spec.ts deleted file mode 100644 index 28ed71e984..0000000000 --- a/test/unit/interpreter/function-ceiling.math.spec.ts +++ /dev/null @@ -1,88 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CEILING.MATH', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CEILING.MATH()'], - ['=CEILING.MATH(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=CEILING.MATH("foo")'], - ['=CEILING.MATH(1, "bar")'], - ['=CEILING.MATH(1, 2, "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=CEILING.MATH(4.43, 0.3)'], - ['=CEILING.MATH(4.43, 0.6)'], - ['=CEILING.MATH(4.43, 2)'], - ['=CEILING.MATH(4.43)'], - ['=CEILING.MATH(-4.43)'], - ['=CEILING.MATH(-3.14, -1.8)'], - ['=CEILING.MATH(-3.14, 0)'], - ['=CEILING.MATH(3.14, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4.5) - expect(engine.getCellValue(adr('A2'))).toEqual(4.8) - expect(engine.getCellValue(adr('A3'))).toEqual(6) - expect(engine.getCellValue(adr('A4'))).toEqual(5) - expect(engine.getCellValue(adr('A5'))).toEqual(-4) - expect(engine.getCellValue(adr('A6'))).toEqual(-1.8) - expect(engine.getCellValue(adr('A7'))).toEqual(0) - expect(engine.getCellValue(adr('A8'))).toEqual(0) - }) - - it('should work with mode for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=CEILING.MATH(-11, -2)'], - ['=CEILING.MATH(-11, -2, 0)'], - ['=CEILING.MATH(-11, -2, 1)'], - ['=CEILING.MATH(-11, 0, 1)'], - ['=CEILING.MATH(-11, 0, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(-10) - expect(engine.getCellValue(adr('A2'))).toEqual(-10) - expect(engine.getCellValue(adr('A3'))).toEqual(-12) - expect(engine.getCellValue(adr('A4'))).toEqual(0) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - }) - - it('negative values', () => { - const engine = HyperFormula.buildFromArray([ - ['=CEILING.MATH(11, 2, 0)'], - ['=CEILING.MATH(-11, 2, 0)'], - ['=CEILING.MATH(11, -2, 0)'], - ['=CEILING.MATH(-11, -2, 0)'], - ['=CEILING.MATH(11, 2, 1)'], - ['=CEILING.MATH(-11, 2, 1)'], - ['=CEILING.MATH(11, -2, 1)'], - ['=CEILING.MATH(-11, -2, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(12) - expect(engine.getCellValue(adr('A2'))).toEqual(-10) - expect(engine.getCellValue(adr('A3'))).toEqual(12) - expect(engine.getCellValue(adr('A4'))).toEqual(-10) - expect(engine.getCellValue(adr('A5'))).toEqual(12) - expect(engine.getCellValue(adr('A6'))).toEqual(-12) - expect(engine.getCellValue(adr('A7'))).toEqual(12) - expect(engine.getCellValue(adr('A8'))).toEqual(-12) - }) -}) diff --git a/test/unit/interpreter/function-ceiling.precise.spec.ts b/test/unit/interpreter/function-ceiling.precise.spec.ts deleted file mode 100644 index 21fa16fe0b..0000000000 --- a/test/unit/interpreter/function-ceiling.precise.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CEILING.PRECISE', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CEILING.PRECISE()'], - ['=CEILING.PRECISE(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=CEILING.PRECISE(1, "bar")'], - ['=CEILING.PRECISE("bar", 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=CEILING.PRECISE(4.43, 0.3)'], - ['=CEILING.PRECISE(4.43, 0.6)'], - ['=CEILING.PRECISE(4.43, 2)'], - ['=CEILING.PRECISE(-3.14, -1.8)'], - ['=CEILING.PRECISE(-3.14, 0)'], - ['=CEILING.PRECISE(3.14, 0)'], - ['=CEILING.PRECISE(3.14)'], - ['=CEILING.PRECISE(-3.14)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4.5) - expect(engine.getCellValue(adr('A2'))).toEqual(4.8) - expect(engine.getCellValue(adr('A3'))).toEqual(6) - expect(engine.getCellValue(adr('A4'))).toEqual(-1.8) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - expect(engine.getCellValue(adr('A6'))).toEqual(0) - expect(engine.getCellValue(adr('A7'))).toEqual(4) - expect(engine.getCellValue(adr('A8'))).toEqual(-3) - }) - - it('negative values', () => { - const engine = HyperFormula.buildFromArray([ - ['=CEILING.PRECISE(11, 2)'], - ['=CEILING.PRECISE(-11, 2)'], - ['=CEILING.PRECISE(11, -2)'], - ['=CEILING.PRECISE(-11, -2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(12) - expect(engine.getCellValue(adr('A2'))).toEqual(-10) - expect(engine.getCellValue(adr('A3'))).toEqual(12) - expect(engine.getCellValue(adr('A4'))).toEqual(-10) - }) -}) diff --git a/test/unit/interpreter/function-ceiling.spec.ts b/test/unit/interpreter/function-ceiling.spec.ts deleted file mode 100644 index b0e9ea174a..0000000000 --- a/test/unit/interpreter/function-ceiling.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CEILING', () => { - /*Inconsistent with ODFF standard.*/ - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CEILING(1)'], - ['=CEILING(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=CEILING(1, "bar")'], - ['=CEILING("bar", 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=CEILING(4.43, 0.3)'], - ['=CEILING(4.43, 0.6)'], - ['=CEILING(4.43, 2)'], - ['=CEILING(-3.14, -1.8)'], - ['=CEILING(-3.14, 0)'], - ['=CEILING(3.14, 0)'], - ['=CEILING(0, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4.5) - expect(engine.getCellValue(adr('A2'))).toEqual(4.8) - expect(engine.getCellValue(adr('A3'))).toEqual(6) - expect(engine.getCellValue(adr('A4'))).toEqual(-3.6) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A7'))).toEqual(0) - }) - - /*Inconsistent with ODFF standard.*/ - it('negative values', () => { - const engine = HyperFormula.buildFromArray([ - ['=CEILING(11, 2)'], - ['=CEILING(-11, 2)'], - ['=CEILING(11, -2)'], - ['=CEILING(-11, -2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(12) - expect(engine.getCellValue(adr('A2'))).toEqual(-10) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.DistinctSigns)) - expect(engine.getCellValue(adr('A4'))).toEqual(-12) - }) -}) diff --git a/test/unit/interpreter/function-char.spec.ts b/test/unit/interpreter/function-char.spec.ts deleted file mode 100644 index e947f18f6d..0000000000 --- a/test/unit/interpreter/function-char.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CHAR', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHAR()'], - ['=CHAR(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for wrong type of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHAR("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHAR(1)'], - ['=CHAR(33)'], - ['=CHAR(65)'], - ['=CHAR(90)'], - ['=CHAR(209)'], - ['=CHAR(255)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('') - expect(engine.getCellValue(adr('A2'))).toEqual('!') - expect(engine.getCellValue(adr('A3'))).toEqual('A') - expect(engine.getCellValue(adr('A4'))).toEqual('Z') - expect(engine.getCellValue(adr('A5'))).toEqual('Ñ') - expect(engine.getCellValue(adr('A6'))).toEqual('ÿ') - }) - - it('should round down floats', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHAR(42)'], - ['=CHAR(42.2)'], - ['=CHAR(42.8)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('*') - expect(engine.getCellValue(adr('A2'))).toEqual('*') - expect(engine.getCellValue(adr('A3'))).toEqual('*') - }) - - it('should work only for values from 1 to 255 truncating decimal part', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHAR(0)'], - ['=CHAR(1)'], - ['=CHAR(255)'], - ['=CHAR(256)'], - ['=CHAR(0.5)'], - ['=CHAR(255.5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.CharacterCodeBounds)) - expect(engine.getCellValue(adr('A2'))).toEqual('') - expect(engine.getCellValue(adr('A3'))).toEqual('ÿ') - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.CharacterCodeBounds)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.CharacterCodeBounds)) - expect(engine.getCellValue(adr('A6'))).toEqual('ÿ') - }) -}) diff --git a/test/unit/interpreter/function-chisq.dist.rt.spec.ts b/test/unit/interpreter/function-chisq.dist.rt.spec.ts deleted file mode 100644 index a781782f83..0000000000 --- a/test/unit/interpreter/function-chisq.dist.rt.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CHISQ.DIST.RT', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.DIST.RT(1)'], - ['=CHISQ.DIST.RT(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.DIST.RT("foo", 2)'], - ['=CHISQ.DIST.RT(1, "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.DIST.RT(1, 1)'], - ['=CHISQ.DIST.RT(3, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.317310507862944, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.22313016014843, 6) - }) - - it('truncates second arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.DIST.RT(1, 1.9)'], - ['=CHISQ.DIST.RT(3, 2.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.317310507862944, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.22313016014843, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.DIST.RT(10, 0.999)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-chisq.dist.spec.ts b/test/unit/interpreter/function-chisq.dist.spec.ts deleted file mode 100644 index 12fe8990f9..0000000000 --- a/test/unit/interpreter/function-chisq.dist.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CHISQ.DIST', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.DIST(1, 2)'], - ['=CHISQ.DIST(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.DIST("foo", 2, TRUE())'], - ['=CHISQ.DIST(1, "baz", TRUE())'], - ['=CHISQ.DIST(1, 2, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.DIST(1, 1, TRUE())'], - ['=CHISQ.DIST(3, 2, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.682689492137056, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.77686983985157, 6) - }) - - it('should work as pdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.DIST(1, 1, FALSE())'], - ['=CHISQ.DIST(3, 2, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.241970724519133, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.111565080074215, 6) - }) - - it('truncates second arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.DIST(1, 1.9, FALSE())'], - ['=CHISQ.DIST(3, 2.9, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.241970724519133, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.111565080074215, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.DIST(10, 0.999, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-chisq.inv.rt.spec.ts b/test/unit/interpreter/function-chisq.inv.rt.spec.ts deleted file mode 100644 index f320fa1f53..0000000000 --- a/test/unit/interpreter/function-chisq.inv.rt.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CHISQ.INV.RT', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.INV.RT(1)'], - ['=CHISQ.INV.RT(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.INV.RT("foo", 2)'], - ['=CHISQ.INV.RT(1, "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.INV.RT(0.1, 1)'], - ['=CHISQ.INV.RT(0.9, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(2.70554345409603, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.210721031315653, 6) - }) - - it('truncates second arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.INV.RT(0.1, 1.9)'], - ['=CHISQ.INV.RT(0.9, 2.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(2.70554345409603, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.210721031315653, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.INV.RT(0.5, 0.999)'], - ['=CHISQ.INV.RT(-0.0001, 2)'], - ['=CHISQ.INV.RT(1.0001, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-chisq.inv.spec.ts b/test/unit/interpreter/function-chisq.inv.spec.ts deleted file mode 100644 index 0fdfccb577..0000000000 --- a/test/unit/interpreter/function-chisq.inv.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CHISQ.INV', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.INV(1)'], - ['=CHISQ.INV(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.INV("foo", 2)'], - ['=CHISQ.INV(1, "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.INV(0.1, 1)'], - ['=CHISQ.INV(0.9, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0157907740934326, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(4.60517018598809, 6) - }) - - it('truncates second arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.INV(0.1, 1.9)'], - ['=CHISQ.INV(0.9, 2.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0157907740934326, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(4.60517018598809, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.INV(0.5, 0.999)'], - ['=CHISQ.INV(-0.0001, 2)'], - ['=CHISQ.INV(1.0001, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-chisq.test.spec.ts b/test/unit/interpreter/function-chisq.test.spec.ts deleted file mode 100644 index fc41402b63..0000000000 --- a/test/unit/interpreter/function-chisq.test.spec.ts +++ /dev/null @@ -1,107 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('CHISQ.TEST', () => { - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.TEST(1)'], - ['=CHISQ.TEST(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10], - [2, 5], - ['=CHISQ.TEST(A1:A2, B1:B2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.001652787719, 6) - }) - - it('works for larger ranges', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10, 1, 1, 3, 7], - [2, 5, 1, 1, 4, 8], - ['=CHISQ.TEST(A1:C2, D1:F2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.00000054330486, 9) - }) - - /** - * product #2 accepts this as a valid input - */ - it('validates dimensions', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10, 1, 1, 3, 7], - [2, 5, 1, 1, 4, 8], - ['=CHISQ.TEST(A1:C2, A1:F1)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.EqualLength)) - }) - - it('validates values #1', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10, 1, 1, 3, 7], - [2, 5, 1, 1, 4, 0], - ['=CHISQ.TEST(A1:C2, D1:F2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('accepts negative values', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10, 1, 1, 3, 7], - [2, 5, 1, 1, 4, -1], - ['=CHISQ.TEST(A1:C2, D1:F2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.0000858340104264999, 9) - }) - - it('but checks intermediate values for negatives', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10, 1, 1, 3, 7], - [2, 5, 1, 1, 4, -0.001], - ['=CHISQ.TEST(A1:C2, D1:F2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('doesnt do coercions, nonnumeric values are skipped', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10, 1, 1, 3, 7], - [2, 5, null, 1, 4, 8], - ['=CHISQ.TEST(A1:C2, D1:F2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.00001161637011, 9) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['=NA()', '50'], - ['3', '30'], - ['=CHISQ.TEST(A1:A3, B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('error when not enough data', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHISQ.TEST(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.TwoValues)) - }) -}) diff --git a/test/unit/interpreter/function-choose.spec.ts b/test/unit/interpreter/function-choose.spec.ts deleted file mode 100644 index 931e6796c7..0000000000 --- a/test/unit/interpreter/function-choose.spec.ts +++ /dev/null @@ -1,77 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueDetailedType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Interpreter - CHOOSE function', () => { - it('Should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHOOSE(0)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('Should work with more arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHOOSE(1, 2, 3)', '=CHOOSE(3, 2, 3, 4)', '=CHOOSE(2, 2, 3, 4, 5)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(engine.getCellValue(adr('B1'))).toEqual(4) - expect(engine.getCellValue(adr('C1'))).toEqual(3) - }) - - it('should preserve types', () => { - const engine = HyperFormula.buildFromArray([['=CHOOSE(1, B1, 3)', '1%']]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - }) - - it('preserves types of second arg', () => { - const engine = HyperFormula.buildFromArray([['=IFERROR(NA(), B1)', '1%']]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - }) - - it('Should fail when wrong first argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHOOSE(1.5, 2, 3)', '=CHOOSE(0, 2, 3, 4)', '=CHOOSE(5, 2, 3, 4, 5)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.IntegerExpected)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.Selector)) - }) - - it('Coercions', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHOOSE(TRUE(), 2, 3)', '=CHOOSE("31/12/1899", 2, 3, 4)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(engine.getCellValue(adr('B1'))).toEqual(2) - }) - - it('Should fail with error in first argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHOOSE(1/0, 3, 4, 5)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - it('Should not fail with error in other arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHOOSE(4, 1/0, 3, 4, 5)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(5) - }) - it('Should pass errors as normal values', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHOOSE(4, 2, 3, 4, 1/0)', '=CHOOSE(1, 2, 3, 4, COS(1, 1), 5)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('B1'))).toEqual(2) - }) - it('Should fail with range', () => { - const engine = HyperFormula.buildFromArray([ - ['=CHOOSE(1, 2, A2:A3, 4, 5)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-clean.spec.ts b/test/unit/interpreter/function-clean.spec.ts deleted file mode 100644 index 06e4d6bcdc..0000000000 --- a/test/unit/interpreter/function-clean.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CLEAN', () => { - it('should return N/A when number of arguments is incorrect', () => { - const engine = HyperFormula.buildFromArray([ - ['=CLEAN()'], - ['=CLEAN("foo", "bar")'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=CLEAN("foo\u0000")'], - ['=CLEAN("foo\u0020")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('foo') - expect(engine.getCellValue(adr('A2'))).toEqual('foo\u0020') - }) - - it('should clean all non-printable ASCII characters', () => { - const str = Array.from(Array(32).keys()).map(code => String.fromCharCode(code)).join('') - - const engine = HyperFormula.buildFromArray([ - [str, '=LEN(A1)', '=CLEAN(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(32) - expect(engine.getCellValue(adr('C1'))).toEqual('') - }) - - it('should coerce other types to string', () => { - const engine = HyperFormula.buildFromArray([ - ['=CLEAN(1)'], - ['=CLEAN(5+5)'], - ['=CLEAN(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - expect(engine.getCellValue(adr('A2'))).toEqual('10') - expect(engine.getCellValue(adr('A3'))).toEqual('TRUE') - }) -}) diff --git a/test/unit/interpreter/function-code.spec.ts b/test/unit/interpreter/function-code.spec.ts deleted file mode 100644 index 53ce41fc9d..0000000000 --- a/test/unit/interpreter/function-code.spec.ts +++ /dev/null @@ -1,75 +0,0 @@ -import {CellValueType, ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CODE', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CODE()'], - ['=CODE("foo", "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for empty strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=CODE("")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EmptyString)) - }) - - it('should work for single chars', () => { - const engine = HyperFormula.buildFromArray([ - ['=CODE("")'], - ['=CODE("!")'], - ['=CODE("A")'], - ['=CODE("Z")'], - ['=CODE("Ñ")'], - ['=CODE("ÿ")'], - ['=CODE(TRUE())'], - ['=CODE("€")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(33) - expect(engine.getCellValue(adr('A3'))).toEqual(65) - expect(engine.getCellValue(adr('A4'))).toEqual(90) - expect(engine.getCellValue(adr('A5'))).toEqual(209) - expect(engine.getCellValue(adr('A6'))).toEqual(255) - expect(engine.getCellValue(adr('A7'))).toEqual(84) - expect(engine.getCellValue(adr('A8'))).toEqual(8364) - }) - - it('should return code of first character', () => { - const engine = HyperFormula.buildFromArray([ - ['=CODE("Abar")'], - ['=CODE("Ñbaz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(65) - expect(engine.getCellValue(adr('A2'))).toEqual(209) - }) - - it('should return number', () => { - const engine = HyperFormula.buildFromArray([ - ['=CODE("foo")'] - ]) - - expect(engine.getCellValueType(adr('A1'))).toEqual(CellValueType.NUMBER) - }) - - it('should be identity when composed with CHAR', () => { - const engine = HyperFormula.buildFromArray([ - ['=CODE(CHAR(1))'], - ['=CODE(CHAR(128))'], - ['=CODE(CHAR(255))'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(128) - expect(engine.getCellValue(adr('A3'))).toEqual(255) - }) -}) diff --git a/test/unit/interpreter/function-column.spec.ts b/test/unit/interpreter/function-column.spec.ts deleted file mode 100644 index a207b96da9..0000000000 --- a/test/unit/interpreter/function-column.spec.ts +++ /dev/null @@ -1,117 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function COLUMN', () => { - it('should take one or zero arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=COLUMN(B1, B2)'], - ['=COLUMN(B1, B2, B3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should take only reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=COLUMN(42)'], - ['=COLUMN("foo")'], - ['=COLUMN(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.CellRefExpected)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.CellRefExpected)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.CellRefExpected)) - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=COLUMN(1/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should return row of a reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=COLUMN(A2)'], - ['=COLUMN(G7)'], - ['=COLUMN($E5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(7) - expect(engine.getCellValue(adr('A3'))).toEqual(5) - }) - - it('should work for itself', () => { - const engine = HyperFormula.buildFromArray([ - ['=COLUMN(A1)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('should return row of a cell in which formula is', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=COLUMN()'], - ['=COLUMN()'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(2) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - }) - - it('should return row of range start', () => { - const engine = HyperFormula.buildFromArray([ - ['=COLUMN(C1:D1)'], - ['=COLUMN(A1:B1)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - }) - - it('should be dependent on sheet structure changes', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['=COLUMN(A1)'] - ]) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - - engine.addColumns(0, [0, 1]) - - expect(engine.getCellValue(adr('B2'))).toEqual(2) - }) - - it('should collect dependencies of inner function and return argument type error', () => { - const engine = HyperFormula.buildFromArray([ - ['=SIN(1)'], - ['=COLUMN(SUM(A1,A3))'], - ['=SIN(1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.CellRefExpected)) - }) - - it('should propagate error of inner function', () => { - const engine = HyperFormula.buildFromArray([ - ['=1/0'], - ['=COLUMN(SUM(A1, A3))'], - ['=1/0'] - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should return #CYCLE! when cyclic reference occurs not directly in COLUMN', () => { - const engine = HyperFormula.buildFromArray([ - ['=COLUMN(SUM(A1))'], - ['=COLUMN(A1+A2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.CYCLE)) - }) -}) diff --git a/test/unit/interpreter/function-columns.spec.ts b/test/unit/interpreter/function-columns.spec.ts deleted file mode 100644 index 96e9388c0e..0000000000 --- a/test/unit/interpreter/function-columns.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function COLUMNS', () => { - it('accepts exactly one argument', () => { - const engine = HyperFormula.buildFromArray([['=COLUMNS()', '=COLUMNS(A1:B1, A2:B2)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works for range', () => { - const engine = HyperFormula.buildFromArray([['=COLUMNS(A1:C2)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('works for column range', () => { - const engine = HyperFormula.buildFromArray([['=COLUMNS(A:C)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('works for row range', () => { - const engine = HyperFormula.buildFromArray([['=COLUMNS(1:2)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(engine.getConfig().maxColumns) - }) - - it('works for array', () => { - const engine = HyperFormula.buildFromArray([['=COLUMNS({1,2,3})']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('works with cell reference', () => { - const engine = HyperFormula.buildFromArray([['=COLUMNS(A1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('error when nested cycle', () => { - const engine = HyperFormula.buildFromArray([['=COLUMNS(A1+1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.CYCLE)) - }) - - it('propagates only direct errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/0'], - ['=COLUMNS(4/0)'], - ['=COLUMNS(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - }) - - it('works with formulas', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1'], - ['1', '1'], - ['=COLUMNS(MMULT(A1:B2, A1:B2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(2) - }) - - it('should work when adding column', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1'], - ['=COLUMNS(A1:B1)'] - ]) - - engine.addColumns(0, [1, 1]) - - expect(engine.getCellValue(adr('A2'))).toEqual(3) - }) -}) diff --git a/test/unit/interpreter/function-combin.spec.ts b/test/unit/interpreter/function-combin.spec.ts deleted file mode 100644 index 92e02f439d..0000000000 --- a/test/unit/interpreter/function-combin.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function COMBIN', () => { - it('checks number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMBIN(1)', '=COMBIN(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMBIN(0,0)'], - ['=COMBIN(1,0)'], - ['=COMBIN(4,2)'], - ['=COMBIN(9,6)'], - ['=COMBIN(20,10)'], - ['=COMBIN(30,10)'], - ['=COMBIN(40,10)'], - ['=COMBIN(100,99)'], - ['=COMBIN(100,8)'], - ['=COMBIN(1029,512)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('A2'))).toBe(1) - expect(engine.getCellValue(adr('A3'))).toBe(6) - expect(engine.getCellValue(adr('A4'))).toBe(84) - expect(engine.getCellValue(adr('A5'))).toBe(184756) - expect(engine.getCellValue(adr('A6'))).toBe(30045015) - expect(engine.getCellValue(adr('A7'))).toBe(847660528) - expect(engine.getCellValue(adr('A8'))).toBe(100) - expect(engine.getCellValue(adr('A9'))).toBe(186087894300) - expect(engine.getCellValue(adr('A10')) as number / 1.41325918108873e+308).toBeCloseTo(1, 6) - }) - - it('truncates argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMBIN(9.9,6.6)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(84) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMBIN(1.1, 1.2)'], - ['=COMBIN(1, 2)'], - ['=COMBIN(2, -1)'], - ['=COMBIN(-1, -1)'], - ['=COMBIN(1030, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.WrongOrder)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.WrongOrder)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - //inconsistency with product #2 - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('uses coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMBIN(TRUE(),"0")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - }) - - it('propagates error', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMBIN(NA(), NA())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-combina.spec.ts b/test/unit/interpreter/function-combina.spec.ts deleted file mode 100644 index 9522e55b9b..0000000000 --- a/test/unit/interpreter/function-combina.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function COMBINA', () => { - it('checks number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMBINA(1)', '=COMBINA(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMBINA(0,0)'], - ['=COMBINA(1,0)'], - ['=COMBINA(2,2)'], - ['=COMBINA(0,2)'], - ['=COMBINA(10,10)'], - ['=COMBINA(20,10)'], - ['=COMBINA(30,10)'], - ['=COMBINA(100,500)'], - ['=COMBINA(100,8)'], - ['=COMBINA(518,512)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('A2'))).toBe(1) - expect(engine.getCellValue(adr('A3'))).toBe(3) - expect(engine.getCellValue(adr('A4'))).toBe(1) - expect(engine.getCellValue(adr('A5'))).toBe(92378) - expect(engine.getCellValue(adr('A6'))).toBe(20030010) - expect(engine.getCellValue(adr('A7'))).toBe(635745396) - expect(engine.getCellValue(adr('A8')) as number / 1.8523520317769801e+115).toBeCloseTo(1, 6) - expect(engine.getCellValue(adr('A9'))).toBeCloseTo(325949656825, -2) - expect(engine.getCellValue(adr('A10')) as number / 1.41325918108873e+308).toBeCloseTo(1, 6) - }) - - it('truncates argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMBINA(9.9,6.6)'], - ['=COMBINA(518, 512.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(3003) - expect(engine.getCellValue(adr('A2')) as number / 1.41325918108873e+308).toBeCloseTo(1) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMBINA(2, -1)'], - ['=COMBINA(-1, 2)'], - ['=COMBINA(1031, 0)'], - ['=COMBINA(518, 513)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('uses coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMBINA(TRUE(),"0")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - }) - - it('propagates error', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMBINA(NA(), NA())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-complex.spec.ts b/test/unit/interpreter/function-complex.spec.ts deleted file mode 100644 index 12dd20770d..0000000000 --- a/test/unit/interpreter/function-complex.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function COMPLEX', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMPLEX(1)'], - ['=COMPLEX(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMPLEX("foo", 2)'], - ['=COMPLEX(1, "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMPLEX(0, 0)'], - ['=COMPLEX(0, 1)'], - ['=COMPLEX(0, -1)'], - ['=COMPLEX(0, 2)'], - ['=COMPLEX(0, -2)'], - ['=COMPLEX(1, 0)'], - ['=COMPLEX(1, 1)'], - ['=COMPLEX(1, -1)'], - ['=COMPLEX(1, 2)'], - ['=COMPLEX(1, -2)'], - ['=COMPLEX(-1, 0)'], - ['=COMPLEX(-1, 1)'], - ['=COMPLEX(-1, -1)'], - ['=COMPLEX(-1, 2)'], - ['=COMPLEX(-1, -2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('0') - expect(engine.getCellValue(adr('A2'))).toEqual('i') - expect(engine.getCellValue(adr('A3'))).toEqual('-i') - expect(engine.getCellValue(adr('A4'))).toEqual('2i') - expect(engine.getCellValue(adr('A5'))).toEqual('-2i') - expect(engine.getCellValue(adr('A6'))).toEqual('1') - expect(engine.getCellValue(adr('A7'))).toEqual('1+i') - expect(engine.getCellValue(adr('A8'))).toEqual('1-i') - expect(engine.getCellValue(adr('A9'))).toEqual('1+2i') - expect(engine.getCellValue(adr('A10'))).toEqual('1-2i') - expect(engine.getCellValue(adr('A11'))).toEqual('-1') - expect(engine.getCellValue(adr('A12'))).toEqual('-1+i') - expect(engine.getCellValue(adr('A13'))).toEqual('-1-i') - expect(engine.getCellValue(adr('A14'))).toEqual('-1+2i') - expect(engine.getCellValue(adr('A15'))).toEqual('-1-2i') - }) - - it('should work with third argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=COMPLEX(1, 1, "i")'], - ['=COMPLEX(1, 1, "j")'], - ['=COMPLEX(1, 1, "k")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1+i') - expect(engine.getCellValue(adr('A2'))).toEqual('1+j') - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ShouldBeIorJ)) - }) -}) diff --git a/test/unit/interpreter/function-concatenate.spec.ts b/test/unit/interpreter/function-concatenate.spec.ts deleted file mode 100644 index fbf8e9a68e..0000000000 --- a/test/unit/interpreter/function-concatenate.spec.ts +++ /dev/null @@ -1,63 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function CONCATENATE', () => { - it('validate arguments', () => { - const engine = HyperFormula.buildFromArray([['=CONCATENATE()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([['John', 'Smith', '=CONCATENATE(A1, B1)']]) - - expect(engine.getCellValue(adr('C1'))).toEqual('JohnSmith') - }) - - it('propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/0', '=FOOBAR()'], - ['=CONCATENATE(4/0)'], - ['=CONCATENATE(A1)'], - ['=CONCATENATE(A1,B1)'], - ['=CONCATENATE(A1:B1)'], - ['=CONCATENATE(C1,B1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - }) - - it('empty value is empty string', () => { - const engine = HyperFormula.buildFromArray([ - ['foo', '', 'bar', '=CONCATENATE(A1, B1, C1)'], - ]) - - expect(engine.getCellValue(adr('D1'))).toEqual('foobar') - }) - - it('supports range values', () => { - const engine = HyperFormula.buildFromArray([ - ['Topleft', 'Topright'], - ['Bottomleft', 'Bottomright'], - ['=CONCATENATE(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual('TopleftToprightBottomleftBottomright') - }) - - it('coerce to strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRUE()', '42', '=CONCATENATE(A1:B1)'], - ['=TRUE()', '=42%', '=CONCATENATE(A2:B2)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual('TRUE42') - expect(engine.getCellValue(adr('C2'))).toEqual('TRUE0.42') - }) -}) diff --git a/test/unit/interpreter/function-confidence.norm.spec.ts b/test/unit/interpreter/function-confidence.norm.spec.ts deleted file mode 100644 index 36a90c082e..0000000000 --- a/test/unit/interpreter/function-confidence.norm.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CONFIDENCE.NORM', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CONFIDENCE.NORM(1, 2)'], - ['=CONFIDENCE.NORM(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=CONFIDENCE.NORM("foo", 2, 3)'], - ['=CONFIDENCE.NORM(0.5, "baz", 3)'], - ['=CONFIDENCE.NORM(0.5, 2, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=CONFIDENCE.NORM(0.1, 1, 1)'], - ['=CONFIDENCE.NORM(0.9, 10, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.64485362695147, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.561974627424251, 6) - }) - - it('should truncate third argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=CONFIDENCE.NORM(0.1, 1, 1.9)'], - ['=CONFIDENCE.NORM(0.9, 10, 5.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.64485362695147, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.561974627424251, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=CONFIDENCE.NORM(0.01, 0.01, 1)'], - ['=CONFIDENCE.NORM(0, 0.01, 1)'], - ['=CONFIDENCE.NORM(0.01, 0, 1)'], - ['=CONFIDENCE.NORM(0.01, 0.1, 0.99)'], - ['=CONFIDENCE.NORM(0.99, 0.01, 1)'], - ['=CONFIDENCE.NORM(1, 0.01, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0257582930354889, 6) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(0.000125334695080692, 6) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-confidence.t.spec.ts b/test/unit/interpreter/function-confidence.t.spec.ts deleted file mode 100644 index 0cc83cac2a..0000000000 --- a/test/unit/interpreter/function-confidence.t.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CONFIDENCE.T', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CONFIDENCE.T(1, 2)'], - ['=CONFIDENCE.T(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=CONFIDENCE.T("foo", 2, 3)'], - ['=CONFIDENCE.T(0.5, "baz", 3)'], - ['=CONFIDENCE.T(0.5, 2, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=CONFIDENCE.T(0.1, 1, 2)'], - ['=CONFIDENCE.T(0.9, 10, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(4.46449651075278, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.59850759663214, 6) - }) - - it('should truncate third argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=CONFIDENCE.T(0.1, 1, 2.9)'], - ['=CONFIDENCE.T(0.9, 10, 5.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(4.46449651075278, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.59850759663214, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=CONFIDENCE.T(0.01, 0.01, 2)'], - ['=CONFIDENCE.T(0, 0.01, 2)'], - ['=CONFIDENCE.T(0.01, 0, 2)'], - ['=CONFIDENCE.T(0.01, 0.1, 1.99)'], - ['=CONFIDENCE.T(0.99, 0.01, 2)'], - ['=CONFIDENCE.T(1, 0.01, 2)'], - ['=CONFIDENCE.T(0.01, 0.1, 0.99)'], - ['=CONFIDENCE.T(0.01, 0.1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.450121133444994, 6) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(0.000111081209667629, 6) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A7'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A8'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-correl.spec.ts b/test/unit/interpreter/function-correl.spec.ts deleted file mode 100644 index b47b683256..0000000000 --- a/test/unit/interpreter/function-correl.spec.ts +++ /dev/null @@ -1,92 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('CORREL', () => { - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CORREL(B1:B5)'], - ['=CORREL(B1:B5, C1:C5, D1:D5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('ranges need to have same amount of elements', () => { - const engine = HyperFormula.buildFromArray([ - ['=CORREL(B1:B5, C1:C6)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.EqualLength)) - }) - - it('works (simple)', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['2', '20'], - ['=CORREL(A1:A2, B1:B2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBe(1) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['2', '4'], - ['5', '3'], - ['7', '6'], - ['1', '1'], - ['8', '5'], - ['=CORREL(A1:A5, B1:B5)'] - ]) - - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(0.7927032095) - }) - - it('error when not enough data', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['=CORREL(A1:A1, B1:B1)'], - ['=CORREL(42, 43)'], - ['=CORREL("foo", "bar")'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.TwoValues)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.TwoValues)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.TwoValues)) - }) - - it('doesnt do coercions, nonnumeric values are skipped', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['="2"', '50'], - ['3', '30'], - ['=CORREL(A1:A3, B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(1) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ['=CORREL(MMULT(A1:B2, A1:B2), MMULT(B1:C2, B1:C2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.999248091927219, 6) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['=4/0', '50'], - ['3', '30'], - ['=CORREL(A1:A3, B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-cos.spec.ts b/test/unit/interpreter/function-cos.spec.ts deleted file mode 100644 index b199115d06..0000000000 --- a/test/unit/interpreter/function-cos.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function COS', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=COS(0)', '=COS(7)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.753902254343305) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=COS("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=COS()', '=COS(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=COS(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.54030230586814) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=COS(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-cosh.spec.ts b/test/unit/interpreter/function-cosh.spec.ts deleted file mode 100644 index 17c492ea80..0000000000 --- a/test/unit/interpreter/function-cosh.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function COSH', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=COSH(0)', '=COSH(1)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(1.54308063481524) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=COSH("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=COSH()', '=COSH(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=COSH(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(1.54308063481524) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=COSH(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-cot.spec.ts b/test/unit/interpreter/function-cot.spec.ts deleted file mode 100644 index d2d46bd396..0000000000 --- a/test/unit/interpreter/function-cot.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function COT', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=COT(1)']]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.642092615934331) - }) - - it('DIV/0 for zero', () => { - const engine = HyperFormula.buildFromArray([ - ['=COT(0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=COT("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=COT()', '=COT(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=COT(A1)'], - ['', '=COT(A2)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-0.642092615934331) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=COT(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-coth.spec.ts b/test/unit/interpreter/function-coth.spec.ts deleted file mode 100644 index a37a314308..0000000000 --- a/test/unit/interpreter/function-coth.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function COTH', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=COTH(1)']]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.31303528549933) - }) - - it('DIV/0 for zero', () => { - const engine = HyperFormula.buildFromArray([ - ['=COTH(0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=COTH("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=COTH()', '=COTH(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=COTH(A1)'], - ['', '=COTH(A2)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-1.31303528549933) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=COTH(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-count.spec.ts b/test/unit/interpreter/function-count.spec.ts deleted file mode 100644 index 97f84cc680..0000000000 --- a/test/unit/interpreter/function-count.spec.ts +++ /dev/null @@ -1,75 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('COUNT', () => { - it('COUNT with empty args', () => { - const engine = HyperFormula.buildFromArray([['=COUNT()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('COUNT with args', () => { - const engine = HyperFormula.buildFromArray([['=COUNT(1, B1)', '3.14']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('COUNT with range', () => { - const engine = HyperFormula.buildFromArray([['1'], ['3'], ['2'], ['=COUNT(A1:A3)']]) - - expect(engine.getCellValue(adr('A4'))).toEqual(3) - }) - - it('COUNT ignores all nonnumeric arguments', () => { - const engine = HyperFormula.buildFromArray([['foo'], [null], ['=TRUE()'], ['=COUNT(A1:A3)']]) - - expect(engine.getCellValue(adr('A4'))).toEqual(0) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=COUNT(MMULT(A1:B2, A1:B2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(4) - }) - - it('error in ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['', ''], - ['=COUNT(MMULT(A1:B3, A1:B3))'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(0) - }) - - it('doesnt propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=4/0'], - ['=FOOBAR()', '4'], - ['=COUNT(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(2) - }) - - it('should work with explicit error in arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNT(NA())'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) - - it('should work for empty arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNT(1,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) -}) diff --git a/test/unit/interpreter/function-counta.spec.ts b/test/unit/interpreter/function-counta.spec.ts deleted file mode 100644 index 0b55282a1e..0000000000 --- a/test/unit/interpreter/function-counta.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('COUNTA', () => { - it('COUNTA with empty args', () => { - const engine = HyperFormula.buildFromArray([['=COUNTA()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('COUNTA with args', () => { - const engine = HyperFormula.buildFromArray([['=COUNTA(1, B1)', '3.14']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('COUNTA with range', () => { - const engine = HyperFormula.buildFromArray([['1'], ['3'], ['2'], ['=COUNTA(A1:A3)']]) - - expect(engine.getCellValue(adr('A4'))).toEqual(3) - }) - - it('COUNTA doesnt count only empty values', () => { - const engine = HyperFormula.buildFromArray([['foo'], ['=""'], [null], ['=TRUE()'], ['=COUNTA(A1:A4)']]) - - expect(engine.getCellValue(adr('A5'))).toEqual(3) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=COUNTA(MMULT(A1:B2, A1:B2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(4) - }) - - it('error in ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['', ''], - ['=COUNTA(MMULT(A1:B3, A1:B3))'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(1) - }) - - it('doesnt propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=4/0'], - ['=FOOBAR()', '4'], - ['=COUNTA(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(4) - }) - - it('should work with explicit error in arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNTA(NA())'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('should work for empty arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNTA(1,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) //Compatible with product 2 - }) -}) diff --git a/test/unit/interpreter/function-countblank.spec.ts b/test/unit/interpreter/function-countblank.spec.ts deleted file mode 100644 index eee2a88ae9..0000000000 --- a/test/unit/interpreter/function-countblank.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('COUNTBLANK', () => { - it('with empty args', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNTBLANK()'], - ['=COUNTBLANK(,)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - }) - - it('with args', () => { - const engine = HyperFormula.buildFromArray([['=COUNTBLANK(B1, C1)', '3.14']]) - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('with range', () => { - const engine = HyperFormula.buildFromArray([['1', null, null, '=COUNTBLANK(A1:C1)']]) - expect(engine.getCellValue(adr('D1'))).toEqual(2) - }) - - it('with empty strings', () => { - const engine = HyperFormula.buildFromArray([['', null, null, '=COUNTBLANK(A1:C1)']]) - expect(engine.getCellValue(adr('D1'))).toEqual(2) - }) - - it('does not propagate errors from ranges', () => { - const engine = HyperFormula.buildFromArray([ - [null], - ['=4/0'], - ['=COUNTBLANK(A1:A2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(1) - }) - - it('does not propagate errors from arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNTBLANK(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) - - it('works even when range vertex is in cycle', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['=COUNTBLANK(A1:A3)'], - [null], - ['=COUNTBLANK(A1:A3)'] - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - }) -}) diff --git a/test/unit/interpreter/function-countif.spec.ts b/test/unit/interpreter/function-countif.spec.ts deleted file mode 100644 index 0e40774772..0000000000 --- a/test/unit/interpreter/function-countif.spec.ts +++ /dev/null @@ -1,147 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {StatType} from '../../../src/statistics' -import {adr, detailedError} from '../testUtils' - -describe('Function COUNTIF', () => { - it('requires 2 arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNTIF(B1:B3)'], - ['=COUNTIF(B1:B3, ">0", B1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['0'], - ['1'], - ['2'], - ['=COUNTIF(A1:A3, ">=1")'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(2) - }) - - it('works with mixed types', () => { - const engine = HyperFormula.buildFromArray([ - ['0'], - ['"1"'], - ['2'], - ['=COUNTIF(A1:A3, "=1")'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(0) - }) - - it('use partial cache', () => { - const engine = HyperFormula.buildFromArray([ - ['0'], - ['1'], - ['2', '=COUNTIF(A1:A3, ">=1")'], - ['3', '=COUNTIF(A1:A4, ">=1")'], - ]) - - expect(engine.getCellValue(adr('B3'))).toEqual(2) - expect(engine.getCellValue(adr('B4'))).toEqual(3) - expect(engine.getStats().get(StatType.CRITERION_FUNCTION_PARTIAL_CACHE_USED)).toEqual(1) - }) - - it('use full cache', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '=COUNTIF(A1:A3, ">=1")'], - ['1', '=COUNTIF(A1:A3, ">=1")'], - ['2'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(2) - expect(engine.getCellValue(adr('B2'))).toEqual(2) - expect(engine.getStats().get(StatType.CRITERION_FUNCTION_FULL_CACHE_USED)).toEqual(1) - }) - - it('works for only one cell', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=COUNTIF(A1, ">=1")'], - ['0', '=COUNTIF(A2, ">=1")'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('B2'))).toEqual(0) - }) - - it('error when criterion unparsable', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNTIF(B1:B2, "> { - const engine = HyperFormula.buildFromArray([ - ['=COUNTIF(10, ">1")'], - ['=COUNTIF(0, ">1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) - - it('error propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNTIF(4/0, ">1")'], - ['=COUNTIF(0, 4/0)'], - ['=COUNTIF(4/0, FOOBAR())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('works with range values', () => { - const engine = HyperFormula.buildFromArray([ - ['3', '5'], - ['7', '9'], - ['=COUNTIF(A1:B2, ">4")'], - ['=COUNTIF(MMULT(A1:B2, A1:B2), ">50")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqual(3) - }) - - it('works for matrices', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - [], - ['=COUNTIF(A2:A3, ">0")'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(2) - }) - - it('ignore errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['=4/0'], - ['1'], - ['=COUNTIF(A1:A3, "=1")'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(2) - }) - - it('works with a column range reference to an empty sheet', () => { - const hf = HyperFormula.buildFromSheets({ - table1: [], - table2: [['=COUNTIF(table1!A:C, ">1")']], - }) - - expect(hf.getCellValue(adr('A1', 1))).toEqual(0) - }) -}) diff --git a/test/unit/interpreter/function-countifs.spec.ts b/test/unit/interpreter/function-countifs.spec.ts deleted file mode 100644 index 662c2d0368..0000000000 --- a/test/unit/interpreter/function-countifs.spec.ts +++ /dev/null @@ -1,140 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {StatType} from '../../../src/statistics' -import {adr, detailedError} from '../testUtils' - -describe('Function COUNTIFS', () => { - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNTIFS(B1:B3)'], - ['=COUNTIFS(B1:B3, ">0", B1)'], - ['=COUNTIFS(B1:B3, ">0", B1, ">1", 42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['0'], - ['1'], - ['2'], - ['=COUNTIFS(A1:A3, ">=1")'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(2) - }) - - it('works for more criteria pairs', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['2', '20'], - ['3', '30'], - ['=COUNTIFS(A1:A3, ">=2", B1:B3, "<=20")'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(1) - }) - - it('use partial cache', () => { - const engine = HyperFormula.buildFromArray([ - ['0'], - ['1'], - ['2', '=COUNTIFS(A1:A3, ">=1")'], - ['3', '=COUNTIFS(A1:A4, ">=1")'], - ]) - - expect(engine.getCellValue(adr('B3'))).toEqual(2) - expect(engine.getCellValue(adr('B4'))).toEqual(3) - expect(engine.getStats().get(StatType.CRITERION_FUNCTION_PARTIAL_CACHE_USED)).toEqual(1) - }) - - it('use full cache', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '=COUNTIFS(A1:A3, ">=1")'], - ['1', '=COUNTIFS(A1:A3, ">=1")'], - ['2'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(2) - expect(engine.getCellValue(adr('B2'))).toEqual(2) - expect(engine.getStats().get(StatType.CRITERION_FUNCTION_FULL_CACHE_USED)).toEqual(1) - }) - - it('works for only one cell', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=COUNTIFS(A1, ">=1")'], - ['0', '=COUNTIFS(A2, ">=1")'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('B2'))).toEqual(0) - }) - - it('error when criterion unparsable', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNTIFS(B1:B2, "> { - const engine = HyperFormula.buildFromArray([ - ['=COUNTIFS(10, ">1")'], - ['=COUNTIFS(0, ">1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) - - it('error propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNTIFS(4/0, ">1")'], - ['=COUNTIFS(0, 4/0)'], - ['=COUNTIFS(4/0, FOOBAR())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('works with range values', () => { - const engine = HyperFormula.buildFromArray([ - ['3', '5'], - ['7', '9'], - ['=COUNTIFS(A1:B2, ">4")'], - ['=COUNTIFS(MMULT(A1:B2, A1:B2), ">50")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqual(3) - }) - - it('works for matrices', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - [], - ['=COUNTIFS(A2:A3, ">0")'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(2) - }) - - it('ignore errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['=4/0'], - ['1'], - ['=COUNTIFS(A1:A3, "=1")'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(2) - }) -}) diff --git a/test/unit/interpreter/function-countunique.spec.ts b/test/unit/interpreter/function-countunique.spec.ts deleted file mode 100644 index 286f98d3c7..0000000000 --- a/test/unit/interpreter/function-countunique.spec.ts +++ /dev/null @@ -1,94 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function COUNTUNIQUE', () => { - it('error when no arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNTUNIQUE()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('single number', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNTUNIQUE(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('three numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNTUNIQUE(2, 1, 2)'], - ['=COUNTUNIQUE(2, 1, 1)'], - ['=COUNTUNIQUE(2, 1, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - }) - - it('theres no coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '="1"'], - ['=COUNTUNIQUE(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(2) - }) - - it('errors in arguments are not propagated', () => { - const engine = HyperFormula.buildFromArray([ - ['=COUNTUNIQUE(5/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('different errors are counted by type', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/0', '=COUNTUNIQUE(A1:A4)'], - ['=FOOBAR()'], - ['=5/0'], - ['=BARFOO()'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(2) - }) - - it('empty string doesnt count', () => { - const engine = HyperFormula.buildFromArray([ - ['=""', '=COUNTUNIQUE("", A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(0) - }) - - it('different strings are recognized are counted by type', () => { - const engine = HyperFormula.buildFromArray([ - ['foo', '=COUNTUNIQUE(A1:A4)'], - ['bar'], - ['foo'], - ['bar '], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(3) - }) - - it('singular values are counted', () => { - const engine = HyperFormula.buildFromArray([ - ['TRUE()', '=COUNTUNIQUE(A1:A6)'], - ['FALSE()'], - [null], - ['TRUE()'], - ['FALSE()'], - [null], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(3) - }) -}) diff --git a/test/unit/interpreter/function-covariance.p.spec.ts b/test/unit/interpreter/function-covariance.p.spec.ts deleted file mode 100644 index 1c4ba56444..0000000000 --- a/test/unit/interpreter/function-covariance.p.spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('COVARIANCE.P', () => { - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=COVARIANCE.P(B1:B5)'], - ['=COVARIANCE.P(B1:B5, C1:C5, D1:D5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('ranges need to have same amount of elements', () => { - const engine = HyperFormula.buildFromArray([ - ['=COVARIANCE.P(B1:B5, C1:C6)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.EqualLength)) - }) - - it('works (simple)', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['2', '20'], - ['=COVARIANCE.P(A1:A2, B1:B2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(2.5) - }) - - it('error when not enough data', () => { - const engine = HyperFormula.buildFromArray([ - ['=COVARIANCE.P(A2:A2, A2:A2)'], - [null] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.OneValue)) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['2', '4'], - ['5', '3'], - ['7', '6'], - ['1', '1'], - ['8', '5'], - ['=COVARIANCE.P(A1:A5, B1:B5)'], - ['=COVARIANCE.P(1,1)'], - ]) - - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(3.72) - expect(engine.getCellValue(adr('A7'))).toEqual(0) - }) - - it('doesnt do coercions, nonnumeric values are skipped', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['="2"', '50'], - ['3', '30'], - ['=COVARIANCE.P(A1:A3, B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(10) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ['=COVARIANCE.P(MMULT(A1:B2, A1:B2), MMULT(B1:C2, B1:C2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(122.25) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['=4/0', '50'], - ['3', '30'], - ['=COVARIANCE.P(A1:A3, B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-covariance.s.spec.ts b/test/unit/interpreter/function-covariance.s.spec.ts deleted file mode 100644 index 10cc4ed085..0000000000 --- a/test/unit/interpreter/function-covariance.s.spec.ts +++ /dev/null @@ -1,92 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('COVARIANCE.S', () => { - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=COVARIANCE.S(B1:B5)'], - ['=COVARIANCE.S(B1:B5, C1:C5, D1:D5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('ranges need to have same amount of elements', () => { - const engine = HyperFormula.buildFromArray([ - ['=COVARIANCE.S(B1:B5, C1:C6)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.EqualLength)) - }) - - it('works (simple)', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['2', '20'], - ['=COVARIANCE.S(A1:A2, B1:B2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(5) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['2', '4'], - ['5', '3'], - ['7', '6'], - ['1', '1'], - ['8', '5'], - ['=COVARIANCE.S(A1:A5, B1:B5)'] - ]) - - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(4.65) - }) - - it('error when not enough data', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['=COVARIANCE.S(A1:A1, B1:B1)'], - ['=COVARIANCE.S(42, 43)'], - ['=COVARIANCE.S("foo", "bar")'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.TwoValues)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.TwoValues)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.TwoValues)) - }) - - it('doesnt do coercions, nonnumeric values are skipped', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['="2"', '50'], - ['3', '30'], - ['=COVARIANCE.S(A1:A3, B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(20) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ['=COVARIANCE.S(MMULT(A1:B2, A1:B2), MMULT(B1:C2, B1:C2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(163) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['=4/0', '50'], - ['3', '30'], - ['=COVARIANCE.S(A1:A3, B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-csc.spec.ts b/test/unit/interpreter/function-csc.spec.ts deleted file mode 100644 index 58ad53a441..0000000000 --- a/test/unit/interpreter/function-csc.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CSC', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=CSC(PI()/2)', '=CSC(1)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(1.18839510577812) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=CSC("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=CSC()', '=CSC(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=CSC(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-1.18839510577812) - }) - - it('div/zero', () => { - const engine = HyperFormula.buildFromArray([ - [0, '=CSC(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=CSC(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-csch.spec.ts b/test/unit/interpreter/function-csch.spec.ts deleted file mode 100644 index 5f5fe13db0..0000000000 --- a/test/unit/interpreter/function-csch.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CSCH', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=CSCH(1)']]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.850918128239322, 11) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=CSCH("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=CSCH()', '=CSCH(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=CSCH(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-0.850918128239322) - }) - - it('div/zero', () => { - const engine = HyperFormula.buildFromArray([ - [0, '=CSCH(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=CSCH(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-cumimpt.spec.ts b/test/unit/interpreter/function-cumimpt.spec.ts deleted file mode 100644 index b5f99bb8e7..0000000000 --- a/test/unit/interpreter/function-cumimpt.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CUMIPMT', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CUMIPMT(1,1,1,1,1)', '=CUMIPMT(1, 1, 1, 1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=CUMIPMT(1.1%, 12, 100, 1, 5, 0)', '=CUMIPMT(1.1%, 12, 100, 1, 5, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-4.6279374617215) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-3.4895523854812) - }) - - it('should return error when args are incorrect', () => { - const engine = HyperFormula.buildFromArray([ - ['=CUMIPMT(1.1%, 12, 100, 5, 1, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.EndStartPeriod)) - }) -}) diff --git a/test/unit/interpreter/function-cumprinc.spec.ts b/test/unit/interpreter/function-cumprinc.spec.ts deleted file mode 100644 index 730a98e262..0000000000 --- a/test/unit/interpreter/function-cumprinc.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function CUMPRINC', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=CUMPRINC(1,1,1,1,1)', '=CUMPRINC(1, 1, 1, 1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=CUMPRINC(1.1%, 12, 100, 1, 5, 0)', '=CUMPRINC(1.1%, 12, 100, 1, 5, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-40.07763042) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-40.72960477) - }) - - it('should return error when args are incorrect', () => { - const engine = HyperFormula.buildFromArray([ - ['=CUMPRINC(1.1%, 12, 100, 5, 1, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.EndStartPeriod)) - }) -}) diff --git a/test/unit/interpreter/function-date.spec.ts b/test/unit/interpreter/function-date.spec.ts deleted file mode 100644 index c7078bef73..0000000000 --- a/test/unit/interpreter/function-date.spec.ts +++ /dev/null @@ -1,222 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueDetailedType, ErrorType} from '../../../src/Cell' -import {Config} from '../../../src/Config' -import {ErrorMessage} from '../../../src/error-message' -import {adr, dateNumberToString, detailedError} from '../testUtils' - -describe('Function DATE', () => { - it('with 3 numerical arguments', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=DATE(1900, 1, 1)', '=DATE(1900, 1, 2)', '=DATE(1915, 10, 24)'], - ], config) - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(dateNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('01/01/1900') - expect(engine.getCellValue(adr('B1'))).toEqual(3) - expect(dateNumberToString(engine.getCellValue(adr('B1')), config)).toEqual('02/01/1900') - expect(dateNumberToString(engine.getCellValue(adr('C1')), config)).toEqual('24/10/1915') - }) - - it('truncation', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=DATE(1900.9, 1, 1)', '=DATE(1900, 1.9, 2)', '=DATE(1915, 10, 24.9)'], - ], config) - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(dateNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('01/01/1900') - expect(engine.getCellValue(adr('B1'))).toEqual(3) - expect(dateNumberToString(engine.getCellValue(adr('B1')), config)).toEqual('02/01/1900') - expect(dateNumberToString(engine.getCellValue(adr('C1')), config)).toEqual('24/10/1915') - }) - - it('negative', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=DATE(-1900, 1, 1)', '=DATE(1901, -1, 2)', '=DATE(2000, -13, 2)', '=DATE(1915, 10, -24)', '=DATE(1900, 1, -100000)', '=DATE(1900, 1, -200000)', '=DATE(-1, 1, 1)'], - ], config) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.InvalidDate)) - expect(dateNumberToString(engine.getCellValue(adr('B1')), config)).toEqual('02/11/1900') - expect(dateNumberToString(engine.getCellValue(adr('C1')), config)).toEqual('02/11/1998') - expect(dateNumberToString(engine.getCellValue(adr('D1')), config)).toEqual('06/09/1915') - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.DateBounds)) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.DateBounds)) - expect(engine.getCellValue(adr('G1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.DateBounds)) - }) - - it('rollover', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=DATE(1900, 14, 28)', '=DATE(1900, 14, 29)', '=DATE(1915, 100, 1000)'], - ], config) - expect(dateNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('28/02/1901') - expect(dateNumberToString(engine.getCellValue(adr('B1')), config)).toEqual('01/03/1901') - expect(dateNumberToString(engine.getCellValue(adr('C1')), config)).toEqual('25/12/1925') - }) - - it('number of arguments', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=DATE(1900, 1)'], - ['=DATE(1900, 1, 1, 1)'], - ], config) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('with incoercible argument', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=DATE("foo", 1, 1)'], - ['=DATE(1900, "foo", 1)'], - ['=DATE(1900, 1, "foo")'], - ], config) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('with coercible argument', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['="2000"', '=TRUE()'], - ['=DATE(A1, 1, 1)'], - ['=DATE(2000, B1, 1)'], - ['=DATE(2000, 1, B1)'], - ], config) - expect(dateNumberToString(engine.getCellValue(adr('A2')), config)).toEqual('01/01/2000') - expect(dateNumberToString(engine.getCellValue(adr('A3')), config)).toEqual('01/01/2000') - expect(dateNumberToString(engine.getCellValue(adr('A4')), config)).toEqual('01/01/2000') - }) - - it('precedence of errors', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=DATE(FOOBAR(), 4/0, 1)'], - ['=DATE(2000, FOOBAR(), 4/0)'], - ['=DATE(2000, 1, FOOBAR())'], - ], config) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - }) -}) - -describe('Function DATE + leap years', () => { - it('should support nonleap year 2001', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=DATE(2001, 02, 29)'], - ], config) - expect(dateNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('01/03/2001') - }) - - it('should support leap year 2016', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=DATE(2016, 02, 29)'], - ], config) - expect(dateNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('29/02/2016') - }) - - it('should support leap year 1920', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=DATE(1920, 02, 29)'], - ], config) - expect(dateNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('29/02/1920') - }) - - it('should support nonleap year 1900', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=DATE(1900, 02, 29)'], - ], config) - expect(dateNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('01/03/1900') - }) - - it('should support nonleap year 1900 with excel compatibility', () => { - const config = new Config({leapYear1900: true}) - const engine = HyperFormula.buildFromArray([ - ['=DATE(1900, 02, 29)'], - ], config) - expect(dateNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('29/02/1900') - }) - - it('should support leap year 2400', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=DATE(2400, 02, 29)'], - ], config) - expect(dateNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('29/02/2400') - }) - - it('small year values', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=DATE(0, 02, 29)'], - ['=DATE(1800, 02, 28)'], - ], config) - expect(dateNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('01/03/1900') - expect(dateNumberToString(engine.getCellValue(adr('A2')), config)).toEqual('28/02/3700') - }) - - it('different nullDate', () => { - const config = new Config({nullDate: {year: 1900, day: 1, month: 1}}) - const engine = HyperFormula.buildFromArray([ - ['=DATE(0, 02, 28)'], - ['=DATE(1800, 02, 28)'], - ], config) - expect(dateNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('28/02/1900') - expect(dateNumberToString(engine.getCellValue(adr('A2')), config)).toEqual('28/02/3700') - }) - - it('should be leap1900 sensitive', () => { - const config = new Config({leapYear1900: true}) - const engine = HyperFormula.buildFromArray([ - ['=DATE(10, 03, 03)'], - ['=DATE(1800, 02, 28)'], - ], config) - expect(dateNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('03/03/1909') - expect(dateNumberToString(engine.getCellValue(adr('A2')), config)).toEqual('28/02/3699') - }) - - it('different nullDate, leap1900 sensitive', () => { - const config = new Config({nullDate: {year: 1899, day: 31, month: 12}, leapYear1900: true}) - const engine = HyperFormula.buildFromArray([ - ['=DATE(0, 02, 28)'], - ['=DATE(0, 02, 29)'], - ['=DATE(1800, 02, 28)'], - ], config) - expect(dateNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('28/02/1900') - expect(dateNumberToString(engine.getCellValue(adr('A2')), config)).toEqual('29/02/1900') - expect(dateNumberToString(engine.getCellValue(adr('A3')), config)).toEqual('28/02/3700') - }) - - it('should throw a error in the absence of arguments', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=DATE()'], - ], config) - - expect(dateNumberToString(engine.getCellValue(adr('A1')), config)).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('with blanks', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - [null, '', 'string', null, '\''], - ['=DATE(A1, 2, 3)'], - ['=DATE(B1, 2, 3)'], - ['=DATE(C1, 2, 3)'], - ['=DATE(D1, 2, 3)'], - ['=DATE(E1, 2, 3)'], - ], config) - - expect(dateNumberToString(engine.getCellValue(adr('A2')), config)).toEqual('03/02/1900') - expect(dateNumberToString(engine.getCellValue(adr('A3')), config)).toEqual('03/02/1900') - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(dateNumberToString(engine.getCellValue(adr('A5')), config)).toEqual('03/02/1900') - expect(dateNumberToString(engine.getCellValue(adr('A6')), config)).toEqual('03/02/1900') - }) -}) diff --git a/test/unit/interpreter/function-datedif.spec.ts b/test/unit/interpreter/function-datedif.spec.ts deleted file mode 100644 index efacfd5f95..0000000000 --- a/test/unit/interpreter/function-datedif.spec.ts +++ /dev/null @@ -1,349 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function DATEDIF', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF(1, 2, 3, 4)'], - ['=DATEDIF(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for wrong type of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("foo", 1, "Y")'], - ['=DATEDIF(2, "bar", "Y")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('numerical errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF(1, 2, "abcd")'], - ['=DATEDIF(2, 1, "Y")'], - ['=DATEDIF(1.9, 1.8, "Y")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.BadMode)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.StartEndDate)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.StartEndDate)) - }) - - it('"D" mode', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("30/12/2018", "30/12/2018", "D")'], - ['=DATEDIF("28/02/2019", "01/03/2019", "D")'], - ['=DATEDIF("28/02/2020", "01/03/2020", "D")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - }) - - it('ignores time', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("22:00", "36:00", "D")'], - ['=DATEDIF("28/02/2019", "01/03/2019 1:00am", "D")'], - ['=DATEDIF("28/02/2020 2:00pm", "01/03/2020", "D")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - }) - - it('"M" mode', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("30/12/2018", "30/12/2019", "M")'], - ['=DATEDIF("28/02/2019", "29/03/2019", "M")'], - ['=DATEDIF("29/02/2020", "28/03/2020", "M")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(12) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(0) - }) - - it('"YM" mode', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("30/12/2018", "30/12/2019", "YM")'], - ['=DATEDIF("28/02/2019", "29/03/2019", "YM")'], - ['=DATEDIF("29/02/2020", "28/03/2020", "YM")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(0) - }) - - it('"Y" mode', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("01/03/2019", "29/02/2020", "Y")'], - ['=DATEDIF("01/03/2019", "28/02/2020", "Y")'], - ['=DATEDIF("28/02/2019", "29/02/2020", "Y")'], - ['=DATEDIF("28/02/2019", "28/02/2020", "Y")'], - ['=DATEDIF("29/02/2020", "28/02/2021", "Y")'], - ['=DATEDIF("29/02/2020", "01/03/2021", "Y")'], - ['=DATEDIF("28/02/2020", "28/02/2021", "Y")'], - ['=DATEDIF("28/02/2020", "01/03/2021", "Y")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - expect(engine.getCellValue(adr('A6'))).toEqual(1) - expect(engine.getCellValue(adr('A7'))).toEqual(1) - expect(engine.getCellValue(adr('A8'))).toEqual(1) - }) - - it('"MD" mode #1', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("28/03/2019", "29/02/2020", "MD")'], - ['=DATEDIF("28/03/2019", "28/02/2020", "MD")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) - - it('"MD" mode #2', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("28/03/2016", "01/05/2020", "MD")'], - ['=DATEDIF("28/02/2016", "01/05/2020", "MD")'], - ['=DATEDIF("28/02/2015", "01/05/2020", "MD")'], - ['=DATEDIF("28/01/2016", "01/05/2020", "MD")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - expect(engine.getCellValue(adr('A2'))).toEqual(3) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqual(3) - }) - - it('"MD" mode #3', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("28/03/2016", "01/03/2020", "MD")'], - ['=DATEDIF("28/02/2016", "01/03/2020", "MD")'], - ['=DATEDIF("28/02/2015", "01/03/2020", "MD")'], - ['=DATEDIF("28/01/2016", "01/03/2020", "MD")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - expect(engine.getCellValue(adr('A4'))).toEqual(2) - }) - - it('"MD" mode #4', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("28/03/2016", "01/03/2021", "MD")'], - ['=DATEDIF("28/02/2016", "01/03/2021", "MD")'], - ['=DATEDIF("28/02/2015", "01/03/2021", "MD")'], - ['=DATEDIF("28/01/2016", "01/03/2021", "MD")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - }) - - it('"MD" mode #5', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("28/03/2016", "01/02/2020", "MD")'], - ['=DATEDIF("28/02/2016", "01/02/2020", "MD")'], - ['=DATEDIF("28/02/2015", "01/02/2020", "MD")'], - ['=DATEDIF("28/01/2016", "01/02/2020", "MD")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - expect(engine.getCellValue(adr('A2'))).toEqual(4) - expect(engine.getCellValue(adr('A3'))).toEqual(4) - expect(engine.getCellValue(adr('A4'))).toEqual(4) - }) - - it('"MD" mode #6', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("28/03/2016", "01/01/2020", "MD")'], - ['=DATEDIF("28/02/2016", "01/01/2020", "MD")'], - ['=DATEDIF("28/02/2015", "01/01/2020", "MD")'], - ['=DATEDIF("28/01/2016", "01/01/2020", "MD")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - expect(engine.getCellValue(adr('A2'))).toEqual(4) - expect(engine.getCellValue(adr('A3'))).toEqual(4) - expect(engine.getCellValue(adr('A4'))).toEqual(4) - }) - - it('"MD" mode negative result', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("31/01/2020", "01/03/2020", "MD")'], - ['=DATEDIF("31/01/2021", "01/03/2021", "MD")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(-1) - expect(engine.getCellValue(adr('A2'))).toEqual(-2) - }) - - it('"YD" mode #1', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("27/02/2016", "27/02/2020", "YD")'], - ['=DATEDIF("27/02/2016", "28/02/2020", "YD")'], - ['=DATEDIF("27/02/2016", "29/02/2020", "YD")'], - ['=DATEDIF("27/02/2016", "01/03/2020", "YD")'], - ['=DATEDIF("27/02/2016", "27/02/2021", "YD")'], - ['=DATEDIF("27/02/2016", "28/02/2021", "YD")'], - ['=DATEDIF("27/02/2016", "01/03/2021", "YD")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - expect(engine.getCellValue(adr('A4'))).toEqual(3) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - expect(engine.getCellValue(adr('A6'))).toEqual(1) - expect(engine.getCellValue(adr('A7'))).toEqual(2) - }) - - it('"YD" mode #2', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("28/02/2016", "27/02/2020", "YD")'], - ['=DATEDIF("28/02/2016", "28/02/2020", "YD")'], - ['=DATEDIF("28/02/2016", "29/02/2020", "YD")'], - ['=DATEDIF("28/02/2016", "01/03/2020", "YD")'], - ['=DATEDIF("28/02/2016", "27/02/2021", "YD")'], - ['=DATEDIF("28/02/2016", "28/02/2021", "YD")'], - ['=DATEDIF("28/02/2016", "01/03/2021", "YD")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(365) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(2) - expect(engine.getCellValue(adr('A5'))).toEqual(365) - expect(engine.getCellValue(adr('A6'))).toEqual(0) - expect(engine.getCellValue(adr('A7'))).toEqual(1) - }) - - it('"YD" mode #3', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("29/02/2016", "27/02/2020", "YD")'], - ['=DATEDIF("29/02/2016", "28/02/2020", "YD")'], - ['=DATEDIF("29/02/2016", "29/02/2020", "YD")'], - ['=DATEDIF("29/02/2016", "01/03/2020", "YD")'], - ['=DATEDIF("29/02/2016", "27/02/2021", "YD")'], - ['=DATEDIF("29/02/2016", "28/02/2021", "YD")'], - ['=DATEDIF("29/02/2016", "01/03/2021", "YD")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(364) - expect(engine.getCellValue(adr('A2'))).toEqual(365) - expect(engine.getCellValue(adr('A3'))).toEqual(0) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - expect(engine.getCellValue(adr('A5'))).toEqual(364) - expect(engine.getCellValue(adr('A6'))).toEqual(365) - expect(engine.getCellValue(adr('A7'))).toEqual(0) - }) - - it('"YD" mode #4', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("01/03/2016", "27/02/2020", "YD")'], - ['=DATEDIF("01/03/2016", "28/02/2020", "YD")'], - ['=DATEDIF("01/03/2016", "29/02/2020", "YD")'], - ['=DATEDIF("01/03/2016", "01/03/2020", "YD")'], - ['=DATEDIF("01/03/2016", "27/02/2021", "YD")'], - ['=DATEDIF("01/03/2016", "28/02/2021", "YD")'], - ['=DATEDIF("01/03/2016", "01/03/2021", "YD")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(363) - expect(engine.getCellValue(adr('A2'))).toEqual(364) - expect(engine.getCellValue(adr('A3'))).toEqual(365) - expect(engine.getCellValue(adr('A4'))).toEqual(0) - expect(engine.getCellValue(adr('A5'))).toEqual(363) - expect(engine.getCellValue(adr('A6'))).toEqual(364) - expect(engine.getCellValue(adr('A7'))).toEqual(0) - }) - - it('"YD" mode #5', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("27/02/2015", "27/02/2020", "YD")'], - ['=DATEDIF("27/02/2015", "28/02/2020", "YD")'], - ['=DATEDIF("27/02/2015", "29/02/2020", "YD")'], - ['=DATEDIF("27/02/2015", "01/03/2020", "YD")'], - ['=DATEDIF("27/02/2015", "27/02/2021", "YD")'], - ['=DATEDIF("27/02/2015", "28/02/2021", "YD")'], - ['=DATEDIF("27/02/2015", "01/03/2021", "YD")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - expect(engine.getCellValue(adr('A4'))).toEqual(3) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - expect(engine.getCellValue(adr('A6'))).toEqual(1) - expect(engine.getCellValue(adr('A7'))).toEqual(2) - }) - - it('"YD" mode #6', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("28/02/2015", "27/02/2020", "YD")'], - ['=DATEDIF("28/02/2015", "28/02/2020", "YD")'], - ['=DATEDIF("28/02/2015", "29/02/2020", "YD")'], - ['=DATEDIF("28/02/2015", "01/03/2020", "YD")'], - ['=DATEDIF("28/02/2015", "27/02/2021", "YD")'], - ['=DATEDIF("28/02/2015", "28/02/2021", "YD")'], - ['=DATEDIF("28/02/2015", "01/03/2021", "YD")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(364) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(2) - expect(engine.getCellValue(adr('A5'))).toEqual(364) - expect(engine.getCellValue(adr('A6'))).toEqual(0) - expect(engine.getCellValue(adr('A7'))).toEqual(1) - }) - - it('"YD" mode #7', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF("01/03/2015", "27/02/2020", "YD")'], - ['=DATEDIF("01/03/2015", "28/02/2020", "YD")'], - ['=DATEDIF("01/03/2015", "29/02/2020", "YD")'], - ['=DATEDIF("01/03/2015", "01/03/2020", "YD")'], - ['=DATEDIF("01/03/2015", "27/02/2021", "YD")'], - ['=DATEDIF("01/03/2015", "28/02/2021", "YD")'], - ['=DATEDIF("01/03/2015", "01/03/2021", "YD")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(363) - expect(engine.getCellValue(adr('A2'))).toEqual(364) - expect(engine.getCellValue(adr('A3'))).toEqual(365) - expect(engine.getCellValue(adr('A4'))).toEqual(0) - expect(engine.getCellValue(adr('A5'))).toEqual(363) - expect(engine.getCellValue(adr('A6'))).toEqual(364) - expect(engine.getCellValue(adr('A7'))).toEqual(0) - }) - - //inconsistency with product 1 - it('fails for negative values', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEDIF(-1, 0, "Y")'], - ['=DATEDIF(0, -1, "M")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-datevalue.spec.ts b/test/unit/interpreter/function-datevalue.spec.ts deleted file mode 100644 index 813bf39704..0000000000 --- a/test/unit/interpreter/function-datevalue.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueDetailedType, ErrorType} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function DATEVALUE', () => { - it('with wrong arguments', () => { - const engine = HyperFormula.buildFromArray([['=DATEVALUE("foo")', '=DATEVALUE(1)', '=DATEVALUE(1, 2)', '=DATEVALUE()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.IncorrectDateTime)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.IncorrectDateTime)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('with string arguments', () => { - const engine = HyperFormula.buildFromArray([['=DATEVALUE("31/12/1899")', '=DATEVALUE("01/01/1900")', '=DATEVALUE("31/12/2018")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValue(adr('B1'))).toEqual(2) - expect(engine.getCellValue(adr('C1'))).toEqual(43465) - }) - - it('ignores time', () => { - const engine = HyperFormula.buildFromArray([['=DATEVALUE("2:00pm")', '=DATEVALUE("31/12/2018 2:00pm")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('B1'))).toEqual(43465) - }) - - it('border case', () => { - const engine = HyperFormula.buildFromArray([['=DATEVALUE("25:00")', '=DATEVALUE("31/12/2018 25:00")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('B1'))).toEqual(43466) - }) - - it('propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEVALUE(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should return NUMBER_DATE', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATEVALUE("25/02/1991")'], - ]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.NUMBER_DATE) - }) -}) diff --git a/test/unit/interpreter/function-day.spec.ts b/test/unit/interpreter/function-day.spec.ts deleted file mode 100644 index b362d867b0..0000000000 --- a/test/unit/interpreter/function-day.spec.ts +++ /dev/null @@ -1,247 +0,0 @@ -import {HyperFormula, ErrorType} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function DAY', () => { - it('with wrong arguments', () => { - const engine = HyperFormula.buildFromArray([['=DAY("foo")', '=DAY("12/30/2018")', '=DAY(1, 2)', '=DAY()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('with numerical arguments', () => { - const engine = HyperFormula.buildFromArray([['=DAY(0)', '=DAY(2)', '=DAY(43465)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(30) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('C1'))).toEqual(31) - }) - - it('with string arguments', () => { - const engine = HyperFormula.buildFromArray([['=DAY("31/12/1899")', '=DAY("01/01/1900")', '=DAY("31/12/2018")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(31) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('C1'))).toEqual(31) - }) - - it('use datenumber coercion for 1st argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAY(TRUE())'], - ['=DAY(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(31) - expect(engine.getCellValue(adr('A2'))).toEqual(31) - }) - - it('propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAY(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('test for days in month, start of month', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAY(DATE(2021, 1, 1))'], - ['=DAY(DATE(2021, 2, 1))'], - ['=DAY(DATE(2021, 3, 1))'], - ['=DAY(DATE(2021, 4, 1))'], - ['=DAY(DATE(2021, 5, 1))'], - ['=DAY(DATE(2021, 6, 1))'], - ['=DAY(DATE(2021, 7, 1))'], - ['=DAY(DATE(2021, 8, 1))'], - ['=DAY(DATE(2021, 9, 1))'], - ['=DAY(DATE(2021, 10, 1))'], - ['=DAY(DATE(2021, 11, 1))'], - ['=DAY(DATE(2021, 12, 1))'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - expect(engine.getCellValue(adr('A5'))).toEqual(1) - expect(engine.getCellValue(adr('A6'))).toEqual(1) - expect(engine.getCellValue(adr('A7'))).toEqual(1) - expect(engine.getCellValue(adr('A8'))).toEqual(1) - expect(engine.getCellValue(adr('A9'))).toEqual(1) - expect(engine.getCellValue(adr('A10'))).toEqual(1) - expect(engine.getCellValue(adr('A11'))).toEqual(1) - expect(engine.getCellValue(adr('A12'))).toEqual(1) - }) - - it('test for days in month, end of month', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAY(DATE(2021, 1, 31))'], - ['=DAY(DATE(2021, 2, 28))'], - ['=DAY(DATE(2021, 3, 31))'], - ['=DAY(DATE(2021, 4, 30))'], - ['=DAY(DATE(2021, 5, 31))'], - ['=DAY(DATE(2021, 6, 30))'], - ['=DAY(DATE(2021, 7, 31))'], - ['=DAY(DATE(2021, 8, 31))'], - ['=DAY(DATE(2021, 9, 30))'], - ['=DAY(DATE(2021, 10, 31))'], - ['=DAY(DATE(2021, 11, 30))'], - ['=DAY(DATE(2021, 12, 31))'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(31) - expect(engine.getCellValue(adr('A2'))).toEqual(28) - expect(engine.getCellValue(adr('A3'))).toEqual(31) - expect(engine.getCellValue(adr('A4'))).toEqual(30) - expect(engine.getCellValue(adr('A5'))).toEqual(31) - expect(engine.getCellValue(adr('A6'))).toEqual(30) - expect(engine.getCellValue(adr('A7'))).toEqual(31) - expect(engine.getCellValue(adr('A8'))).toEqual(31) - expect(engine.getCellValue(adr('A9'))).toEqual(30) - expect(engine.getCellValue(adr('A10'))).toEqual(31) - expect(engine.getCellValue(adr('A11'))).toEqual(30) - expect(engine.getCellValue(adr('A12'))).toEqual(31) - }) - - it('test for days in month, end of month+1', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAY(DATE(2021, 1, 31)+1)'], - ['=DAY(DATE(2021, 2, 28)+1)'], - ['=DAY(DATE(2021, 3, 31)+1)'], - ['=DAY(DATE(2021, 4, 30)+1)'], - ['=DAY(DATE(2021, 5, 31)+1)'], - ['=DAY(DATE(2021, 6, 30)+1)'], - ['=DAY(DATE(2021, 7, 31)+1)'], - ['=DAY(DATE(2021, 8, 31)+1)'], - ['=DAY(DATE(2021, 9, 30)+1)'], - ['=DAY(DATE(2021, 10, 31)+1)'], - ['=DAY(DATE(2021, 11, 30)+1)'], - ['=DAY(DATE(2021, 12, 31)+1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - expect(engine.getCellValue(adr('A5'))).toEqual(1) - expect(engine.getCellValue(adr('A6'))).toEqual(1) - expect(engine.getCellValue(adr('A7'))).toEqual(1) - expect(engine.getCellValue(adr('A8'))).toEqual(1) - expect(engine.getCellValue(adr('A9'))).toEqual(1) - expect(engine.getCellValue(adr('A10'))).toEqual(1) - expect(engine.getCellValue(adr('A11'))).toEqual(1) - expect(engine.getCellValue(adr('A12'))).toEqual(1) - }) - - it('test for days in month, start of month, leap year', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAY(DATE(2020, 1, 1))'], - ['=DAY(DATE(2020, 2, 1))'], - ['=DAY(DATE(2020, 3, 1))'], - ['=DAY(DATE(2020, 4, 1))'], - ['=DAY(DATE(2020, 5, 1))'], - ['=DAY(DATE(2020, 6, 1))'], - ['=DAY(DATE(2020, 7, 1))'], - ['=DAY(DATE(2020, 8, 1))'], - ['=DAY(DATE(2020, 9, 1))'], - ['=DAY(DATE(2020, 10, 1))'], - ['=DAY(DATE(2020, 11, 1))'], - ['=DAY(DATE(2020, 12, 1))'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - expect(engine.getCellValue(adr('A5'))).toEqual(1) - expect(engine.getCellValue(adr('A6'))).toEqual(1) - expect(engine.getCellValue(adr('A7'))).toEqual(1) - expect(engine.getCellValue(adr('A8'))).toEqual(1) - expect(engine.getCellValue(adr('A9'))).toEqual(1) - expect(engine.getCellValue(adr('A10'))).toEqual(1) - expect(engine.getCellValue(adr('A11'))).toEqual(1) - expect(engine.getCellValue(adr('A12'))).toEqual(1) - }) - - it('test for days in month, end of month, leap year', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAY(DATE(2020, 1, 31))'], - ['=DAY(DATE(2020, 2, 29))'], - ['=DAY(DATE(2020, 3, 31))'], - ['=DAY(DATE(2020, 4, 30))'], - ['=DAY(DATE(2020, 5, 31))'], - ['=DAY(DATE(2020, 6, 30))'], - ['=DAY(DATE(2020, 7, 31))'], - ['=DAY(DATE(2020, 8, 31))'], - ['=DAY(DATE(2020, 9, 30))'], - ['=DAY(DATE(2020, 10, 31))'], - ['=DAY(DATE(2020, 11, 30))'], - ['=DAY(DATE(2020, 12, 31))'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(31) - expect(engine.getCellValue(adr('A2'))).toEqual(29) - expect(engine.getCellValue(adr('A3'))).toEqual(31) - expect(engine.getCellValue(adr('A4'))).toEqual(30) - expect(engine.getCellValue(adr('A5'))).toEqual(31) - expect(engine.getCellValue(adr('A6'))).toEqual(30) - expect(engine.getCellValue(adr('A7'))).toEqual(31) - expect(engine.getCellValue(adr('A8'))).toEqual(31) - expect(engine.getCellValue(adr('A9'))).toEqual(30) - expect(engine.getCellValue(adr('A10'))).toEqual(31) - expect(engine.getCellValue(adr('A11'))).toEqual(30) - expect(engine.getCellValue(adr('A12'))).toEqual(31) - }) - - it('test for days in month, end of month+1, leap year', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAY(DATE(2020, 1, 31)+1)'], - ['=DAY(DATE(2020, 2, 29)+1)'], - ['=DAY(DATE(2020, 3, 31)+1)'], - ['=DAY(DATE(2020, 4, 30)+1)'], - ['=DAY(DATE(2020, 5, 31)+1)'], - ['=DAY(DATE(2020, 6, 30)+1)'], - ['=DAY(DATE(2020, 7, 31)+1)'], - ['=DAY(DATE(2020, 8, 31)+1)'], - ['=DAY(DATE(2020, 9, 30)+1)'], - ['=DAY(DATE(2020, 10, 31)+1)'], - ['=DAY(DATE(2020, 11, 30)+1)'], - ['=DAY(DATE(2020, 12, 31)+1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - expect(engine.getCellValue(adr('A5'))).toEqual(1) - expect(engine.getCellValue(adr('A6'))).toEqual(1) - expect(engine.getCellValue(adr('A7'))).toEqual(1) - expect(engine.getCellValue(adr('A8'))).toEqual(1) - expect(engine.getCellValue(adr('A9'))).toEqual(1) - expect(engine.getCellValue(adr('A10'))).toEqual(1) - expect(engine.getCellValue(adr('A11'))).toEqual(1) - expect(engine.getCellValue(adr('A12'))).toEqual(1) - }) - - it('returns 30 when its argument is a reference to an empty cell', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAY(B1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(30) - }) - - it('returns 31 when its argument is a reference to an empty cell and the engine is configured to use 1899-12-31 as a null year', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAY(B1)'], - ], { - leapYear1900: true, - nullDate: { year: 1899, month: 12, day: 31 }, - }) - - expect(engine.getCellValue(adr('A1'))).toEqual(31) - }) -}) diff --git a/test/unit/interpreter/function-days.spec.ts b/test/unit/interpreter/function-days.spec.ts deleted file mode 100644 index d31d00987c..0000000000 --- a/test/unit/interpreter/function-days.spec.ts +++ /dev/null @@ -1,75 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function DAYS', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAYS(1, 2, 3)'], - ['=DAYS(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for wrong type of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAYS("foo", 1)'], - ['=DAYS(2, "bar")'], - ['=DAYS(2, "12/30/2018")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work for strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAYS("30/12/2018", "30/12/2018")'], - ['=DAYS("31/12/2018", "30/12/2018")'], - ['=DAYS("30/12/2018", "31/12/2018")'], - ['=DAYS("28/02/2017", "28/02/2016")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(-1) - expect(engine.getCellValue(adr('A4'))).toEqual(366) - }) - it('ignores time', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAYS("30/12/2018 1:00am", "30/12/2018 11:00pm")'], - ['=DAYS("31/12/2018 1:00am", "30/12/2018 11:00pm")'], - ['=DAYS("30/12/2018 11:00pm", "31/12/2018 1:00am")'], - ['=DAYS("28/02/2017 11:00pm", "28/02/2016 1:00am")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(-1) - expect(engine.getCellValue(adr('A4'))).toEqual(366) - }) - - it('should work for numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAYS(20, 10)'], - ['=DAYS(12346, "28/02/2016")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(10) - expect(engine.getCellValue(adr('A2'))).toEqual(-30082) - }) - - //inconsistency with product 1 - it('fails for negative values', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAYS(-1, 0)'], - ['=DAYS(0, -1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-days360.spec.ts b/test/unit/interpreter/function-days360.spec.ts deleted file mode 100644 index 0ae351719a..0000000000 --- a/test/unit/interpreter/function-days360.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function DAYS360', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAYS360(1, 2, 3, 4)'], - ['=DAYS360(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for wrong type of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAYS360("foo", 1, TRUE())'], - ['=DAYS360(2, "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('US mode', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAYS360("30/03/2020", "31/03/2020")'], - ['=DAYS360("28/02/2020", "29/02/2020")'], - ['=DAYS360("29/02/2020", "01/03/2020")'], - ['=DAYS360("28/02/2021", "01/03/2021")'], - ['=DAYS360("31/03/2020", "30/03/2020")'], - ['=DAYS360("29/02/2020", "28/02/2020")'], - ['=DAYS360("01/03/2020", "29/02/2020")'], - ['=DAYS360("01/03/2021", "28/02/2021")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - expect(engine.getCellValue(adr('A6'))).toEqual(-2) - expect(engine.getCellValue(adr('A7'))).toEqual(-2) - expect(engine.getCellValue(adr('A8'))).toEqual(-3) - }) - - it('EU mode', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAYS360("30/03/2020", "31/03/2020", TRUE())'], - ['=DAYS360("28/02/2020", "29/02/2020", TRUE())'], - ['=DAYS360("29/02/2020", "01/03/2020", TRUE())'], - ['=DAYS360("28/02/2021", "01/03/2021", TRUE())'], - ['=DAYS360("31/03/2020", "30/03/2020", TRUE())'], - ['=DAYS360("29/02/2020", "28/02/2020", TRUE())'], - ['=DAYS360("01/03/2020", "29/02/2020", TRUE())'], - ['=DAYS360("01/03/2021", "28/02/2021", TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - expect(engine.getCellValue(adr('A4'))).toEqual(3) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - expect(engine.getCellValue(adr('A6'))).toEqual(-1) - expect(engine.getCellValue(adr('A7'))).toEqual(-2) - expect(engine.getCellValue(adr('A8'))).toEqual(-3) - }) -}) diff --git a/test/unit/interpreter/function-db.spec.ts b/test/unit/interpreter/function-db.spec.ts deleted file mode 100644 index a6bc17fb33..0000000000 --- a/test/unit/interpreter/function-db.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function DB', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DB(1, 1, 1)', '=DB(1, 1, 1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=DB(10000, 50, 10, 2, 12)', - '=DB(10000, 50, 10, 2)', - '=DB(10000, 50, 10, 2, 7)'], - ['=DB(10000, 50, 10, 1, 12)', - '=DB(10000, 50, 10, 1, 7)'], - ['=DB(10000, 50, 10, 10, 12)', - '=DB(10000, 50, 10, 10, 7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(2420.79) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(2420.79) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(3124.63) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(4110.00) - expect(engine.getCellValue(adr('B2'))).toBeCloseTo(2397.50) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(35.07) - expect(engine.getCellValue(adr('B3'))).toBeCloseTo(45.26) - }) - - it('compatibility', () => { - const engine = HyperFormula.buildFromArray([ - ['=DB(1000000, 100000, 6, 7, 7)', - '=DB(1000000, 100000, 6, 8, 7)', - '=DB(1000000, 100000, 6, 7)', ], - ]) - //product #1 returns #NUM! instead of an actual value - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(15845.10) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.PeriodLong)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.PeriodLong)) - }) - - it('should return zero if salvage >= cost', () => { - const engine = HyperFormula.buildFromArray([ - ['=DB(10000, 10000, 10, 2, 12)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) -}) diff --git a/test/unit/interpreter/function-ddb.spec.ts b/test/unit/interpreter/function-ddb.spec.ts deleted file mode 100644 index 28901dbfb5..0000000000 --- a/test/unit/interpreter/function-ddb.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function DDB', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DDB(1, 1, 1)', '=DDB(1, 1, 1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=DDB(10000, 50, 10, 2, 1)', - '=DDB(10000, 50, 10, 2)', - '=DDB(10000, 50, 10, 2, 1.2)', - '=DDB(10000, 50, 10, 2, 2.5)', - '=DDB(10000, 10010, 10, 2, 2.5)', - '=DDB(2, 1, 20, 10, 60)', - ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(900) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(1600) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(1056) - expect(engine.getCellValue(adr('D1'))).toBeCloseTo(1875) - expect(engine.getCellValue(adr('E1'))).toBeCloseTo(0) - expect(engine.getCellValue(adr('F1'))).toBeCloseTo(0) - }) - - it('should return correct value for fractional period', () => { - const engine = HyperFormula.buildFromArray([ - ['=DDB(10000, 50, 10, 2.5, 1)', - '=DDB(10000, 50, 10, 2.1)', - '=DDB(10000, 50, 10, 2.9, 1.2)', - '=DDB(10000, 50, 10, 2.5, 2.5)', - '=DDB(10000, 10010, 10, 2.1, 2.5)', - '=DDB(2, 1, 20, 10.9, 60)', - '=DDB(2, 1, 20, 1, 60)', - ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(853.8149682, 6) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(1564.69243, 6) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(941.2355527, 6) - expect(engine.getCellValue(adr('D1'))).toBeCloseTo(1623.797632, 6) - expect(engine.getCellValue(adr('E1'))).toBeCloseTo(0) - expect(engine.getCellValue(adr('F1'))).toBeCloseTo(0) - expect(engine.getCellValue(adr('G1'))).toEqual(1) - }) - - it('should return error when args are incorrect', () => { - const engine = HyperFormula.buildFromArray([ - ['=DDB(10000, 50, 2, 10, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM)) - }) -}) diff --git a/test/unit/interpreter/function-dec2bin.spec.ts b/test/unit/interpreter/function-dec2bin.spec.ts deleted file mode 100644 index 4d267b4307..0000000000 --- a/test/unit/interpreter/function-dec2bin.spec.ts +++ /dev/null @@ -1,117 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function DEC2BIN', () => { - it('should return error when wrong type of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2BIN("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return error when wrong number of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2BIN("foo", 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2BIN(1)'], - ['=DEC2BIN(2)'], - ['=DEC2BIN(98)'], - ['=DEC2BIN(-12)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - expect(engine.getCellValue(adr('A2'))).toEqual('10') - expect(engine.getCellValue(adr('A3'))).toEqual('1100010') - expect(engine.getCellValue(adr('A4'))).toEqual('1111110100') - }) - - it('should work for numeric strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2BIN("123")'], - ['=DEC2BIN("-15")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1111011') - expect(engine.getCellValue(adr('A2'))).toEqual('1111110001') - }) - - it('should work for reference', () => { - const engine = HyperFormula.buildFromArray([ - ['12'], - ['=DEC2BIN(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual('1100') - }) - - it('should return string value', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2BIN(123)'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.STRING) - }) - - it('should work for numbers between -512 and 511', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2BIN(-513)'], - ['=DEC2BIN(-512)'], - ['=DEC2BIN(511)'], - ['=DEC2BIN(512)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseSmall)) - expect(engine.getCellValue(adr('A2'))).toEqual('1000000000') - expect(engine.getCellValue(adr('A3'))).toEqual('111111111') - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLarge)) - }) - - it('should respect second argument and fill with zeros for positive arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2BIN(2, 8)'], - ['=DEC2BIN(5, "4")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('00000010') - expect(engine.getCellValue(adr('A2'))).toEqual('0101') - }) - - it('should fail if the result is longer than the desired number of digits', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2BIN(50, 1)'], - ['=DEC2BIN(777, "4")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLong)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLarge)) - }) - - it('should ignore second argument for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2BIN(-2, 1)'], - ['=DEC2BIN(-2, 10)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1111111110') - expect(engine.getCellValue(adr('A2'))).toEqual('1111111110') - }) - - it('should allow for numbers from 1 to 10 as second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2BIN(2, 0)'], - ['=DEC2BIN(-2, 12)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-dec2hex.spec.ts b/test/unit/interpreter/function-dec2hex.spec.ts deleted file mode 100644 index 4ef24cff81..0000000000 --- a/test/unit/interpreter/function-dec2hex.spec.ts +++ /dev/null @@ -1,107 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function DEC2HEX', () => { - it('should return error when wrong type of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2HEX("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return error when wrong number of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2HEX("foo", 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2HEX(1)'], - ['=DEC2HEX(50)'], - ['=DEC2HEX(122)'], - ['=DEC2HEX(-154)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - expect(engine.getCellValue(adr('A2'))).toEqual('32') - expect(engine.getCellValue(adr('A3'))).toEqual('7A') - expect(engine.getCellValue(adr('A4'))).toEqual('FFFFFFFF66') - }) - - it('should work for numeric strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2HEX("123")'], - ['=DEC2HEX("-15")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('7B') - expect(engine.getCellValue(adr('A2'))).toEqual('FFFFFFFFF1') - }) - - it('should work for reference', () => { - const engine = HyperFormula.buildFromArray([ - ['12'], - ['=DEC2HEX(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual('C') - }) - - it('should return string value', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2HEX(123)'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.STRING) - }) - - it('should work for numbers fitting in 10 bits', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2HEX(-549755813889)'], - ['=DEC2HEX(-549755813888)'], - ['=DEC2HEX(549755813887)'], - ['=DEC2HEX(549755813888)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseSmall)) - expect(engine.getCellValue(adr('A2'))).toEqual('8000000000') - expect(engine.getCellValue(adr('A3'))).toEqual('7FFFFFFFFF') - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLarge)) - }) - - it('should respect second argument and fill with zeros for positive arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2HEX(2, 8)'], - ['=DEC2HEX(20, "4")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('00000002') - expect(engine.getCellValue(adr('A2'))).toEqual('0014') - }) - - it('should ignore second argument for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2HEX(-2, 1)'], - ['=DEC2HEX(-2, 10)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('FFFFFFFFFE') - expect(engine.getCellValue(adr('A2'))).toEqual('FFFFFFFFFE') - }) - - it('should allow for numbers from 1 to 10 as second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2HEX(2, 0)'], - ['=DEC2HEX(-2, 12)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-dec2oct.spec.ts b/test/unit/interpreter/function-dec2oct.spec.ts deleted file mode 100644 index 0e0b326812..0000000000 --- a/test/unit/interpreter/function-dec2oct.spec.ts +++ /dev/null @@ -1,107 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function DEC2OCT', () => { - it('should return error when wrong type of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2OCT("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return error when wrong number of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2OCT("foo", 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2OCT(1)'], - ['=DEC2OCT(10)'], - ['=DEC2OCT(98)'], - ['=DEC2OCT(-12)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - expect(engine.getCellValue(adr('A2'))).toEqual('12') - expect(engine.getCellValue(adr('A3'))).toEqual('142') - expect(engine.getCellValue(adr('A4'))).toEqual('7777777764') - }) - - it('should work for numeric strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2OCT("123")'], - ['=DEC2OCT("-15")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('173') - expect(engine.getCellValue(adr('A2'))).toEqual('7777777761') - }) - - it('should work for reference', () => { - const engine = HyperFormula.buildFromArray([ - ['12'], - ['=DEC2OCT(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual('14') - }) - - it('should return string value', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2OCT(123)'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.STRING) - }) - - it('should work for numbers fitting in 10 bits', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2OCT(-536870913)'], - ['=DEC2OCT(-536870912)'], - ['=DEC2OCT(536870911)'], - ['=DEC2OCT(536870912)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseSmall)) - expect(engine.getCellValue(adr('A2'))).toEqual('4000000000') - expect(engine.getCellValue(adr('A3'))).toEqual('3777777777') - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLarge)) - }) - - it('should respect second argument and fill with zeros for positive arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2OCT(2, 8)'], - ['=DEC2OCT(5, "4")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('00000002') - expect(engine.getCellValue(adr('A2'))).toEqual('0005') - }) - - it('should ignore second argument for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2OCT(-2, 1)'], - ['=DEC2OCT(-2, 10)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('7777777776') - expect(engine.getCellValue(adr('A2'))).toEqual('7777777776') - }) - - it('should allow for numbers from 1 to 10 as second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEC2OCT(2, 0)'], - ['=DEC2OCT(-2, 12)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-decimal.spec.ts b/test/unit/interpreter/function-decimal.spec.ts deleted file mode 100644 index 8e3e37346f..0000000000 --- a/test/unit/interpreter/function-decimal.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function DECIMAL', () => { - it('should return error when wrong number of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=DECIMAL(123)'], - ['=DECIMAL("foo", 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error when value does not correspond to base', () => { - const engine = HyperFormula.buildFromArray([ - ['=DECIMAL(12, 2)'], - ['=DECIMAL("123XYZ", 33)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=DECIMAL(10, 2)'], - ['=DECIMAL("11111111111111111111111111111111110", 2)'], - ['=DECIMAL(123, 4)'], - ['=DECIMAL("C0FFEE", 16)'], - ['=DECIMAL("C0FFEE", 25)'], - ['=DECIMAL("89WPQ", 33)'], - ['=DECIMAL("123XYZ", 36)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(engine.getCellValue(adr('A2'))).toEqual(34359738366) - expect(engine.getCellValue(adr('A3'))).toEqual(27) - expect(engine.getCellValue(adr('A4'))).toEqual(12648430) - expect(engine.getCellValue(adr('A5'))).toEqual(117431614) - expect(engine.getCellValue(adr('A6'))).toEqual(9846500) - expect(engine.getCellValue(adr('A7'))).toEqual(64009403) - }) - - it('should work for of max length 255', () => { - const longNumber = '1'.repeat(255) - const tooLongNumber = '1'.repeat(256) - const engine = HyperFormula.buildFromArray([ - [`=DECIMAL(\"${longNumber}\", 2)`], - [`=DECIMAL(\"${tooLongNumber}\", 2)`], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(5.78960446186581e+76, -66) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex)) - }) - - it('should work only for bases from 2 to 36', () => { - const engine = HyperFormula.buildFromArray([ - ['=DECIMAL("0", 0)'], - ['=DECIMAL("10", 2)'], - ['=DECIMAL("XYZ", 36)'], - ['=DECIMAL("XYZ", 37)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - expect(engine.getCellValue(adr('A3'))).toEqual(44027) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('should return number', () => { - const engine = HyperFormula.buildFromArray([ - ['=DECIMAL("123", 4)'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toEqual(CellValueType.NUMBER) - }) -}) diff --git a/test/unit/interpreter/function-degrees.spec.ts b/test/unit/interpreter/function-degrees.spec.ts deleted file mode 100644 index ecd7fc2475..0000000000 --- a/test/unit/interpreter/function-degrees.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function DEGREES', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEGREES(0)', '=DEGREES(3.14)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(179.9087477) - }) - - it('given wrong argument type', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEGREES("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="3.14"', '=DEGREES(A1)'], - ['=TRUE()', '=DEGREES(A2)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(179.9087477) - expect(engine.getCellValue(adr('B2'))).toBeCloseTo(57.29577951) - }) - - it('given wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEGREES()'], - ['=DEGREES(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEGREES(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-delta.spec.ts b/test/unit/interpreter/function-delta.spec.ts deleted file mode 100644 index 9a5559f847..0000000000 --- a/test/unit/interpreter/function-delta.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function DELTA', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DELTA()'], - ['=DELTA(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for wrong type of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DELTA("foo")'], - ['=DELTA(1, "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should compare to 0 if one argument provided', () => { - const engine = HyperFormula.buildFromArray([ - ['=DELTA(0)'], - ['=DELTA("123")'], - ['=DELTA(FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - }) - - it('should compare two arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DELTA(1, 0)'], - ['=DELTA(2, 2)'], - ['=DELTA(123, "123")'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - }) - - it('should return number', () => { - const engine = HyperFormula.buildFromArray([ - ['=DELTA(3, 3)'], - ['=DELTA("123")'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toEqual(CellValueType.NUMBER) - expect(engine.getCellValueType(adr('A2'))).toEqual(CellValueType.NUMBER) - }) -}) diff --git a/test/unit/interpreter/function-devsq.spec.ts b/test/unit/interpreter/function-devsq.spec.ts deleted file mode 100644 index 5eb51652e2..0000000000 --- a/test/unit/interpreter/function-devsq.spec.ts +++ /dev/null @@ -1,85 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {adr, detailedError} from '../testUtils' - -describe('Function DEVSQ', () => { - it('single number', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEVSQ(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) - - it('two numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEVSQ(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.5) - }) - - it('more numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEVSQ(3, 1, 2, 4, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(10) - }) - - it('works with ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '9', '0'], - ['=DEVSQ(A1:C1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(54) - }) - - it('propagates error from regular argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=3/0', '=DEVSQ(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('propagates first error from range argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=3/0', '=FOO(', '=DEVSQ(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - //inconsistency with product #2 - it('returns 0 for empty ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEVSQ(A2:A3)'], - [null], - [null], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) - - /** - * product #1 does not coerce the input - */ - it('does coercions of nonnumeric explicit arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEVSQ(TRUE(),FALSE(),)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.666666666666667, 6) - }) - - it('ignores nonnumeric values in ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['=DEVSQ(A2:D2)'], - [0, 1, false, null, '\'0'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.5) - }) -}) diff --git a/test/unit/interpreter/function-dollarde.spec.ts b/test/unit/interpreter/function-dollarde.spec.ts deleted file mode 100644 index 5c2ba5874e..0000000000 --- a/test/unit/interpreter/function-dollarde.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function DOLLARDE', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DOLLARDE(1)', '=DOLLARDE(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('div/0 when second argument too small', () => { - const engine = HyperFormula.buildFromArray([ - ['=DOLLARDE(1,0)', '=DOLLARDE(1, 0.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - [ - '=DOLLARDE(1.01, 8)', - '=DOLLARDE(1.0000001, 8.9)', - '=DOLLARDE(1.1, 100001)', - '=DOLLARDE(1.1, 100000)', - '=DOLLARDE(123.456, 2)', - '=DOLLARDE(1.9, 2)', - '=DOLLARDE(1.01,10.1)', - ], - [ - '=DOLLARDE(-1.01, 8)', - '=DOLLARDE(-1.0000001, 8.9)', - '=DOLLARDE(-1.1, 100001)', - '=DOLLARDE(-1.1, 100000)', - '=DOLLARDE(-123.456, 2)', - '=DOLLARDE(-1.9, 2)', - '=DOLLARDE(-1.01,10.1)', - ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.0125) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(1.000000125) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(1.9999900001) - expect(engine.getCellValue(adr('D1'))).toBeCloseTo(1.1) - expect(engine.getCellValue(adr('E1'))).toBeCloseTo(125.28) - expect(engine.getCellValue(adr('F1'))).toBeCloseTo(5.5) - expect(engine.getCellValue(adr('G1'))).toBeCloseTo(1.01) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(-1.0125) - expect(engine.getCellValue(adr('B2'))).toBeCloseTo(-1.000000125) - expect(engine.getCellValue(adr('C2'))).toBeCloseTo(-1.9999900001) - expect(engine.getCellValue(adr('D2'))).toBeCloseTo(-1.1) - expect(engine.getCellValue(adr('E2'))).toBeCloseTo(-125.28) - expect(engine.getCellValue(adr('F2'))).toBeCloseTo(-5.5) - expect(engine.getCellValue(adr('G2'))).toBeCloseTo(-1.01) - }) -}) diff --git a/test/unit/interpreter/function-dollarfr.spec.ts b/test/unit/interpreter/function-dollarfr.spec.ts deleted file mode 100644 index b90a6296d3..0000000000 --- a/test/unit/interpreter/function-dollarfr.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function DOLLARFR', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DOLLARFR(1)', '=DOLLARFR(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('div/0 when second argument too small', () => { - const engine = HyperFormula.buildFromArray([ - ['=DOLLARFR(1,0)', '=DOLLARFR(1, 0.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - [ - '=DOLLARFR(1.0125, 8)', - '=DOLLARFR(1.000000125, 8.9)', - '=DOLLARFR(1.9999900001, 100001)', - '=DOLLARFR(1.1, 100000)', - '=DOLLARFR(125.28, 2)', - '=DOLLARFR(5.5, 2)', - '=DOLLARFR(1.01,10.1)', - ], - [ - '=DOLLARFR(-1.0125, 8)', - '=DOLLARFR(-1.000000125, 8.9)', - '=DOLLARFR(-1.9999900001, 100001)', - '=DOLLARFR(-1.1, 100000)', - '=DOLLARFR(-125.28, 2)', - '=DOLLARFR(-5.5, 2)', - '=DOLLARFR(-1.01,10.1)', - ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.01) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(1.0000001) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(1.1) - expect(engine.getCellValue(adr('D1'))).toBeCloseTo(1.1) - expect(engine.getCellValue(adr('E1'))).toBeCloseTo(125.056) - expect(engine.getCellValue(adr('F1'))).toBeCloseTo(5.1) - expect(engine.getCellValue(adr('G1'))).toBeCloseTo(1.01) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(-1.01) - expect(engine.getCellValue(adr('B2'))).toBeCloseTo(-1.0000001) - expect(engine.getCellValue(adr('C2'))).toBeCloseTo(-1.1) - expect(engine.getCellValue(adr('D2'))).toBeCloseTo(-1.1) - expect(engine.getCellValue(adr('E2'))).toBeCloseTo(-125.056) - expect(engine.getCellValue(adr('F2'))).toBeCloseTo(-5.1) - expect(engine.getCellValue(adr('G2'))).toBeCloseTo(-1.01) - }) -}) diff --git a/test/unit/interpreter/function-edate.spec.ts b/test/unit/interpreter/function-edate.spec.ts deleted file mode 100644 index 60fd5b12d6..0000000000 --- a/test/unit/interpreter/function-edate.spec.ts +++ /dev/null @@ -1,136 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueDetailedType, ErrorType} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectCellValueToEqualDate} from '../testUtils' - -describe('Function EDATE', () => { - it('validate arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 3, 31)'], - ['=EDATE("foo", 0)'], - ['=EDATE(A1, "bar")'], - ['=EDATE(A1)'], - ['=EDATE(A1, "bar", "baz")'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works for 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 3, 10)'], - ['=EDATE(A1, 0)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '10/03/2019') - expect(engine.getCellValueDetailedType(adr('A2'))).toBe(CellValueDetailedType.NUMBER_DATE) - }) - - it('works for exact end of month', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 3, 31)'], - ['=EDATE(A1, 0)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '31/03/2019') - }) - - it('works for positive numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 7, 31)'], - ['=EDATE(A1, 1)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '31/08/2019') - }) - - it('should return NUMBER_DATE', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 7, 31)'], - ['=EDATE(A1, 1)'], - ]) - - expect(engine.getCellValueDetailedType(adr('A2'))).toBe(CellValueDetailedType.NUMBER_DATE) - }) - - it('works for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 8, 31)'], - ['=EDATE(A1, -1)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '31/07/2019') - }) - - it('works when next date will have more days', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 6, 30)'], - ['=EDATE(A1, 1)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '30/07/2019') - }) - - it('works when next date will have less days', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 1, 31)'], - ['=EDATE(A1, 1)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '28/02/2019') - }) - - it('works when previous date will have more days', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 2, 28)'], - ['=EDATE(A1, -1)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '28/01/2019') - }) - - it('works when previous date will have less days', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 3, 31)'], - ['=EDATE(A1, -1)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '28/02/2019') - }) - - it('use number coercion for 1st argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=EDATE(TRUE(), 1)'], - ['=EDATE(1, 1)'], - ]) - - expectCellValueToEqualDate(engine, adr('A1'), '31/01/1900') - expectCellValueToEqualDate(engine, adr('A2'), '31/01/1900') - }) - - it('use number coercion for 2nd argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 3, 31)'], - ['="1"', '=EDATE(A1, A2)'], - ['=TRUE()', '=EDATE(A1, A3)'], - ]) - - expectCellValueToEqualDate(engine, adr('B2'), '30/04/2019') - expectCellValueToEqualDate(engine, adr('B3'), '30/04/2019') - }) - - it('propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=EDATE(4/0, 0)'], - ['=EDATE(0, 4/0)'], - ['=EDATE(4/0, FOOBAR())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-effect.spec.ts b/test/unit/interpreter/function-effect.spec.ts deleted file mode 100644 index ecd4fde353..0000000000 --- a/test/unit/interpreter/function-effect.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function EFFECT', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=EFFECT(1)', '=EFFECT(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=EFFECT(2%, 1)', '=EFFECT(2%, 2)', '=EFFECT(2%, 2.9)', '=EFFECT(2%, 24)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.02, 9) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.0201, 9) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(0.0201, 9) - expect(engine.getCellValue(adr('D1'))).toBeCloseTo(0.0201928431045086, 9) - }) -}) diff --git a/test/unit/interpreter/function-eomonth.spec.ts b/test/unit/interpreter/function-eomonth.spec.ts deleted file mode 100644 index 5c92ea2aa8..0000000000 --- a/test/unit/interpreter/function-eomonth.spec.ts +++ /dev/null @@ -1,154 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueDetailedType, ErrorType} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectCellValueToEqualDate} from '../testUtils' - -describe('Function EOMONTH', () => { - it('validate arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 3, 31)'], - ['=EOMONTH("foo", 0)'], - ['=EOMONTH(A1, "bar")'], - ['=EOMONTH(A1)'], - ['=EOMONTH(A1, "bar", "baz")'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return NUMBER_DATE', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 7, 31)'], - ['=EOMONTH(A1, 1)'], - ]) - - expect(engine.getCellValueDetailedType(adr('A2'))).toBe(CellValueDetailedType.NUMBER_DATE) - }) - - it('works for 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 3, 10)'], - ['=EOMONTH(A1, 0)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '31/03/2019') - expect(engine.getCellValueDetailedType(adr('A2'))).toBe(CellValueDetailedType.NUMBER_DATE) - }) - - it('works for exact end of month', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 3, 31)'], - ['=EOMONTH(A1, 0)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '31/03/2019') - }) - - it('works for positive numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 7, 31)'], - ['=EOMONTH(A1, 1)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '31/08/2019') - }) - - it('works for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 8, 31)'], - ['=EOMONTH(A1, -1)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '31/07/2019') - }) - - it('works when next date will have more days', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 6, 30)'], - ['=EOMONTH(A1, 1)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '31/07/2019') - }) - - it('works when next date will have less days', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 1, 31)'], - ['=EOMONTH(A1, 1)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '28/02/2019') - }) - - it('works when previous date will have more days', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 2, 28)'], - ['=EOMONTH(A1, -1)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '31/01/2019') - }) - - it('works when previous date will have less days', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 3, 31)'], - ['=EOMONTH(A1, -1)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '28/02/2019') - }) - - it('works for leap years', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2020, 2, 28)'], - ['=EOMONTH(A1, 0)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '29/02/2020') - }) - - it('works for non-leap years', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 2, 28)'], - ['=EOMONTH(A1, 0)'], - ]) - - expectCellValueToEqualDate(engine, adr('A2'), '28/02/2019') - }) - - it('use number coercion for 1st argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=EOMONTH(TRUE(), 1)'], - ['=EOMONTH(1, 1)'], - ]) - - expectCellValueToEqualDate(engine, adr('A1'), '31/01/1900') - expectCellValueToEqualDate(engine, adr('A2'), '31/01/1900') - }) - - it('use number coercion for 2nd argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(2019, 3, 31)'], - ['="1"', '=EOMONTH(A1, A2)'], - ['=TRUE()', '=EOMONTH(A1, A3)'], - ]) - - expectCellValueToEqualDate(engine, adr('B2'), '30/04/2019') - expectCellValueToEqualDate(engine, adr('B3'), '30/04/2019') - }) - - it('propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=EOMONTH(4/0, 0)'], - ['=EOMONTH(0, 4/0)'], - ['=EOMONTH(4/0, FOOBAR())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-erf.spec.ts b/test/unit/interpreter/function-erf.spec.ts deleted file mode 100644 index 9ef083c227..0000000000 --- a/test/unit/interpreter/function-erf.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ERF', () => { - - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ERF()'], - ['=ERF(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=ERF("foo")'], - ['=ERF(1, "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work for single argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ERF(0)'], - ['=ERF(1)'], - ['=ERF(3.14)'], - ['=ERF(-2.56)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.9999910304344467, 6) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(-0.999705836979508, 6) - }) - - it('should work with second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ERF(-2.3, -0.7)'], - ['=ERF(-2.3, 2)'], - ['=ERF(5.6, -3.1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.32105562956522493, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(1.9941790884215962, 6) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(-1.9999883513426304, 6) - }) -}) diff --git a/test/unit/interpreter/function-erfc.spec.ts b/test/unit/interpreter/function-erfc.spec.ts deleted file mode 100644 index c3beafce27..0000000000 --- a/test/unit/interpreter/function-erfc.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ERFC', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ERFC()'], - ['=ERFC(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=ERFC("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=ERFC(0)'], - ['=ERFC(2)'], - ['=ERFC(0.5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.004677734981047288, 6) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.4795001221869535, 6) - }) - - it('should work for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=ERFC(-10.123)'], - ['=ERFC(-14.8)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(2) - expect(engine.getCellValue(adr('A2'))).toBe(2) - }) -}) diff --git a/test/unit/interpreter/function-even.spec.ts b/test/unit/interpreter/function-even.spec.ts deleted file mode 100644 index d81a98f51d..0000000000 --- a/test/unit/interpreter/function-even.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function EVEN', () => { - it('number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=EVEN()', '=EVEN(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works for positive numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=EVEN(0.3)', '=EVEN(1.7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(2) - expect(engine.getCellValue(adr('B1'))).toBe(2) - }) - - it('works for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=EVEN(-0.3)', '=EVEN(-1.7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(-2) - expect(engine.getCellValue(adr('B1'))).toBe(-2) - }) - - it('use coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['=EVEN("42.3")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(44) - }) - - it('propagates error', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/0'], - ['=EVEN(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-exact.spec.ts b/test/unit/interpreter/function-exact.spec.ts deleted file mode 100644 index 27fd373d6e..0000000000 --- a/test/unit/interpreter/function-exact.spec.ts +++ /dev/null @@ -1,71 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function EXACT', () => { - it('should take two arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=EXACT("foo")'], - ['=EXACT("foo", "bar", "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should compare strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=EXACT(B1, C1)', '', ''], - ['=EXACT(B2, C2)', 'foo', 'foo'], - ['=EXACT(B3, C3)', 'foo', 'fo'], - ['=EXACT(B4, C4)', 'foo', 'bar'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('A2'))).toBe(true) - expect(engine.getCellValue(adr('A3'))).toBe(false) - expect(engine.getCellValue(adr('A4'))).toBe(false) - }) - - it('should be case/accent sensitive', () => { - const engine = HyperFormula.buildFromArray([ - ['=EXACT(B1, C1)', 'foo', 'FOO'], - ['=EXACT(B2, C2)', 'foo', 'fóó'], - ], {caseSensitive: false}) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('A2'))).toBe(false) - }) - - it('should be case sensitive', () => { - const engine = HyperFormula.buildFromArray([ - ['=EXACT(B1, C1)', 'foo', 'Foo'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - }) - - it('should coerce', () => { - const engine = HyperFormula.buildFromArray([ - ['=EXACT(,)'], - ['=EXACT(B2, "0")', 0], - ['=EXACT(B3, "")', null], - ['=EXACT(B4, "TRUE")', '=TRUE()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('A2'))).toBe(true) - expect(engine.getCellValue(adr('A3'))).toBe(true) - expect(engine.getCellValue(adr('A4'))).toBe(true) - }) - - it('should return error for range', () => { - const engine = HyperFormula.buildFromArray([ - ['=EXACT("foo",B1:C1)'], - ['=EXACT(B1:C1,"foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-exp.spec.ts b/test/unit/interpreter/function-exp.spec.ts deleted file mode 100644 index 5fdf8bd0dd..0000000000 --- a/test/unit/interpreter/function-exp.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function EXP', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([ - ['=EXP(0)', '=EXP(2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(7.38905609893065) - }) - - it('given wrong argument type', () => { - const engine = HyperFormula.buildFromArray([ - ['=EXP("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="2"', '=EXP(A1)'], - ['=FALSE()', '=EXP(A2)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(7.38905609893065) - expect(engine.getCellValue(adr('B2'))).toEqual(1) - }) - - it('given wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=EXP()'], - ['=EXP(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=EXP(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-expon.dist.spec.ts b/test/unit/interpreter/function-expon.dist.spec.ts deleted file mode 100644 index c14c4ebb16..0000000000 --- a/test/unit/interpreter/function-expon.dist.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function EXPON.DIST', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=EXPON.DIST(1, 2)'], - ['=EXPON.DIST(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=EXPON.DIST("foo", 2, TRUE())'], - ['=EXPON.DIST(1, "baz", TRUE())'], - ['=EXPON.DIST(1, 2, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=EXPON.DIST(1, 1, TRUE())'], - ['=EXPON.DIST(3, 2, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.632120558828558, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.997521247823334, 6) - }) - - it('should work as pdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=EXPON.DIST(1, 1, FALSE())'], - ['=EXPON.DIST(3, 2, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.367879441171442, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.00495750435333272, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=EXPON.DIST(0, 1, FALSE())'], - ['=EXPON.DIST(-0.00001, 1, FALSE())'], - ['=EXPON.DIST(1, 0, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-f.dist.rt.spec.ts b/test/unit/interpreter/function-f.dist.rt.spec.ts deleted file mode 100644 index 68d6fc38fa..0000000000 --- a/test/unit/interpreter/function-f.dist.rt.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function F.DIST.RT', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.DIST.RT(1, 2)'], - ['=F.DIST.RT(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.DIST.RT("foo", 2, 3)'], - ['=F.DIST.RT(1, "baz", 3)'], - ['=F.DIST.RT(1, 2, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.DIST.RT(1, 1, 1)'], - ['=F.DIST.RT(3, 2, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.5, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.25, 6) - }) - - it('truncates second and third args', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.DIST.RT(1, 1.9, 1)'], - ['=F.DIST.RT(3, 2, 2.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.5, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.25, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.DIST.RT(0, 1, 1)'], - ['=F.DIST.RT(-0.001, 1, 1)'], - ['=F.DIST.RT(0, 0.999, 1)'], - ['=F.DIST.RT(0, 1, 0.999)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-f.dist.spec.ts b/test/unit/interpreter/function-f.dist.spec.ts deleted file mode 100644 index 1122561bdd..0000000000 --- a/test/unit/interpreter/function-f.dist.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function F.DIST', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.DIST(1, 2, 3)'], - ['=F.DIST(1, 2, 3, 4, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.DIST("foo", 2, 3, TRUE())'], - ['=F.DIST(1, "baz", 3, TRUE())'], - ['=F.DIST(1, 2, "abcd", TRUE())'], - ['=F.DIST(1, 2, 3, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.DIST(1, 1, 1, TRUE())'], - ['=F.DIST(3, 2, 2, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.5, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.75, 6) - }) - - it('should work as pdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.DIST(1, 1, 1, FALSE())'], - ['=F.DIST(3, 2, 2, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.159154942198517, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0625, 6) - }) - - it('truncates second and third arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.DIST(1, 1.9, 1, FALSE())'], - ['=F.DIST(3, 2, 2.9, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.159154942198517, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0625, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.DIST(0, 1, 1, FALSE())'], - ['=F.DIST(-0.001, 1, 1, FALSE())'], - ['=F.DIST(0, 0.999, 1, FALSE())'], - ['=F.DIST(0, 1, 0.999, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-f.inv.rt.spec.ts b/test/unit/interpreter/function-f.inv.rt.spec.ts deleted file mode 100644 index 8e82250b0d..0000000000 --- a/test/unit/interpreter/function-f.inv.rt.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function F.INV.RT', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.INV.RT(1, 2)'], - ['=F.INV.RT(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.INV.RT("foo", 2, 3)'], - ['=F.INV.RT(1, "baz", 3)'], - ['=F.INV.RT(1, 2, "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.INV.RT(0.1, 1, 1)'], - ['=F.INV.RT(0.9, 2, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(39.8634581890474, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.111111111111111, 6) - }) - - it('truncates second and third arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.INV.RT(0.1, 1.9, 1)'], - ['=F.INV.RT(0.9, 2, 2.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(39.8634581890474, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.111111111111111, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.INV.RT(0.5, 0.999, 1)'], - ['=F.INV.RT(0.5, 1, 0.999)'], - ['=F.INV.RT(-0.0001, 2, 1)'], - ['=F.INV.RT(1.0001, 2, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-f.inv.spec.ts b/test/unit/interpreter/function-f.inv.spec.ts deleted file mode 100644 index 846421eb60..0000000000 --- a/test/unit/interpreter/function-f.inv.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function F.INV', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.INV(1, 2)'], - ['=F.INV(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.INV("foo", 2, 3)'], - ['=F.INV(1, "baz", 3)'], - ['=F.INV(1, 2, "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.INV(0.1, 1, 1)'], - ['=F.INV(0.9, 2, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0250856309369253, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(9, 6) - }) - - it('truncates second and third arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.INV(0.1, 1.9, 1)'], - ['=F.INV(0.9, 2, 2.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0250856309369253, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(9, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.INV(0.5, 0.999, 1)'], - ['=F.INV(0.5, 1, 0.999)'], - ['=F.INV(-0.0001, 2, 1)'], - ['=F.INV(1.0001, 2, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-f.test.spec.ts b/test/unit/interpreter/function-f.test.spec.ts deleted file mode 100644 index 4bdda704c9..0000000000 --- a/test/unit/interpreter/function-f.test.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('F.TEST', () => { - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.TEST(1)'], - ['=F.TEST(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['2', '5'], - ['=F.TEST(A1:A2, B1:B2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.2513318328, 6) - }) - - it('works for uneven ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1'], - ['2', '3'], - [null, '1'], - ['=F.TEST(A1:A2, B1:B3)'] - ]) - - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(0.794719414238988, 6) - }) - - it('doesnt do coercions, nonnumeric values are skipped', () => { - const engine = HyperFormula.buildFromArray([ - ['5', '3'], - [null, '6'], - [true, false], - ['8'], - ['=F.TEST(A1:A4, B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(1, 6) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['=4/0', '50'], - ['3', '30'], - ['=F.TEST(A1:A3, B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('error when not enough data', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.TEST(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('error when 0 variance', () => { - const engine = HyperFormula.buildFromArray([ - ['=F.TEST(A2:C2, A3:C3)'], - [1, 1, 1], - [0, 1, 0], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-fact.spec.ts b/test/unit/interpreter/function-fact.spec.ts deleted file mode 100644 index fe4da366e5..0000000000 --- a/test/unit/interpreter/function-fact.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function FACT', () => { - it('checks number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=FACT()', '=FACT(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['=FACT(0)'], - ['=FACT(1)'], - ['=FACT(10)'], - ['=FACT(170)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('A2'))).toBe(1) - expect(engine.getCellValue(adr('A3'))).toBe(3628800) - expect(engine.getCellValue(adr('A4')) as number / 7.257415615307999e+306).toBeCloseTo(1, 6) - }) - - it('rounds argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=FACT(0.9)'], - ['=FACT(1.1)'], - ['=FACT(10.42)'], - ['=FACT(169.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('A2'))).toBe(1) - expect(engine.getCellValue(adr('A3'))).toBe(3628800) - expect(engine.getCellValue(adr('A4')) as number / 4.2690680090046997e+304).toBeCloseTo(1, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=FACT(-1)'], - ['=FACT(171)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('uses coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['=FACT("0")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - }) - - it('propagates error', () => { - const engine = HyperFormula.buildFromArray([ - ['=FACT(NA())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-factdouble.spec.ts b/test/unit/interpreter/function-factdouble.spec.ts deleted file mode 100644 index c612f569d4..0000000000 --- a/test/unit/interpreter/function-factdouble.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function FACTDOUBLE', () => { - it('checks number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=FACTDOUBLE()', '=FACTDOUBLE(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['=FACTDOUBLE(0)'], - ['=FACTDOUBLE(1)'], - ['=FACTDOUBLE(10)'], - ['=FACTDOUBLE(288)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('A2'))).toBe(1) - expect(engine.getCellValue(adr('A3'))).toBe(3840) - expect(engine.getCellValue(adr('A4')) as number / 1.23775688540895e+293).toBeCloseTo(1, 6) - }) - - it('rounds argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=FACTDOUBLE(0.9)'], - ['=FACTDOUBLE(1.1)'], - ['=FACTDOUBLE(10.42)'], - ['=FACTDOUBLE(287.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('A2'))).toBe(1) - expect(engine.getCellValue(adr('A3'))).toBe(3840) - expect(engine.getCellValue(adr('A4')) as number / 5.81436347598024e+291).toBeCloseTo(1, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=FACTDOUBLE(-1)'], - ['=FACTDOUBLE(289)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('uses coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['=FACTDOUBLE("0")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - }) - - it('propagates error', () => { - const engine = HyperFormula.buildFromArray([ - ['=FACTDOUBLE(NA())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-false.spec.ts b/test/unit/interpreter/function-false.spec.ts deleted file mode 100644 index ad25775244..0000000000 --- a/test/unit/interpreter/function-false.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function FALSE', () => { - it('works', () => { - const engine = HyperFormula.buildFromArray([['=FALSE()']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(false) - }) - - it('is 0-arity', () => { - const engine = HyperFormula.buildFromArray([['=FALSE(1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) -}) diff --git a/test/unit/interpreter/function-filter.spec.ts b/test/unit/interpreter/function-filter.spec.ts deleted file mode 100644 index 8306f072b7..0000000000 --- a/test/unit/interpreter/function-filter.spec.ts +++ /dev/null @@ -1,71 +0,0 @@ -import {ErrorType, HyperFormula, Sheets} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function FILTER', () => { - it('should return an error for 2-dimensional arrays', () => { - const engine = HyperFormula.buildFromArray([['=FILTER(D2:E3, D2:E3)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongDimension)) - }) - - it('should return an error if arrays have different dimensions', () => { - const engine = HyperFormula.buildFromArray([['=FILTER(D2:D3, D2:D3, D2:D4)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.EqualLength)) - }) - - it('should return an error if param1 is not a range', () => { - const engine = HyperFormula.buildFromArray([['=FILTER(1, FALSE())']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.EmptyRange)) - }) - - it('should filter a horizontal range if one condition is passed', () => { - const engine = HyperFormula.buildFromArray([['=FILTER(A2:C2,A3:C3)'], [1, 2, 3], [true, false, true]]) - - expect(engine.getSheetValues(0)).toEqual([[1, 3], [1, 2, 3], [true, false, true]]) - }) - - it('should filter a horizontal range if two conditions are passed', () => { - const engine = HyperFormula.buildFromArray([['=FILTER(A2:C2,A3:C3,A4:C4)'], [1, 2, 3], [true, false, true], [true, true, false]]) - - expect(engine.getSheetValues(0)).toEqual([[1], [1, 2, 3], [true, false, true], [true, true, false]]) - }) - - it('should filter a vertical range', () => { - const engine = HyperFormula.buildFromArray([['=FILTER(B1:B3,C1:C3)', 1, true], [undefined, 2, false], [undefined, 3, true]]) - - expect(engine.getSheetValues(0)).toEqual([[1, 1, true], [3, 2, false], [null, 3, true]]) - }) - - it('should enable array arithmetic implicitly', () => { - const engine = HyperFormula.buildFromArray([['=FILTER(2*A2:C2,A3:C3)'], [1, 2, 3], [true, true, true]]) - - expect(engine.getSheetValues(0)).toEqual([[2, 4, 6], [1, 2, 3], [true, true, true]]) - }) - - it('should allow to construct a multidimensional array by calling filter for each column separately', () => { - const sheets: Sheets = { - Data: [ - ['a', 1, 42], - ['b', 2, 42], - ['a', 3, 42], - ['b', 4, 42], - ], - Result: [[ - '=FILTER(Data!A1:A4, Data!A1:A4="a")', - '=FILTER(Data!B1:B4, Data!A1:A4="a")', - '=FILTER(Data!C1:C4, Data!A1:A4="a")', - ]], - } - - const engine = HyperFormula.buildFromSheets(sheets) - const result = engine.getSheetValues(engine.getSheetId('Result') as number) - - expect(result).toEqual([ - ['a', 1, 42], - ['a', 3, 42], - ]) - }) -}) diff --git a/test/unit/interpreter/function-find.spec.ts b/test/unit/interpreter/function-find.spec.ts deleted file mode 100644 index 025a066add..0000000000 --- a/test/unit/interpreter/function-find.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function FIND', () => { - it('should return N/A when number of arguments is incorrect', () => { - const engine = HyperFormula.buildFromArray([ - ['=FIND()'], - ['=FIND("foo")'], - ['=FIND("foo", 1, 2, 3)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return VALUE when wrong type of third parameter', () => { - const engine = HyperFormula.buildFromArray([ - ['=FIND("foo", "bar", "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return VALUE if third parameter is not between 1 and text length', () => { - const engine = HyperFormula.buildFromArray([ - ['=FIND("foo", "bar", 0)'], - ['=FIND("foo", "bar", -1)'], - ['=FIND("foo", "bar", 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.IndexBounds)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.IndexBounds)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.IndexBounds)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=FIND("f", "foo")'], - ['=FIND("o", "foo")'], - ['=FIND("o", "foo", 3)'], - ['=FIND("g", "foo")'], - ['=FIND("?o", "?o")'], - ['=FIND("?o", "oo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.PatternNotFound)) - expect(engine.getCellValue(adr('A5'))).toEqual(1) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.PatternNotFound)) - }) - - it('should be case sensitive', () => { - const engine = HyperFormula.buildFromArray([ - ['=FIND("R", "bar")'], - ['=FIND("r", "bar")'], - ['=FIND("r", "baR")'], - ['=FIND("R", "baR")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.PatternNotFound)) - expect(engine.getCellValue(adr('A2'))).toEqual(3) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.PatternNotFound)) - expect(engine.getCellValue(adr('A4'))).toEqual(3) - }) - - it('should coerce other types to string', () => { - const engine = HyperFormula.buildFromArray([ - ['=FIND(1, 1, 1)'], - ['=FIND(0, 5+5)'], - ['=FIND("U", TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - }) -}) diff --git a/test/unit/interpreter/function-fisher.spec.ts b/test/unit/interpreter/function-fisher.spec.ts deleted file mode 100644 index 0db60ffd21..0000000000 --- a/test/unit/interpreter/function-fisher.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function FISHER', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=FISHER()'], - ['=FISHER(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=FISHER("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=FISHER(0)'], - ['=FISHER(0.5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.549306144334055, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=FISHER(-1)'], - ['=FISHER(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-fisherinv.spec.ts b/test/unit/interpreter/function-fisherinv.spec.ts deleted file mode 100644 index 7346e19420..0000000000 --- a/test/unit/interpreter/function-fisherinv.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function FISHERINV', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=FISHERINV()'], - ['=FISHERINV(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=FISHERINV("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=FISHERINV(0)'], - ['=FISHERINV(0.5)'], - ['=FISHERINV(-5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.46211715726001, 6) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(-0.999909204262595, 6) - }) -}) diff --git a/test/unit/interpreter/function-floor.math.spec.ts b/test/unit/interpreter/function-floor.math.spec.ts deleted file mode 100644 index 24615f64d7..0000000000 --- a/test/unit/interpreter/function-floor.math.spec.ts +++ /dev/null @@ -1,88 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function FLOOR.MATH', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=FLOOR.MATH()'], - ['=FLOOR.MATH(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=FLOOR.MATH("foo")'], - ['=FLOOR.MATH(1, "bar")'], - ['=FLOOR.MATH(1, 2, "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=FLOOR.MATH(4.43, 0.3)'], - ['=FLOOR.MATH(4.43, 0.6)'], - ['=FLOOR.MATH(4.43, 2)'], - ['=FLOOR.MATH(4.43)'], - ['=FLOOR.MATH(-4.43)'], - ['=FLOOR.MATH(-3.14, -1.8)'], - ['=FLOOR.MATH(-3.14, 0)'], - ['=FLOOR.MATH(3.14, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4.2) - expect(engine.getCellValue(adr('A2'))).toEqual(4.2) - expect(engine.getCellValue(adr('A3'))).toEqual(4) - expect(engine.getCellValue(adr('A4'))).toEqual(4) - expect(engine.getCellValue(adr('A5'))).toEqual(-5) - expect(engine.getCellValue(adr('A6'))).toEqual(-3.6) - expect(engine.getCellValue(adr('A7'))).toEqual(0) - expect(engine.getCellValue(adr('A8'))).toEqual(0) - }) - - it('should work with mode for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=FLOOR.MATH(-11, -2)'], - ['=FLOOR.MATH(-11, -2, 0)'], - ['=FLOOR.MATH(-11, -2, 1)'], - ['=FLOOR.MATH(-11, 0, 1)'], - ['=FLOOR.MATH(-11, 0, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(-12) - expect(engine.getCellValue(adr('A2'))).toEqual(-12) - expect(engine.getCellValue(adr('A3'))).toEqual(-10) - expect(engine.getCellValue(adr('A4'))).toEqual(0) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - }) - - it('negative values', () => { - const engine = HyperFormula.buildFromArray([ - ['=FLOOR.MATH(11, 2, 0)'], - ['=FLOOR.MATH(-11, 2, 0)'], - ['=FLOOR.MATH(11, -2, 0)'], - ['=FLOOR.MATH(-11, -2, 0)'], - ['=FLOOR.MATH(11, 2, 1)'], - ['=FLOOR.MATH(-11, 2, 1)'], - ['=FLOOR.MATH(11, -2, 1)'], - ['=FLOOR.MATH(-11, -2, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(10) - expect(engine.getCellValue(adr('A2'))).toEqual(-12) - expect(engine.getCellValue(adr('A3'))).toEqual(10) - expect(engine.getCellValue(adr('A4'))).toEqual(-12) - expect(engine.getCellValue(adr('A5'))).toEqual(10) - expect(engine.getCellValue(adr('A6'))).toEqual(-10) - expect(engine.getCellValue(adr('A7'))).toEqual(10) - expect(engine.getCellValue(adr('A8'))).toEqual(-10) - }) -}) diff --git a/test/unit/interpreter/function-floor.precise.spec.ts b/test/unit/interpreter/function-floor.precise.spec.ts deleted file mode 100644 index e83b106c66..0000000000 --- a/test/unit/interpreter/function-floor.precise.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function FLOOR.PRECISE', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=FLOOR.PRECISE()'], - ['=FLOOR.PRECISE(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=FLOOR.PRECISE(1, "bar")'], - ['=FLOOR.PRECISE("bar", 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=FLOOR.PRECISE(4.43, 0.3)'], - ['=FLOOR.PRECISE(4.43, 0.6)'], - ['=FLOOR.PRECISE(4.43, 2)'], - ['=FLOOR.PRECISE(-3.14, -1.8)'], - ['=FLOOR.PRECISE(-3.14, 0)'], - ['=FLOOR.PRECISE(3.14, 0)'], - ['=FLOOR.PRECISE(3.14)'], - ['=FLOOR.PRECISE(-3.14)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4.2) - expect(engine.getCellValue(adr('A2'))).toEqual(4.2) - expect(engine.getCellValue(adr('A3'))).toEqual(4) - expect(engine.getCellValue(adr('A4'))).toEqual(-3.6) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - expect(engine.getCellValue(adr('A6'))).toEqual(0) - expect(engine.getCellValue(adr('A7'))).toEqual(3) - expect(engine.getCellValue(adr('A8'))).toEqual(-4) - }) - - it('negative values', () => { - const engine = HyperFormula.buildFromArray([ - ['=FLOOR.PRECISE(11, 2)'], - ['=FLOOR.PRECISE(-11, 2)'], - ['=FLOOR.PRECISE(11, -2)'], - ['=FLOOR.PRECISE(-11, -2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(10) - expect(engine.getCellValue(adr('A2'))).toEqual(-12) - expect(engine.getCellValue(adr('A3'))).toEqual(10) - expect(engine.getCellValue(adr('A4'))).toEqual(-12) - }) -}) diff --git a/test/unit/interpreter/function-floor.spec.ts b/test/unit/interpreter/function-floor.spec.ts deleted file mode 100644 index 6001387254..0000000000 --- a/test/unit/interpreter/function-floor.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function FLOOR', () => { - /*Inconsistent with ODFF standard.*/ - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=FLOOR(1)'], - ['=FLOOR(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=FLOOR(1, "bar")'], - ['=FLOOR("bar", 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=FLOOR(4.43, 0.3)'], - ['=FLOOR(4.43, 0.6)'], - ['=FLOOR(4.43, 2)'], - ['=FLOOR(-3.14, -1.8)'], - ['=FLOOR(-3.14, 0)'], - ['=FLOOR(3.14, 0)'], - ['=FLOOR(0, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4.2) - expect(engine.getCellValue(adr('A2'))).toEqual(4.2) - expect(engine.getCellValue(adr('A3'))).toEqual(4) - expect(engine.getCellValue(adr('A4'))).toEqual(-1.8) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A7'))).toEqual(0) - }) - - /*Inconsistent with ODFF standard.*/ - it('negative values', () => { - const engine = HyperFormula.buildFromArray([ - ['=FLOOR(11, 2)'], - ['=FLOOR(-11, 2)'], - ['=FLOOR(11, -2)'], - ['=FLOOR(-11, -2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(10) - expect(engine.getCellValue(adr('A2'))).toEqual(-12) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.DistinctSigns)) - expect(engine.getCellValue(adr('A4'))).toEqual(-10) - }) -}) diff --git a/test/unit/interpreter/function-formulatext.spec.ts b/test/unit/interpreter/function-formulatext.spec.ts deleted file mode 100644 index a8e2016336..0000000000 --- a/test/unit/interpreter/function-formulatext.spec.ts +++ /dev/null @@ -1,97 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function FORMULATEXT', () => { - it('should return N/A when number of arguments is incorrect', () => { - const engine = HyperFormula.buildFromArray([ - ['=FORMULATEXT()'], - ['=FORMULATEXT(B2, B3)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return N/A for wrong types of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=FORMULATEXT(1)'], - ['=FORMULATEXT("foo")'], - ['=FORMULATEXT(SUM(1))'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.CellRefExpected)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.CellRefExpected)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.CellRefExpected)) - }) - - it('should propagate expression error', () => { - const engine = HyperFormula.buildFromArray([ - ['=FORMULATEXT(1/0)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should return text of a formula evaluating to error', () => { - const engine = HyperFormula.buildFromArray([ - ['=1/0', '=FORMULATEXT(A1)'] - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual('=1/0') - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(1, 2)', '=FORMULATEXT(A1)'] - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual('=SUM(1, 2)') - }) - - it('should return formula of a left corner cell', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(1, 2)', '=FORMULATEXT(A1:A2)'] - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual('=SUM(1, 2)') - }) - - it('should return REF when', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(1, 2)'] - ]) - engine.addSheet('Sheet2') - engine.setCellContents(adr('B1'), '=FORMULATEXT(Sheet1!A1:Sheet2!A2)') - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.CellRefExpected)) - }) - - it('should work for unparsed formula', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(1,', '=FORMULATEXT(A1)'] - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual('=SUM(1,') - }) - - it('should return itself', () => { - const engine = HyperFormula.buildFromArray([ - ['=FORMULATEXT(A1)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('=FORMULATEXT(A1)') - }) - - it('should be dependent on sheet structure changes', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(A2)', '=FORMULATEXT(A1)'], - [1] - ]) - - engine.addRows(0, [1, 1]) - - expect(engine.getCellFormula(adr('A1'))).toEqual('=SUM(A3)') - expect(engine.getCellValue(adr('B1'))).toEqual('=SUM(A3)') - }) -}) diff --git a/test/unit/interpreter/function-fv.spec.ts b/test/unit/interpreter/function-fv.spec.ts deleted file mode 100644 index 97b31f58bf..0000000000 --- a/test/unit/interpreter/function-fv.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function FV', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=FV(1, 1)', '=FV(1, 1, 1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=FV(2%, 24, 100)', '=FV(2%, 24, 100, 400)', '=FV(2%, 24, 100, 400, 1)'], - ['=FV(0, 24, 100)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-3042.18624737613) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-3685.56114716622) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(-3746.40487211374) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(-2400) - }) -}) diff --git a/test/unit/interpreter/function-fvschedule.spec.ts b/test/unit/interpreter/function-fvschedule.spec.ts deleted file mode 100644 index 548f8d3e79..0000000000 --- a/test/unit/interpreter/function-fvschedule.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function FVSCHEDULE', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=FVSCHEDULE(1)', '=FVSCHEDULE(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=FVSCHEDULE(1, 1)'], - ['=FVSCHEDULE(2, B2:D2)', 1, 1, null], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValue(adr('A2'))).toEqual(8) - }) - - it('should return proper error', () => { - const engine = HyperFormula.buildFromArray([ - ['=FVSCHEDULE(2, B1:C1)', '\'1', true], - ['=FVSCHEDULE(1, B2:C2)', 'abcd', '=NA()'], - ['=FVSCHEDULE(1, B3)', 'abcd'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberExpected)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberExpected)) - }) -}) diff --git a/test/unit/interpreter/function-gamma.dist.spec.ts b/test/unit/interpreter/function-gamma.dist.spec.ts deleted file mode 100644 index c411fd7046..0000000000 --- a/test/unit/interpreter/function-gamma.dist.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function GAMMA.DIST', () => { - - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMA.DIST(1, 2, 3)'], - ['=GAMMA.DIST(1, 2, 3, 4, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMA.DIST("foo", 2, 3, TRUE())'], - ['=GAMMA.DIST(1, "baz", 3, TRUE())'], - ['=GAMMA.DIST(1, 2, "baz", TRUE())'], - ['=GAMMA.DIST(1, 2, 3, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMA.DIST(1, 1, 2, TRUE())'], - ['=GAMMA.DIST(3, 2, 4, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.393469340287367, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.173358532703224, 6) - }) - - it('should work as pdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMA.DIST(1, 1, 2, FALSE())'], - ['=GAMMA.DIST(3, 2, 4, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.303265329856317, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0885687286389403, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMA.DIST(0, 1, 1, FALSE())'], - ['=GAMMA.DIST(-0.00001, 1, 1, FALSE())'], - ['=GAMMA.DIST(1, 0, 1, FALSE())'], - ['=GAMMA.DIST(1, 1, 0, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-gamma.inv.spec.ts b/test/unit/interpreter/function-gamma.inv.spec.ts deleted file mode 100644 index 735c7a977e..0000000000 --- a/test/unit/interpreter/function-gamma.inv.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function GAMMA.INV', () => { - - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMA.INV(1, 2)'], - ['=GAMMA.INV(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMA.INV("foo", 2, 3)'], - ['=GAMMA.INV(0.5, "baz", 3)'], - ['=GAMMA.INV(0.5, 2, "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMA.INV(0.5, 1, 1)'], - ['=GAMMA.INV(0.9, 2, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.693147180559945, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(15.5588806794697, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMA.INV(0, 1, 1)'], - ['=GAMMA.INV(-0.00001, 1, 1)'], - ['=GAMMA.INV(1, 1, 1)'], - ['=GAMMA.INV(0, 0, 1)'], - ['=GAMMA.INV(0, 1, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-gamma.spec.ts b/test/unit/interpreter/function-gamma.spec.ts deleted file mode 100644 index bd5e68c001..0000000000 --- a/test/unit/interpreter/function-gamma.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function GAMMA', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMA()'], - ['=GAMMA(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMA("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMA(1)'], - ['=GAMMA(0.5)'], - ['=GAMMA(10.5)'], - ['=GAMMA(-2.5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(1.77245385588014, 6) - expect(engine.getCellValue(adr('A3')) as number / 1133278.39212948).toBeCloseTo(1, 6) - //product #1 returns NUM for the following test - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(-0.94530871782981, 6) - }) - - it('should return nan', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMA(0)'], - ['=GAMMA(-1)'], - ['=GAMMA(180)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) -}) diff --git a/test/unit/interpreter/function-gammaln.spec.ts b/test/unit/interpreter/function-gammaln.spec.ts deleted file mode 100644 index ea23c1bd7c..0000000000 --- a/test/unit/interpreter/function-gammaln.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function GAMMALN', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMALN()'], - ['=GAMMALN(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMALN("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMALN(0.1)'], - ['=GAMMALN(1)'], - ['=GAMMALN(10)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(2.25271265173425, 6) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(12.801827480082, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAMMALN(0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-gauss.spec.ts b/test/unit/interpreter/function-gauss.spec.ts deleted file mode 100644 index db87d00733..0000000000 --- a/test/unit/interpreter/function-gauss.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function GAUSS', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAUSS()'], - ['=GAUSS(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAUSS("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=GAUSS(-10)'], - ['=GAUSS(0)'], - ['=GAUSS(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-0.5, 6) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.341344746068543, 6) - }) -}) diff --git a/test/unit/interpreter/function-gcd.spec.ts b/test/unit/interpreter/function-gcd.spec.ts deleted file mode 100644 index f8f32f34ae..0000000000 --- a/test/unit/interpreter/function-gcd.spec.ts +++ /dev/null @@ -1,117 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function GCD', () => { - it('checks required number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=GCD()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('computes correct answer for two args', () => { - const engine = HyperFormula.buildFromArray([ - ['=GCD(2*3*5, 3*5*7)', '=GCD(0, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(3 * 5) - expect(engine.getCellValue(adr('B1'))).toBe(1) - }) - - it('computes correct answer for more than two args', () => { - const engine = HyperFormula.buildFromArray([ - ['=GCD(2*3*5, 3*5*7, 2*5*7)', '=GCD(100, 101, 102, 103, 104)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(5) - expect(engine.getCellValue(adr('B1'))).toBe(1) - }) - - it('works with zeroes', () => { - const engine = HyperFormula.buildFromArray([ - ['=GCD(2*3*5, 3*5*7, 2*5*7, 0, 0, 0)', '=GCD(0, 0, 100, 101, 102, 103, 104, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(5) - expect(engine.getCellValue(adr('B1'))).toBe(1) - }) - - it('accepts single arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=GCD(1)', '=GCD(0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('B1'))).toBe(0) - }) - - it('coerces to number', () => { - const engine = HyperFormula.buildFromArray([ - ['=GCD("2",4)'], - ['=GCD(B2:C2)', '\'2', 4], - ['=GCD(TRUE(), 4)'], - ['=GCD(B4:C4)', true, 4], - ['=GCD(,4)'], - ['=GCD(B6:C6)', null, 4], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(2) - expect(engine.getCellValue(adr('A2'))).toBe(2) - expect(engine.getCellValue(adr('A3'))).toBe(1) - expect(engine.getCellValue(adr('A4'))).toBe(1) - expect(engine.getCellValue(adr('A5'))).toBe(4) - expect(engine.getCellValue(adr('A6'))).toBe(4) - }) - - it('ignores non-coercible values', () => { - const engine = HyperFormula.buildFromArray([ - ['=GCD(B1:C1)', 'abcd', 4], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(4) - }) - - it('throws error for non-coercible values', () => { - const engine = HyperFormula.buildFromArray([ - ['=GCD("abcd", 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('handles overflow', () => { - const engine = HyperFormula.buildFromArray([ - ['=GCD(1000000000000000000.0)'], - ]) - - //inconsistency with product #1 - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=GCD(-1, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('truncates numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=GCD(B1:C1)', 5.5, 10], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(5) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=GCD(NA(),4)'], - ['=GCD(B2:C2)', '=NA()', 4], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-geomean.spec.ts b/test/unit/interpreter/function-geomean.spec.ts deleted file mode 100644 index c0fb35e9c6..0000000000 --- a/test/unit/interpreter/function-geomean.spec.ts +++ /dev/null @@ -1,94 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function GEOMEAN', () => { - it('single number', () => { - const engine = HyperFormula.buildFromArray([ - ['=GEOMEAN(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('two numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=GEOMEAN(1, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('more numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=GEOMEAN(8, 1, 2, 4, 16)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('validates input', () => { - const engine = HyperFormula.buildFromArray([ - ['=GEOMEAN(8, 0, 2, 4, 16)'], - ['=GEOMEAN(8, -1, -2, 4, 16)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('works with ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '9', '3'], - ['=GEOMEAN(A1:C1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(3) - }) - - it('propagates error from regular argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=3/0', '=GEOMEAN(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('propagates first error from range argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=3/0', '=FOO(', '=GEOMEAN(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('returns error for empty ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['=GEOMEAN(A2:A3)'], - [null], - [null], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.OneValue)) - }) - - /** - * product #1 does not coerce the input - */ - it('does coercions of nonnumeric explicit arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=GEOMEAN(TRUE(),"4")'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('ignores nonnumeric values in ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['=GEOMEAN(A2:D2)'], - [1, 1, false, null, '\'0'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) -}) diff --git a/test/unit/interpreter/function-harmean.spec.ts b/test/unit/interpreter/function-harmean.spec.ts deleted file mode 100644 index d8da700cfd..0000000000 --- a/test/unit/interpreter/function-harmean.spec.ts +++ /dev/null @@ -1,94 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HARMEAN', () => { - it('single number', () => { - const engine = HyperFormula.buildFromArray([ - ['=HARMEAN(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('two numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=HARMEAN(1, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1.6) - }) - - it('more numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=HARMEAN(8, 1, 2, 4, 16)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(2.58064516129032, 6) - }) - - it('validates input', () => { - const engine = HyperFormula.buildFromArray([ - ['=HARMEAN(8, 0, 2, 4, 16)'], - ['=HARMEAN(8, -1, -2, 4, 16)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('works with ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '9', '3'], - ['=HARMEAN(A1:C1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(2.07692307692308, 6) - }) - - it('propagates error from regular argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=3/0', '=HARMEAN(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('propagates first error from range argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=3/0', '=FOO(', '=HARMEAN(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('returns error for empty ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['=HARMEAN(A2:A3)'], - [null], - [null], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.OneValue)) - }) - - /** - * product #1 does not coerce the input - */ - it('does coercions of nonnumeric explicit arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HARMEAN(TRUE(),"4")'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1.6) - }) - - it('ignores nonnumeric values in ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['=HARMEAN(A2:D2)'], - [1, 1, false, null, '\'0'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) -}) diff --git a/test/unit/interpreter/function-hex2bin.spec.ts b/test/unit/interpreter/function-hex2bin.spec.ts deleted file mode 100644 index aef58abf06..0000000000 --- a/test/unit/interpreter/function-hex2bin.spec.ts +++ /dev/null @@ -1,121 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function HEX2BIN', () => { - it('should return error when wrong number of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2BIN("foo", 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for non-hex arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2BIN("foo")'], - ['=HEX2BIN("G418")'], - ['=HEX2BIN(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2BIN("1")'], - ['=HEX2BIN("F")'], - ['=HEX2BIN("2A")'], - ['=HEX2BIN("1FF")'], - ['=HEX2BIN("FFFFFFFFF6")'], - ['=HEX2BIN("FFFFFFFF9C")'], - ['=HEX2BIN("FFFFFFFE00")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - expect(engine.getCellValue(adr('A2'))).toEqual('1111') - expect(engine.getCellValue(adr('A3'))).toEqual('101010') - expect(engine.getCellValue(adr('A4'))).toEqual('111111111') - expect(engine.getCellValue(adr('A5'))).toEqual('1111110110') - expect(engine.getCellValue(adr('A6'))).toEqual('1110011100') - expect(engine.getCellValue(adr('A7'))).toEqual('1000000000') - }) - - it('should work for numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2BIN(156)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('101010110') - }) - - it('should work for reference', () => { - const engine = HyperFormula.buildFromArray([ - ['="12A"'], - ['=HEX2BIN(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual('100101010') - }) - - it('should return string value', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2BIN(11)'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.STRING) - }) - - it('result cannot be longer than 10 digits', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2BIN("200")'], - ['=HEX2BIN("FFFFFFFDFF")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLarge)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseSmall)) - }) - - it('should respect second argument and fill with zeros for positive arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2BIN(12, 8)'], - ['=HEX2BIN(3, "4")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('00010010') - expect(engine.getCellValue(adr('A2'))).toEqual('0011') - }) - - it('second argument should not affect negative results', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2BIN("FFFFFFFF9C", 1)'], - ['=HEX2BIN("FFFFFFFFF6", 10)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1110011100') - expect(engine.getCellValue(adr('A2'))).toEqual('1111110110') - }) - - it('should fail if the result is longer than the desired length', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2BIN("100", 2)'], - ['=HEX2BIN("FF", "3")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLong)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLong)) - }) - - it('should allow for numbers from 1 to 10 as second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2BIN(2, 0)'], - ['=HEX2BIN(2, 12)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLong)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-hex2dec.spec.ts b/test/unit/interpreter/function-hex2dec.spec.ts deleted file mode 100644 index a7f5b38660..0000000000 --- a/test/unit/interpreter/function-hex2dec.spec.ts +++ /dev/null @@ -1,89 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function HEX2DEC', () => { - it('should return error when wrong number of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2DEC("foo", 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for non-hex arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2DEC("foo")'], - ['=HEX2DEC("23G")'], - ['=HEX2DEC(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2DEC("1")'], - ['=HEX2DEC("10")'], - ['=HEX2DEC("AD")'], - ['=HEX2DEC("ABBA")'], - ['=HEX2DEC("BA0AB")'], - ['=HEX2DEC("B09D65")'], - ['=HEX2DEC("F1808E4")'], - ['=HEX2DEC("B07D007")'], - ['=HEX2DEC("7FFFFFFFFF")'], - ['=HEX2DEC("F352DEB731")'], - ['=HEX2DEC("FFFFFFFFFF")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(16) - expect(engine.getCellValue(adr('A3'))).toEqual(173) - expect(engine.getCellValue(adr('A4'))).toEqual(43962) - expect(engine.getCellValue(adr('A5'))).toEqual(762027) - expect(engine.getCellValue(adr('A6'))).toEqual(11574629) - expect(engine.getCellValue(adr('A7'))).toEqual(253233380) - expect(engine.getCellValue(adr('A8'))).toEqual(185061383) - expect(engine.getCellValue(adr('A9'))).toBeCloseTo(549755813887, -1) - expect(engine.getCellValue(adr('A10'))).toEqual(-54444247247) - expect(engine.getCellValue(adr('A11'))).toEqual(-1) - }) - - it('should work for numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2DEC(456)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1110) - }) - - it('should work for reference', () => { - const engine = HyperFormula.buildFromArray([ - ['="1A3"'], - ['=HEX2DEC(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(419) - }) - - it('should return a number', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2DEC("11")'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.NUMBER) - }) - - it('should work only for 10 digits', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2DEC("1010B040205")'], - ['=HEX2DEC("7777EE70D2")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex)) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(513113223378, -1) - }) -}) diff --git a/test/unit/interpreter/function-hex2oct.spec.ts b/test/unit/interpreter/function-hex2oct.spec.ts deleted file mode 100644 index bdc996de27..0000000000 --- a/test/unit/interpreter/function-hex2oct.spec.ts +++ /dev/null @@ -1,137 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function HEX2OCT', () => { - it('should return error when wrong number of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2OCT("foo", 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for non-hex arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2OCT("foo")'], - ['=HEX2OCT("G418")'], - ['=HEX2OCT(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2OCT("1")'], - ['=HEX2OCT("F")'], - ['=HEX2OCT("2A")'], - ['=HEX2OCT("26235")'], - ['=HEX2OCT("1BB95B19")'], - ['=HEX2OCT("CE6D570")'], - ['=HEX2OCT("FFFB4B62A9")'], - ['=HEX2OCT("FFFF439EB2")'], - ['=HEX2OCT("FFFFFFFFFF")'], - ['=HEX2OCT("1FFFFFFF")'], - ['=HEX2OCT("FFE0000000")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - expect(engine.getCellValue(adr('A2'))).toEqual('17') - expect(engine.getCellValue(adr('A3'))).toEqual('52') - expect(engine.getCellValue(adr('A4'))).toEqual('461065') - expect(engine.getCellValue(adr('A5'))).toEqual('3356255431') - expect(engine.getCellValue(adr('A6'))).toEqual('1471552560') - expect(engine.getCellValue(adr('A7'))).toEqual('7322661251') - expect(engine.getCellValue(adr('A8'))).toEqual('7720717262') - expect(engine.getCellValue(adr('A9'))).toEqual('7777777777') - expect(engine.getCellValue(adr('A10'))).toEqual('3777777777') - expect(engine.getCellValue(adr('A11'))).toEqual('4000000000') - }) - - it('should work for numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2OCT(456)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('2126') - }) - - it('should work for reference', () => { - const engine = HyperFormula.buildFromArray([ - ['="123"'], - ['=HEX2OCT(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual('443') - }) - - it('should return string value', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2OCT(11)'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.STRING) - }) - - it('result cannot be longer than 10 digits', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2OCT("FFDFFFFFFF")'], - ['=HEX2OCT("3FFFFFFF")'], ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLarge)) - }) - - it('input cannot have more than 10 digits', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2OCT("10000000000")'], - - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotHex)) - }) - - it('should respect second argument and fill with zeros for positive arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2OCT(12, 8)'], - ['=HEX2OCT(3, "4")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('00000022') - expect(engine.getCellValue(adr('A2'))).toEqual('0003') - }) - - it('should fail if the result is longer than the desired length', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2OCT(32123, 2)'], - ['=HEX2OCT(433141, "3")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLong)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLong)) - }) - - it('second argument should not affect negative results', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2OCT("FFFB4B62A9", 1)'], - ['=HEX2OCT("FFFF439EB2", 10)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('7322661251') - expect(engine.getCellValue(adr('A2'))).toEqual('7720717262') - }) - - it('should allow for numbers from 1 to 10 as second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=HEX2OCT(2, 0)'], - ['=HEX2OCT(2, 12)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLong)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-hfadd.spec.ts b/test/unit/interpreter/function-hfadd.spec.ts deleted file mode 100644 index 1db97d7427..0000000000 --- a/test/unit/interpreter/function-hfadd.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HF.ADD', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.ADD(1)', '=HF.ADD(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.ADD(2, 3)'], - ['=HF.ADD(1.0000000000001, -1)'], - ['=HF.ADD(1,)'], - ['=HF.ADD(,)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(5) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(0) - }) - - it('should coerce to correct types', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.ADD(TRUE(),B1)'], - ['=HF.ADD("1",)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - }) - - it('should throw correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.ADD("abcd",)'], - ['=HF.ADD(NA(),)'], - ['=HF.ADD(B3:C3,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('passes subtypes', () => { - const engine = HyperFormula.buildFromArray([['=HF.ADD(B1,C1)', '1$', 1]]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - }) -}) diff --git a/test/unit/interpreter/function-hfconcat.spec.ts b/test/unit/interpreter/function-hfconcat.spec.ts deleted file mode 100644 index 59d1f1f87f..0000000000 --- a/test/unit/interpreter/function-hfconcat.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HF.CONCAT', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.CONCAT(1)', '=HF.CONCAT(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.CONCAT("hokuspokus","czarymary")'], - ['=HF.CONCAT(,"a")'], - ['=HF.CONCAT(,)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('hokuspokusczarymary') - expect(engine.getCellValue(adr('A2'))).toEqual('a') - expect(engine.getCellValue(adr('A3'))).toEqual('') - }) - - it('should coerce to correct types', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.CONCAT(TRUE(),B1)'], - ['=HF.CONCAT(1,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('TRUE') - expect(engine.getCellValue(adr('A2'))).toEqual('1') - }) - - it('should throw correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.CONCAT(NA(),)'], - ['=HF.CONCAT(B2:C2,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-hfdivide.spec.ts b/test/unit/interpreter/function-hfdivide.spec.ts deleted file mode 100644 index b21e8787e7..0000000000 --- a/test/unit/interpreter/function-hfdivide.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HF.DIVIDE', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.DIVIDE(1)', '=HF.DIVIDE(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.DIVIDE(6,4)'], - ['=HF.DIVIDE(,1)'], - ['=HF.DIVIDE(1,)'], - ['=HF.DIVIDE(,)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1.5) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should coerce to correct types', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.DIVIDE(TRUE(),1)'], - ['=HF.DIVIDE(B2,1)'], - ['=HF.DIVIDE("1",1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - }) - - it('should throw correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.DIVIDE("abcd",)'], - ['=HF.DIVIDE(NA(),)'], - ['=HF.DIVIDE(B3:C3,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('passes subtypes', () => { - const engine = HyperFormula.buildFromArray([['=HF.DIVIDE(B1,C1)', '1$', 1]]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - }) -}) diff --git a/test/unit/interpreter/function-hfeq.spec.ts b/test/unit/interpreter/function-hfeq.spec.ts deleted file mode 100644 index 8b66a4bc3c..0000000000 --- a/test/unit/interpreter/function-hfeq.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HF.EQ', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.EQ(1)', '=HF.EQ(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.EQ(1, 0)'], - ['=HF.EQ(1, 1)'], - ['=HF.EQ("1", "0")'], - ['=HF.EQ("1", "1")'], - ['=HF.EQ(TRUE(), FALSE())'], - ['=HF.EQ(TRUE(), TRUE())'], - ['=HF.EQ(,)'], - ['=HF.EQ(1,)'], - ['=HF.EQ("1",)'], - ['=HF.EQ(TRUE(),)'], - ['=HF.EQ("1", 1)'], - ['=HF.EQ(TRUE(), 1)'], - ['=HF.EQ(TRUE(), "1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(false) - expect(engine.getCellValue(adr('A2'))).toEqual(true) - expect(engine.getCellValue(adr('A3'))).toEqual(false) - expect(engine.getCellValue(adr('A4'))).toEqual(true) - expect(engine.getCellValue(adr('A5'))).toEqual(false) - expect(engine.getCellValue(adr('A6'))).toEqual(true) - expect(engine.getCellValue(adr('A7'))).toEqual(true) - expect(engine.getCellValue(adr('A8'))).toEqual(false) - expect(engine.getCellValue(adr('A9'))).toEqual(false) - expect(engine.getCellValue(adr('A10'))).toEqual(false) - expect(engine.getCellValue(adr('A11'))).toEqual(false) - expect(engine.getCellValue(adr('A12'))).toEqual(false) - expect(engine.getCellValue(adr('A13'))).toEqual(false) - }) - - it('should throw correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.EQ(NA(),)'], - ['=HF.EQ(B2:C2,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-hfgt.spec.ts b/test/unit/interpreter/function-hfgt.spec.ts deleted file mode 100644 index 5878215571..0000000000 --- a/test/unit/interpreter/function-hfgt.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HF.GT', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.GT(1)', '=HF.GT(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.GT(1, 0)'], - ['=HF.GT(1, 1)'], - ['=HF.GT("1", "0")'], - ['=HF.GT("1", "1")'], - ['=HF.GT(TRUE(), FALSE())'], - ['=HF.GT(TRUE(), TRUE())'], - ['=HF.GT(,)'], - ['=HF.GT(1,)'], - ['=HF.GT("1",)'], - ['=HF.GT(TRUE(),)'], - ['=HF.GT("1", 1)'], - ['=HF.GT(TRUE(), 1)'], - ['=HF.GT(TRUE(), "1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(true) - expect(engine.getCellValue(adr('A2'))).toEqual(false) - expect(engine.getCellValue(adr('A3'))).toEqual(true) - expect(engine.getCellValue(adr('A4'))).toEqual(false) - expect(engine.getCellValue(adr('A5'))).toEqual(true) - expect(engine.getCellValue(adr('A6'))).toEqual(false) - expect(engine.getCellValue(adr('A7'))).toEqual(false) - expect(engine.getCellValue(adr('A8'))).toEqual(true) - expect(engine.getCellValue(adr('A9'))).toEqual(true) - expect(engine.getCellValue(adr('A10'))).toEqual(true) - expect(engine.getCellValue(adr('A11'))).toEqual(true) - expect(engine.getCellValue(adr('A12'))).toEqual(true) - expect(engine.getCellValue(adr('A13'))).toEqual(true) - }) - - it('should throw correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.GT(NA(),)'], - ['=HF.GT(B2:C2,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-hfgte.spec.ts b/test/unit/interpreter/function-hfgte.spec.ts deleted file mode 100644 index 85ddf09b42..0000000000 --- a/test/unit/interpreter/function-hfgte.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HF.GTE', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.GTE(1)', '=HF.GTE(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.GTE(1, 0)'], - ['=HF.GTE(1, 1)'], - ['=HF.GTE("1", "0")'], - ['=HF.GTE("1", "1")'], - ['=HF.GTE(TRUE(), FALSE())'], - ['=HF.GTE(TRUE(), TRUE())'], - ['=HF.GTE(,)'], - ['=HF.GTE(1,)'], - ['=HF.GTE("1",)'], - ['=HF.GTE(TRUE(),)'], - ['=HF.GTE("1", 1)'], - ['=HF.GTE(TRUE(), 1)'], - ['=HF.GTE(TRUE(), "1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(true) - expect(engine.getCellValue(adr('A2'))).toEqual(true) - expect(engine.getCellValue(adr('A3'))).toEqual(true) - expect(engine.getCellValue(adr('A4'))).toEqual(true) - expect(engine.getCellValue(adr('A5'))).toEqual(true) - expect(engine.getCellValue(adr('A6'))).toEqual(true) - expect(engine.getCellValue(adr('A7'))).toEqual(true) - expect(engine.getCellValue(adr('A8'))).toEqual(true) - expect(engine.getCellValue(adr('A9'))).toEqual(true) - expect(engine.getCellValue(adr('A10'))).toEqual(true) - expect(engine.getCellValue(adr('A11'))).toEqual(true) - expect(engine.getCellValue(adr('A12'))).toEqual(true) - expect(engine.getCellValue(adr('A13'))).toEqual(true) - }) - - it('should throw correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.GTE(NA(),)'], - ['=HF.GTE(B2:C2,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-hflt.spec.ts b/test/unit/interpreter/function-hflt.spec.ts deleted file mode 100644 index aa44d5014d..0000000000 --- a/test/unit/interpreter/function-hflt.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HF.LT', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.LT(1)', '=HF.LT(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.LT(1, 0)'], - ['=HF.LT(1, 1)'], - ['=HF.LT("1", "0")'], - ['=HF.LT("1", "1")'], - ['=HF.LT(TRUE(), FALSE())'], - ['=HF.LT(TRUE(), TRUE())'], - ['=HF.LT(,)'], - ['=HF.LT(1,)'], - ['=HF.LT("1",)'], - ['=HF.LT(TRUE(),)'], - ['=HF.LT("1", 1)'], - ['=HF.LT(TRUE(), 1)'], - ['=HF.LT(TRUE(), "1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(false) - expect(engine.getCellValue(adr('A2'))).toEqual(false) - expect(engine.getCellValue(adr('A3'))).toEqual(false) - expect(engine.getCellValue(adr('A4'))).toEqual(false) - expect(engine.getCellValue(adr('A5'))).toEqual(false) - expect(engine.getCellValue(adr('A6'))).toEqual(false) - expect(engine.getCellValue(adr('A7'))).toEqual(false) - expect(engine.getCellValue(adr('A8'))).toEqual(false) - expect(engine.getCellValue(adr('A9'))).toEqual(false) - expect(engine.getCellValue(adr('A10'))).toEqual(false) - expect(engine.getCellValue(adr('A11'))).toEqual(false) - expect(engine.getCellValue(adr('A12'))).toEqual(false) - expect(engine.getCellValue(adr('A13'))).toEqual(false) - }) - - it('should throw correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.LT(NA(),)'], - ['=HF.LT(B2:C2,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-hflte.spec.ts b/test/unit/interpreter/function-hflte.spec.ts deleted file mode 100644 index 2bef54ebcb..0000000000 --- a/test/unit/interpreter/function-hflte.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HF.LTE', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.LTE(1)', '=HF.LTE(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.LTE(1, 0)'], - ['=HF.LTE(1, 1)'], - ['=HF.LTE("1", "0")'], - ['=HF.LTE("1", "1")'], - ['=HF.LTE(TRUE(), FALSE())'], - ['=HF.LTE(TRUE(), TRUE())'], - ['=HF.LTE(,)'], - ['=HF.LTE(1,)'], - ['=HF.LTE("1",)'], - ['=HF.LTE(TRUE(),)'], - ['=HF.LTE("1", 1)'], - ['=HF.LTE(TRUE(), 1)'], - ['=HF.LTE(TRUE(), "1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(false) - expect(engine.getCellValue(adr('A2'))).toEqual(true) - expect(engine.getCellValue(adr('A3'))).toEqual(false) - expect(engine.getCellValue(adr('A4'))).toEqual(true) - expect(engine.getCellValue(adr('A5'))).toEqual(false) - expect(engine.getCellValue(adr('A6'))).toEqual(true) - expect(engine.getCellValue(adr('A7'))).toEqual(true) - expect(engine.getCellValue(adr('A8'))).toEqual(false) - expect(engine.getCellValue(adr('A9'))).toEqual(false) - expect(engine.getCellValue(adr('A10'))).toEqual(false) - expect(engine.getCellValue(adr('A11'))).toEqual(false) - expect(engine.getCellValue(adr('A12'))).toEqual(false) - expect(engine.getCellValue(adr('A13'))).toEqual(false) - }) - - it('should throw correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.LTE(NA(),)'], - ['=HF.LTE(B2:C2,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-hfminus.spec.ts b/test/unit/interpreter/function-hfminus.spec.ts deleted file mode 100644 index f563942a15..0000000000 --- a/test/unit/interpreter/function-hfminus.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HF.MINUS', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.MINUS(1)', '=HF.MINUS(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.MINUS(2,3)'], - ['=HF.MINUS(1.0000000000001,1)'], - ['=HF.MINUS(1,)'], - ['=HF.MINUS(,)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(-1) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(0) - }) - - it('should coerce to correct types', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.MINUS(TRUE(),B1)'], - ['=HF.MINUS("1",)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - }) - - it('should throw correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.MINUS("abcd",)'], - ['=HF.MINUS(NA(),)'], - ['=HF.MINUS(B3:C3,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('passes subtypes', () => { - const engine = HyperFormula.buildFromArray([['=HF.MINUS(B1,C1)', '1$', 1]]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - }) -}) diff --git a/test/unit/interpreter/function-hfmultiply.spec.ts b/test/unit/interpreter/function-hfmultiply.spec.ts deleted file mode 100644 index 8e91e583fe..0000000000 --- a/test/unit/interpreter/function-hfmultiply.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HF.MULTIPLY', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.MULTIPLY(1)', '=HF.MULTIPLY(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.MULTIPLY(2,3)'], - ['=HF.MULTIPLY(1,)'], - ['=HF.MULTIPLY(,)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(6) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(0) - }) - - it('should coerce to correct types', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.MULTIPLY(TRUE(),1)'], - ['=HF.MULTIPLY(B2,1)'], - ['=HF.MULTIPLY("1",1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - }) - - it('should throw correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.MULTIPLY("abcd",)'], - ['=HF.MULTIPLY(NA(),)'], - ['=HF.MULTIPLY(B3:C3,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('passes subtypes', () => { - const engine = HyperFormula.buildFromArray([['=HF.MULTIPLY(B1,C1)', '1$', 1]]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - }) -}) diff --git a/test/unit/interpreter/function-hfne.spec.ts b/test/unit/interpreter/function-hfne.spec.ts deleted file mode 100644 index 46f61cf37b..0000000000 --- a/test/unit/interpreter/function-hfne.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HF.NE', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.NE(1)', '=HF.NE(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.NE(1, 0)'], - ['=HF.NE(1, 1)'], - ['=HF.NE("1", "0")'], - ['=HF.NE("1", "1")'], - ['=HF.NE(TRUE(), FALSE())'], - ['=HF.NE(TRUE(), TRUE())'], - ['=HF.NE(,)'], - ['=HF.NE(1,)'], - ['=HF.NE("1",)'], - ['=HF.NE(TRUE(),)'], - ['=HF.NE("1", 1)'], - ['=HF.NE(TRUE(), 1)'], - ['=HF.NE(TRUE(), "1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(true) - expect(engine.getCellValue(adr('A2'))).toEqual(false) - expect(engine.getCellValue(adr('A3'))).toEqual(true) - expect(engine.getCellValue(adr('A4'))).toEqual(false) - expect(engine.getCellValue(adr('A5'))).toEqual(true) - expect(engine.getCellValue(adr('A6'))).toEqual(false) - expect(engine.getCellValue(adr('A7'))).toEqual(false) - expect(engine.getCellValue(adr('A8'))).toEqual(true) - expect(engine.getCellValue(adr('A9'))).toEqual(true) - expect(engine.getCellValue(adr('A10'))).toEqual(true) - expect(engine.getCellValue(adr('A11'))).toEqual(true) - expect(engine.getCellValue(adr('A12'))).toEqual(true) - expect(engine.getCellValue(adr('A13'))).toEqual(true) - }) - - it('should throw correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.NE(NA(),)'], - ['=HF.NE(B2:C2,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-hfpow.spec.ts b/test/unit/interpreter/function-hfpow.spec.ts deleted file mode 100644 index dadcc127dc..0000000000 --- a/test/unit/interpreter/function-hfpow.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HF.POW', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.POW(1)', '=HF.POW(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.POW(2, 3)'], - ['=HF.POW(,1)'], - ['=HF.POW(,)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(8) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - }) - - it('should coerce to correct types', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.POW(TRUE(),B1)'], - ['=HF.POW("1",)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - }) - - it('should throw correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.POW("abcd",)'], - ['=HF.POW(NA(),)'], - ['=HF.POW(B3:C3,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-hfuminus.spec.ts b/test/unit/interpreter/function-hfuminus.spec.ts deleted file mode 100644 index 8ee67e12f1..0000000000 --- a/test/unit/interpreter/function-hfuminus.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HF.UMINUS', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.UMINUS()', '=HF.UMINUS(1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.UMINUS(2)'], - ['=HF.UMINUS(-3)'], - ['=HF.UMINUS(0)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(-2) - expect(engine.getCellValue(adr('A2'))).toEqual(3) - expect(engine.getCellValue(adr('A3'))).toEqual(0) - }) - - it('should coerce to correct types', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.UMINUS(TRUE())'], - ['=HF.UMINUS(B2)'], - ['=HF.UMINUS("1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(-1) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(-1) - }) - - it('should throw correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.UMINUS("abcd")'], - ['=HF.UMINUS(NA())'], - ['=HF.UMINUS(B3:C3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-hfunary_percent.spec.ts b/test/unit/interpreter/function-hfunary_percent.spec.ts deleted file mode 100644 index 95a2860c10..0000000000 --- a/test/unit/interpreter/function-hfunary_percent.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HF.UNARY_PERCENT', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.UNARY_PERCENT()', '=HF.UNARY_PERCENT(1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.UNARY_PERCENT(2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.02) - }) - - it('should coerce to correct types', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.UNARY_PERCENT(TRUE())'], - ['=HF.UNARY_PERCENT(B2)'], - ['=HF.UNARY_PERCENT("1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.01) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(0.01) - }) - - it('should throw correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.UNARY_PERCENT("abcd")'], - ['=HF.UNARY_PERCENT(NA())'], - ['=HF.UNARY_PERCENT(B3:C3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-hfuplus.spec.ts b/test/unit/interpreter/function-hfuplus.spec.ts deleted file mode 100644 index 687998f31d..0000000000 --- a/test/unit/interpreter/function-hfuplus.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HF.UPLUS', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.UPLUS()', '=HF.UPLUS(1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.UPLUS(2)'], - ['=HF.UPLUS(-3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(engine.getCellValue(adr('A2'))).toEqual(-3) - }) - - it('should coerce to correct types', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.UPLUS(TRUE())'], - ['=HF.UPLUS(B2)'], - ['=HF.UPLUS("1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - }) - - it('should throw correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=HF.UPLUS("abcd")'], - ['=HF.UPLUS(NA())'], - ['=HF.UPLUS(B3:C3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-hlookup.spec.ts b/test/unit/interpreter/function-hlookup.spec.ts deleted file mode 100644 index 99e1ab2d08..0000000000 --- a/test/unit/interpreter/function-hlookup.spec.ts +++ /dev/null @@ -1,358 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HLOOKUP', () => { - describe('HLOOKUP - args validation', () => { - it('not enough parameters', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP(1, A2:B3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('too many parameters', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP(1, A2:B3, 2, TRUE(), "foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('wrong type of first argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP(D1:E1, A2:B3, 2, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('wrong type of second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP(1, "foo", 2, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('wrong type of third argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP(1, A2:B3, "foo", TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong type of fourth argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP(1, A2:B3, 2, "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('should return error when index argument greater that range height', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP(1, A2:B3, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.IndexLarge)) - }) - - it('should return error when index is less than one', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP(1, C3:D5, 0)'], - ['=HLOOKUP(1, C2:D3, -1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - }) - - it('should return #VALUE error when the found value is a range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '4', '5'], - ['a', '=D2:E2', 'c', 'd', 'e'], - ['=HLOOKUP(2, A1:E2, 2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('should propagate errors properly', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP(1/0, B1:B1, 1)'], - ['=HLOOKUP(1, B1:B1, 1/0)'], - ['=HLOOKUP(1, A10:A11, 1, NA())'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA)) - }) - }) - - describe('HLOOKUP', () => { - it('should find value in sorted range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '4', '5'], - ['a', 'b', 'c', 'd', 'e'], - ['=HLOOKUP(2, A1:E2, 2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual('b') - }) - - it('should find value in sorted range using linearSearch', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '4', '5'], - ['a', 'b', 'c', 'd', 'e'], - ['=HLOOKUP(2, A1:E2, 2, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual('b') - }) - - it('should return the first matching value if RangeLookup = FALSE', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '2', '2', '5'], - ['a', 'b', 'c', 'd', 'e'], - ['=HLOOKUP(2, A1:E2, 2, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual('b') - }) - - it('should return the last matching value if RangeLookup = TRUE', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '2', '2', '5'], - ['a', 'b', 'c', 'd', 'e'], - ['=HLOOKUP(2, A1:E2, 2, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual('d') - }) - - it('works with wildcards', () => { - const engine = HyperFormula.buildFromArray([ - ['abd', 1, 'aaaa', 'ddaa', 'abcd'], - ['a', 'b', 'c', 'd', 'e'], - ['=HLOOKUP("*c*", A1:E2, 2, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual('e') - }) - - it('returns error when there is no matching value for the wildcard pattern', () => { - const engine = HyperFormula.buildFromArray([ - ['abd', 1, 'aaaa', 'ddaa', 'abbd'], - ['a', 'b', 'c', 'd', 'e'], - ['=HLOOKUP("*c*", A1:E2, 2, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('on sorted data ignores wildcards', () => { - const engine = HyperFormula.buildFromArray([ - ['abd', 1, '*c*', 'ddaa', 'abcd'], - ['a', 'b', 'c', 'd', 'e'], - ['=HLOOKUP("*c*", A1:E2, 2, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual('c') - }) - - it('should find value in unsorted range using linearSearch', () => { - const engine = HyperFormula.buildFromArray([ - ['5', '4', '3', '2', '1'], - ['a', 'b', 'c', 'd', 'e'], - ['=HLOOKUP(2, A1:E2, 2, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual('d') - }) - - it('should find value in sorted range with different types', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '=TRUE()', 'foo'], - ['a', 'b', 'c', 'd', 'e'], - ['=HLOOKUP(TRUE(), A1:E2, 2, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual('d') - }) - - it('should find value in unsorted range with different types', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRUE()', '4', 'foo', '2', 'bar'], - ['a', 'b', 'c', 'd', 'e'], - ['=HLOOKUP(2, A1:E2, 2, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual('d') - }) - - it('should return the lower bound for sorted values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '8'], - ['a', 'b', 'c'], - ['=HLOOKUP(4, A1:C2, 2, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual('b') - }) - - it('should return the lower bound for sorted values if all are smaller than the search value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['a', 'b', 'c'], - ['=HLOOKUP(4, A1:C2, 2, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual('c') - }) - - it('should return error when all values are greater', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['a', 'b', 'c'], - ['=HLOOKUP(0, A1:C2, 2, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('should return error when value not present using linear search', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['a', 'b', 'c'], - ['=HLOOKUP(4, A1:C2, 2, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('should find value if index build during evaluation', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=A1', '2'], - ['a', 'b', 'c'], - ['=HLOOKUP(1, A1:C2, 2, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual('b') - }) - - it('should properly calculate absolute row index', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP(3, C1:E1, 1, TRUE())', 'foo', '1', '2', '3'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('should calculate indexes properly when using binary search', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP(4, E1:J1, 1, TRUE())', null, null, null, '1', '2', '3', '4', '5'] - ], {useColumnIndex: false}) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('should calculate indexes properly when using naive approach', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP(4, E1:J1, 1, TRUE())', null, null, null, '1', '2', '3', '4', '5'] - ], {useColumnIndex: false}) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('should coerce empty arg to 0', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '2', '3', '4', '5'], - ['a', 'b', 'c', 'd', 'e'], - ['=HLOOKUP(F3, A1:E2, 2)'], - ['=HLOOKUP(, A1:E2, 2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual('a') - expect(engine.getCellValue(adr('A4'))).toEqual('a') - }) - - it('should not coerce', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP("1", A2:C2, 1)'], - [1, 2, 3], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('should properly report no match', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP("0", A2:D2, 1)'], - [1, 2, 3, '\'1'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('should properly report approximate matching', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP("2", A2:D2, 1)'], - [1, 2, 3, '\'1'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - }) - - it('should coerce null to zero when using naive approach', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP(, A2:C2, 1, FALSE())'], - [1, 3, 0], - ], {useColumnIndex: false}) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) - }) - - it('should work on row ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['=HLOOKUP(2,2:3,2)'], - [1, 2, 3], - ['a', 'b', 'c'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual('b') - }) - - it('works for strings, is not case sensitive', () => { - const engine = HyperFormula.buildFromArray([ - ['a', 'b', 'c', 'A', 'B'], - [1, 2, 3, 4, 5], - ['=HLOOKUP("A", A1:E2, 2, FALSE())'] - ], {caseSensitive: false}) - - expect(engine.getCellValue(adr('A3'))).toEqual(1) - }) - - it('works for strings, is not case sensitive even if config defines case sensitivity', () => { - const engine = HyperFormula.buildFromArray([ - ['a', 'b', 'c', 'A', 'B'], - [1, 2, 3, 4, 5], - ['=HLOOKUP("A", A1:E2, 2, FALSE())'] - ], {caseSensitive: true}) - - expect(engine.getCellValue(adr('A3'))).toEqual(1) - }) - - it('should find value in sorted range', () => { - const engine = HyperFormula.buildFromArray([ - ['a', 'B', 'c', 'd', 'e'], - [1, 2, 3, 4, 5], - ['=HLOOKUP("b", A1:E2, 2)'], - ], {caseSensitive: false}) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - }) -}) diff --git a/test/unit/interpreter/function-hour.spec.ts b/test/unit/interpreter/function-hour.spec.ts deleted file mode 100644 index 6211993377..0000000000 --- a/test/unit/interpreter/function-hour.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HOUR', () => { - it('with wrong arguments', () => { - const engine = HyperFormula.buildFromArray([['=HOUR("foo")', '=HOUR("12/30/2018")', '=HOUR(1, 2)', '=HOUR()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('with numerical arguments', () => { - const engine = HyperFormula.buildFromArray([['=HOUR(0.5123456)', '=HOUR(0)', '=HOUR(0.999999)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(12) - expect(engine.getCellValue(adr('B1'))).toEqual(0) - expect(engine.getCellValue(adr('C1'))).toEqual(0) - }) - - it('with string arguments', () => { - const engine = HyperFormula.buildFromArray([['=HOUR("14:42:59")', '=HOUR("01/01/1900 03:01:02am")', '=HOUR("31/12/2018")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(14) - expect(engine.getCellValue(adr('B1'))).toEqual(3) - expect(engine.getCellValue(adr('C1'))).toEqual(0) - }) - - it('use datenumber coercion for 1st argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=HOUR(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) - - it('propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=HOUR(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-hyperlink.spec.ts b/test/unit/interpreter/function-hyperlink.spec.ts deleted file mode 100644 index c292899855..0000000000 --- a/test/unit/interpreter/function-hyperlink.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HYPERLINK', () => { - it('with wrong arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HYPERLINK()', '=HYPERLINK("s1","s2","s3")'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('with only url argument', () => { - const url = 'https://hyperformula.handsontable.com/' - const engine = HyperFormula.buildFromArray([[`=HYPERLINK("${url}")`]]) - expect(engine.getCellValue(adr('A1'))).toEqual(url) - }) - - it('with url and label arguments', () => { - const url = 'https://hyperformula.handsontable.com/' - const linkLabel = 'HyperFormula' - const engine = HyperFormula.buildFromArray([[`=HYPERLINK("${url}","${linkLabel}")`]]) - expect(engine.getCellValue(adr('A1'))).toEqual(linkLabel) - }) - - it('when not the root expression', () => { - const url = 'https://hyperformula.handsontable.com/' - const linkLabel = 'HyperFormula' - const prefix = 'Prefix: ' - const engine = HyperFormula.buildFromArray([[`=CONCATENATE("${prefix}",HYPERLINK("${url}","${linkLabel}"))`]]) - expect(engine.getCellValue(adr('A1'))).toEqual(prefix + linkLabel) - }) - - it('when arguments are simple references', () => { - const url = 'https://hyperformula.handsontable.com/' - const linkLabel = 'HyperFormula' - const engine = HyperFormula.buildFromArray([[url, linkLabel, '=HYPERLINK(A1,B1)']]) - expect(engine.getCellValue(adr('C1'))).toEqual(linkLabel) - }) - - it('when arguments are complex', () => { - const url = 'https://hyperformula.handsontable.com/' - const linkLabel = 'HyperFormula' - const engine = HyperFormula.buildFromArray([[url, linkLabel, '=HYPERLINK(INDEX(A:A,ROW()),B1)']]) - expect(engine.getCellValue(adr('C1'))).toEqual(linkLabel) - }) -}) diff --git a/test/unit/interpreter/function-hypgeom.dist.spec.ts b/test/unit/interpreter/function-hypgeom.dist.spec.ts deleted file mode 100644 index 7ecefb6a66..0000000000 --- a/test/unit/interpreter/function-hypgeom.dist.spec.ts +++ /dev/null @@ -1,106 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function HYPGEOM.DIST', () => { - //In product #1, function takes 4 arguments. - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=HYPGEOM.DIST(1, 2, 3, 4)'], - ['=HYPGEOM.DIST(1, 2, 3, 4, 5, 6)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - //In product #1, function takes 4 arguments. - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=HYPGEOM.DIST("foo", 2, 3, 4, TRUE())'], - ['=HYPGEOM.DIST(1, "baz", 3, 4, TRUE())'], - ['=HYPGEOM.DIST(1, 2, "baz", 4, TRUE())'], - ['=HYPGEOM.DIST(1, 2, 3, "baz", TRUE())'], - ['=HYPGEOM.DIST(1, 1, 1, 1, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - //In product #1, function takes 4 arguments. - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=HYPGEOM.DIST(4, 12, 20, 40, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.150422391245528, 6) - }) - - //In product #1, function takes 4 arguments. - it('should work as pdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=HYPGEOM.DIST(4, 12, 20, 40, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.109243002735772, 6) - }) - - //In product #1, function takes 4 arguments. - it('truncation works', () => { - const engine = HyperFormula.buildFromArray([ - ['=HYPGEOM.DIST(4.9, 12, 20, 40, TRUE())'], - ['=HYPGEOM.DIST(4, 12.9, 20, 40, TRUE())'], - ['=HYPGEOM.DIST(4, 12, 20.9, 40, TRUE())'], - ['=HYPGEOM.DIST(4, 12, 20, 40.9, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.150422391245528, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.150422391245528, 6) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.150422391245528, 6) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(0.150422391245528, 6) - }) - - //In product #1, function takes 4 arguments. - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=HYPGEOM.DIST(0, 12, 20, 40, TRUE())'], - ['=HYPGEOM.DIST(-1, 12, 20, 40, TRUE())'], - ['=HYPGEOM.DIST(12, 12, 20, 40, TRUE())'], - ['=HYPGEOM.DIST(12.1, 12, 20, 40, TRUE())'], - ['=HYPGEOM.DIST(12, 20, 12, 40, TRUE())'], - ['=HYPGEOM.DIST(12.1, 20, 12, 40, TRUE())'], - ['=HYPGEOM.DIST(4, 20, 4, 20, TRUE())'], - ['=HYPGEOM.DIST(4, 20, 4, 19.9, TRUE())'], - ['=HYPGEOM.DIST(4, 4, 20, 20, TRUE())'], - ['=HYPGEOM.DIST(4, 4, 20, 19.9, TRUE())'], - ['=HYPGEOM.DIST(10, 20, 20, 30, TRUE())'], - ['=HYPGEOM.DIST(10, 20.1, 20, 30, TRUE())'], - ['=HYPGEOM.DIST(0, 0.1, 0.1, 0.9, TRUE())'], - ['=HYPGEOM.DIST(10.9, 21, 20, 30.9, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0000225475753840604, 6) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - //product #2 returns value here - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A5'))).toEqual(1) - //product #2 returns value here - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A7'))).toEqual(1) - expect(engine.getCellValue(adr('A8'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A9'))).toEqual(1) - expect(engine.getCellValue(adr('A10'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A11'))).toBeCloseTo(0.00614930629923134, 6) - //product #2 returns value here - expect(engine.getCellValue(adr('A12'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A13'))).toEqual(1) - //value should be 0 or Error, product #1 gives different answer - expect(engine.getCellValue(adr('A14'))).toEqual(0) - }) -}) diff --git a/test/unit/interpreter/function-if.spec.ts b/test/unit/interpreter/function-if.spec.ts deleted file mode 100644 index 1014de7cf6..0000000000 --- a/test/unit/interpreter/function-if.spec.ts +++ /dev/null @@ -1,139 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueDetailedType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function IF', () => { - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=IF(TRUE(), "no", 1, 2)', '=IF(1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('when value is true', () => { - const engine = HyperFormula.buildFromArray([['=IF(TRUE(), "yes", "no")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('yes') - }) - - it('when value is false', () => { - const engine = HyperFormula.buildFromArray([['=IF(FALSE(), "yes", "no")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('no') - }) - - it('coercing empty string', () => { - const engine = HyperFormula.buildFromArray([['', '=IF(A1, "yes", "no")']]) - expect(engine.getCellValue(adr('B1'))).toEqual('no') - }) - - it('when condition is weird type', () => { - const engine = HyperFormula.buildFromArray([['=IF("foo", "yes", "no")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('use coercion', () => { - const engine = HyperFormula.buildFromArray([['=IF("TRUE", "yes", "no")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('yes') - }) - - it('returns error if condition is an error', () => { - const engine = HyperFormula.buildFromArray([['=IF(4/0, "yes", "no")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('passes errors', () => { - const engine = HyperFormula.buildFromArray([['=IF(TRUE(), 4/0, "no")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('passes subtypes of second arg', () => { - const engine = HyperFormula.buildFromArray([['=IF(TRUE(),B1,C1)', '1%', '1']]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - }) - - it('passes subtypes of third arg', () => { - const engine = HyperFormula.buildFromArray([['=IF(FALSE(),B1,C1)', '1', '1%']]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - }) - - it('passes correct value when other arg is an error', () => { - const engine = HyperFormula.buildFromArray([['=IF(FALSE(), 4/0, "no")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('no') - }) - - it('when condition is number', () => { - const engine = HyperFormula.buildFromArray([['=IF(1, "yes", "no")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('yes') - }) - - it('when condition is logic function', () => { - const engine = HyperFormula.buildFromArray([['=IF(OR(1, FALSE()), "yes", "no")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('yes') - }) - - it('works when only first part is given', () => { - const engine = HyperFormula.buildFromArray([['=IF(TRUE(), "yes")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('yes') - }) - - it('works when only first part is given and condition is false', () => { - const engine = HyperFormula.buildFromArray([['=IF(FALSE(), "yes")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(false) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['0'], - ['1'], - ['3'], - ['=IF(A1:A3,"yes","no")'], - ['=IF(A1:A3,"yes","no")'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('works when condition contains a reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRUE()', '=IF(A1, "yes", "no")'], - ['=FALSE()', '=IF(A2, "yes", "no")'] - ]) - expect(engine.getCellValue(adr('B1'))).toEqual('yes') - expect(engine.getCellValue(adr('B2'))).toEqual('no') - }) - - it('works when condition is an expression', () => { - const engine = HyperFormula.buildFromArray([['=IF(1<100, "yes", "no")', '=IF(1000<100, "yes", "no")']]) - expect(engine.getCellValue(adr('A1'))).toEqual('yes') - expect(engine.getCellValue(adr('B1'))).toEqual('no') - }) - - it('works when condition is an expression with cell references', () => { - const engine = HyperFormula.buildFromArray([['10', '=IF(A1<100, "yes", "no")', '=IF(A1<1, "yes", "no")']]) - expect(engine.getCellValue(adr('B1'))).toEqual('yes') - expect(engine.getCellValue(adr('C1'))).toEqual('no') - }) - - it('works when condition references a cell with formula inside', () => { - const engine = HyperFormula.buildFromArray([ - ['100'], - ['300'], - ['=AVERAGE(A1,A2)'], - ['=IF(A3<100,"True","False")'] - ]) - expect(engine.getCellValue(adr('A3'))).toEqual(200) - expect(engine.getCellValue(adr('A4'))).toEqual('False') - }) -}) diff --git a/test/unit/interpreter/function-iferror.spec.ts b/test/unit/interpreter/function-iferror.spec.ts deleted file mode 100644 index 62ea97b4c8..0000000000 --- a/test/unit/interpreter/function-iferror.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueDetailedType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function IFERROR', () => { - it('Should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IFERROR(1)', '=IFERROR(2,3,4)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - it('when no error', () => { - const engine = HyperFormula.buildFromArray([['=IFERROR("abcd", "no")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('abcd') - }) - - it('preserves types of first arg', () => { - const engine = HyperFormula.buildFromArray([['=IFERROR(B1, 1)', '1%']]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - }) - - it('preserves types of second arg', () => { - const engine = HyperFormula.buildFromArray([['=IFERROR(NA(), B1)', '1%']]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - }) - - it('when left-error', () => { - const engine = HyperFormula.buildFromArray([['=IFERROR(1/0, "yes")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('yes') - }) - - it('when right-error', () => { - const engine = HyperFormula.buildFromArray([['=IFERROR("yes", 1/0)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('yes') - }) - - it('when both-error', () => { - const engine = HyperFormula.buildFromArray([['=IFERROR(#VALUE!, 1/0)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('when range', () => { - const engine = HyperFormula.buildFromArray([['=IFERROR("yes", A2:A3)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('when cycle', () => { - const engine = HyperFormula.buildFromArray([['=IFERROR(B1, 1)', '=B1']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('when left-parsing error', () => { - const engine = HyperFormula.buildFromArray([['=IFERROR(B1, 1/0)', '=SUM(']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-ifna.spec.ts b/test/unit/interpreter/function-ifna.spec.ts deleted file mode 100644 index 0f48a8c3ef..0000000000 --- a/test/unit/interpreter/function-ifna.spec.ts +++ /dev/null @@ -1,79 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueDetailedType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function IFNA', () => { - it('Should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IFNA(1)', '=IFNA(2, 3, 4)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - it('when no error', () => { - const engine = HyperFormula.buildFromArray([['=IFNA("abcd", "no")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('abcd') - }) - - it('when left-error NA', () => { - const engine = HyperFormula.buildFromArray([['=IFNA(COS(1, 1), "yes")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('yes') - }) - - it('when left-error DIV0', () => { - const engine = HyperFormula.buildFromArray([['=IFNA(1/0, "yes")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('when right-error', () => { - const engine = HyperFormula.buildFromArray([['=IFNA("yes", 1/0)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('yes') - }) - - it('when both-error', () => { - const engine = HyperFormula.buildFromArray([['=IFNA(COS(1, 1), 1/0)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('when both-error 2', () => { - const engine = HyperFormula.buildFromArray([['=IFNA(1/0, COS(1, 1))']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('when range', () => { - const engine = HyperFormula.buildFromArray([['=IFNA("yes", A2:A3)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('when cycle', () => { - const engine = HyperFormula.buildFromArray([['=IFNA(B1, 1)', '=B1']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.CYCLE)) - }) - - it('when cycle 2', () => { - const engine = HyperFormula.buildFromArray([['=IFNA(1, B1)', '=B1']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('preserves types of first arg', () => { - const engine = HyperFormula.buildFromArray([['=IFNA(B1, 1)', '1%']]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - }) - - it('preserves types of second arg', () => { - const engine = HyperFormula.buildFromArray([['=IFNA(NA(), B1)', '1%']]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - }) -}) diff --git a/test/unit/interpreter/function-ifs.spec.ts b/test/unit/interpreter/function-ifs.spec.ts deleted file mode 100644 index e153e21457..0000000000 --- a/test/unit/interpreter/function-ifs.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function IFS', () => { - it('Should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - [10, '=IFS()'], - [20, '=IFS(A1>90)'], - [30, '=IFS(A1>90, "A", A1>80)'], - ]) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B3'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('Should not work for wrong number of arguments when array arithmetic is on', () => { - const engine = HyperFormula.buildFromArray([ - [10, '=IFS()'], - [20, '=IFS(A1>90)'], - [30, '=IFS(A1>90, "A", A1>80)'], - ], { useArrayArithmetic: true }) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B3'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('Nominal operation', () => { - const engine = HyperFormula.buildFromArray([ - [11, '=IFS(A1>30, "A", A1>20, "B", A1>10, "C")'], - [21, '=IFS(A2>30, "A", A2>20, "B", A2>10, "C")'], - [31, '=IFS(A3>30, "A", A3>20, "B", A3>10, "C")'], - ]) - expect(engine.getCellValue(adr('B1'))).toEqual('C') - expect(engine.getCellValue(adr('B2'))).toEqual('B') - expect(engine.getCellValue(adr('B3'))).toEqual('A') - }) - - it('Return first match', () => { - const engine = HyperFormula.buildFromArray([ - [11, '=IFS(A1>10, "A", A1>10, "B", A1>10, "C")'], - [21, '=IFS(A2>10, "A", A2>10, "B", A2>10, "C")'], - [31, '=IFS(A3>10, "A", A3>10, "B", A3>10, "C")'], - ]) - expect(engine.getCellValue(adr('B1'))).toEqual('A') - expect(engine.getCellValue(adr('B2'))).toEqual('A') - expect(engine.getCellValue(adr('B3'))).toEqual('A') - }) - - it('No match found', () => { - const engine = HyperFormula.buildFromArray([ - [10, '=IFS(A1>90, "A", A1>80, "B", A1>70, "C")'] - ]) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.NoConditionMet)) - }) -}) diff --git a/test/unit/interpreter/function-imabs.spec.ts b/test/unit/interpreter/function-imabs.spec.ts deleted file mode 100644 index a15d224d64..0000000000 --- a/test/unit/interpreter/function-imabs.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function IMABS', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMABS()'], - ['=IMABS(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMABS("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMABS(0)'], - ['=IMABS("i")'], - ['=IMABS("-3+4i")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(5) - }) -}) diff --git a/test/unit/interpreter/function-imaginary.spec.ts b/test/unit/interpreter/function-imaginary.spec.ts deleted file mode 100644 index fc3040b332..0000000000 --- a/test/unit/interpreter/function-imaginary.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function IMAGINARY', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMAGINARY()'], - ['=IMAGINARY(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMAGINARY("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMAGINARY(0)'], - ['=IMAGINARY("i")'], - ['=IMAGINARY("-3+4i")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(4) - }) -}) diff --git a/test/unit/interpreter/function-imargument.spec.ts b/test/unit/interpreter/function-imargument.spec.ts deleted file mode 100644 index e223b6155c..0000000000 --- a/test/unit/interpreter/function-imargument.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function IMARGUMENT', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMARGUMENT()'], - ['=IMARGUMENT(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMARGUMENT("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMARGUMENT(0)'], - ['=IMARGUMENT("i")'], - ['=IMARGUMENT("-3+4i")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(1.5707963267949, 6) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(2.21429743558818) - }) -}) diff --git a/test/unit/interpreter/function-imconjugate.spec.ts b/test/unit/interpreter/function-imconjugate.spec.ts deleted file mode 100644 index 99d516d5bd..0000000000 --- a/test/unit/interpreter/function-imconjugate.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function IMCONJUGATE', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCONJUGATE()'], - ['=IMCONJUGATE(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCONJUGATE("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCONJUGATE(0)'], - ['=IMCONJUGATE("i")'], - ['=IMCONJUGATE("-3+4i")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('0') - expect(engine.getCellValue(adr('A2'))).toEqual('-i') - expect(engine.getCellValue(adr('A3'))).toEqual('-3-4i') - }) -}) diff --git a/test/unit/interpreter/function-imcos.spec.ts b/test/unit/interpreter/function-imcos.spec.ts deleted file mode 100644 index 2599353fc3..0000000000 --- a/test/unit/interpreter/function-imcos.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMCOS', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCOS()'], - ['=IMCOS(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCOS("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCOS(0)'], - ['=IMCOS("i")'], - ['=IMCOS("-3+4i")'], - ]) - - expectToBeCloseForComplex(engine, 'A1', '1') - expectToBeCloseForComplex(engine, 'A2', '1.5430806348') - expectToBeCloseForComplex(engine, 'A3', '-27.0349456030742+3.85115333481178i') - }) -}) diff --git a/test/unit/interpreter/function-imcosh.spec.ts b/test/unit/interpreter/function-imcosh.spec.ts deleted file mode 100644 index d141acfcc3..0000000000 --- a/test/unit/interpreter/function-imcosh.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMCOSH', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCOSH()'], - ['=IMCOSH(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCOSH("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCOSH(0)'], - ['=IMCOSH("i")'], - ['=IMCOSH("-3+4i")'], - ]) - - expectToBeCloseForComplex(engine, 'A1', '1') - expectToBeCloseForComplex(engine, 'A2', '0.5403023058681398') - expectToBeCloseForComplex(engine, 'A3', '-6.58066304055116+7.58155274274655i') - }) -}) diff --git a/test/unit/interpreter/function-imcot.spec.ts b/test/unit/interpreter/function-imcot.spec.ts deleted file mode 100644 index 00d5e67114..0000000000 --- a/test/unit/interpreter/function-imcot.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMCOT', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCOT()'], - ['=IMCOT(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCOT("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCOT(0)'], - ['=IMCOT("i")'], - ['=IMCOT("-3+4i")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expectToBeCloseForComplex(engine, 'A2', '-1.31303528549933i') - expectToBeCloseForComplex(engine, 'A3', '0.000187587737983659-1.00064439247156i') - }) -}) diff --git a/test/unit/interpreter/function-imcsc.spec.ts b/test/unit/interpreter/function-imcsc.spec.ts deleted file mode 100644 index c0bafd545e..0000000000 --- a/test/unit/interpreter/function-imcsc.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMCSC', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCSC()'], - ['=IMCSC(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCSC("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCSC(0)'], - ['=IMCSC("i")'], - ['=IMCSC("-3+4i")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expectToBeCloseForComplex(engine, 'A2', '-0.850918128239322i') - expectToBeCloseForComplex(engine, 'A3', '-0.0051744731840194+0.036275889628626i') - }) -}) diff --git a/test/unit/interpreter/function-imcsch.spec.ts b/test/unit/interpreter/function-imcsch.spec.ts deleted file mode 100644 index 704ab8d961..0000000000 --- a/test/unit/interpreter/function-imcsch.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMCSCH', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCSCH()'], - ['=IMCSCH(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCSCH("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMCSCH(0)'], - ['=IMCSCH("i")'], - ['=IMCSCH("-3+4i")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expectToBeCloseForComplex(engine, 'A2', '-1.18839510577812i') - expectToBeCloseForComplex(engine, 'A3', '0.0648774713706355+0.0754898329158637i') - }) -}) diff --git a/test/unit/interpreter/function-imdiv.spec.ts b/test/unit/interpreter/function-imdiv.spec.ts deleted file mode 100644 index 9c678df332..0000000000 --- a/test/unit/interpreter/function-imdiv.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function IMDIV', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMDIV(1)'], - ['=IMDIV(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMDIV("foo", 1)'], - ['=IMDIV(1, "foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMDIV(0, 1)'], - ['=IMDIV("i", "-i")'], - ['=IMDIV("-3+4i", "1+i")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('0') - expect(engine.getCellValue(adr('A2'))).toEqual('-1') - expect(engine.getCellValue(adr('A3'))).toEqual('0.5+3.5i') - }) -}) diff --git a/test/unit/interpreter/function-imexp.spec.ts b/test/unit/interpreter/function-imexp.spec.ts deleted file mode 100644 index d1a1b9dc60..0000000000 --- a/test/unit/interpreter/function-imexp.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMEXP', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMEXP()'], - ['=IMEXP(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMEXP("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMEXP(0)'], - ['=IMEXP("i")'], - ['=IMEXP("-3+4i")'], - ]) - - expectToBeCloseForComplex(engine, 'A1', '1') - expectToBeCloseForComplex(engine, 'A2', '0.54030230586814+0.841470984807897i') - expectToBeCloseForComplex(engine, 'A3', '-0.0325429996401548-0.0376789775748659i') - }) -}) diff --git a/test/unit/interpreter/function-imln.spec.ts b/test/unit/interpreter/function-imln.spec.ts deleted file mode 100644 index 0b3ed4f19b..0000000000 --- a/test/unit/interpreter/function-imln.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMLN', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMLN()'], - ['=IMLN(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMLN("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMLN(0)'], - ['=IMLN("i")'], - ['=IMLN("-3+4i")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expectToBeCloseForComplex(engine, 'A2', '1.5707963267949i') - expectToBeCloseForComplex(engine, 'A3', '1.6094379124341+2.21429743558818i') - }) -}) diff --git a/test/unit/interpreter/function-imlog10.spec.ts b/test/unit/interpreter/function-imlog10.spec.ts deleted file mode 100644 index c708e2088a..0000000000 --- a/test/unit/interpreter/function-imlog10.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMLOG10', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMLOG10()'], - ['=IMLOG10(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMLOG10("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMLOG10(0)'], - ['=IMLOG10("i")'], - ['=IMLOG10("-3+4i")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expectToBeCloseForComplex(engine, 'A2', '0.682188176920921i') - expectToBeCloseForComplex(engine, 'A3', '0.698970004336019+0.961657157568468i') - }) -}) diff --git a/test/unit/interpreter/function-imlog2.spec.ts b/test/unit/interpreter/function-imlog2.spec.ts deleted file mode 100644 index d07892410d..0000000000 --- a/test/unit/interpreter/function-imlog2.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMLOG2', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMLOG2()'], - ['=IMLOG2(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMLOG2("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMLOG2(0)'], - ['=IMLOG2("i")'], - ['=IMLOG2("-3+4i")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expectToBeCloseForComplex(engine, 'A2', '2.2661800709136i') - expectToBeCloseForComplex(engine, 'A3', '2.32192809488736+3.19455592937622i') - }) -}) diff --git a/test/unit/interpreter/function-impower.spec.ts b/test/unit/interpreter/function-impower.spec.ts deleted file mode 100644 index 533e3ee32b..0000000000 --- a/test/unit/interpreter/function-impower.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMPOWER', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMPOWER(1)'], - ['=IMPOWER(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMPOWER("foo", 2)'], - ['=IMPOWER(1, "foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMPOWER(0, 1)'], - ['=IMPOWER("-4", 0.1)'], - ['=IMPOWER("-3+4i", -1)'], - ['=IMPOWER(0, -1)'], - ['=IMPOWER(0, 0)'], - ['=IMPOWER("i", 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('0') - expectToBeCloseForComplex(engine, 'A2', '1.09247705577745+0.35496731310463i') - expectToBeCloseForComplex(engine, 'A3', '-0.12-0.16i') - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expect(engine.getCellValue(adr('A6'))).toEqual('1') - }) -}) diff --git a/test/unit/interpreter/function-improduct.spec.ts b/test/unit/interpreter/function-improduct.spec.ts deleted file mode 100644 index 7194ccff50..0000000000 --- a/test/unit/interpreter/function-improduct.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function IMPRODUCT', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMPRODUCT()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should coerce explicit arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMPRODUCT(0)'], - ['=IMPRODUCT("i", "-1.5")'], - ['=IMPRODUCT("-3+4i", "1+i", 1, 2, "3")'], - ['=IMPRODUCT("i",)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('0') - expect(engine.getCellValue(adr('A2'))).toEqual('-1.5i') - expect(engine.getCellValue(adr('A3'))).toEqual('-42+6i') - expect(engine.getCellValue(adr('A4'))).toEqual('i') - }) - - it('should fail for non-coercible explicit arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMPRODUCT(1, TRUE())'], - ['=IMPRODUCT(2, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should not coerce range arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMPRODUCT(B1:C1)', 1, '2+i'], - ['=IMPRODUCT(B2:D2)', 1, null, null], - ['=IMPRODUCT(B3:D3)', 'i', 'abcd', true], - ['=IMPRODUCT(B4:D4,)', 'i', '=NA()', 1], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('2+i') - expect(engine.getCellValue(adr('A2'))).toEqual('1') - expect(engine.getCellValue(adr('A3'))).toEqual('i') - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-imreal.spec.ts b/test/unit/interpreter/function-imreal.spec.ts deleted file mode 100644 index 35a7cbe856..0000000000 --- a/test/unit/interpreter/function-imreal.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function IMREAL', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMREAL()'], - ['=IMREAL(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMREAL("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMREAL(1)'], - ['=IMREAL("i")'], - ['=IMREAL("-3+4i")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(-3) - }) -}) diff --git a/test/unit/interpreter/function-imsec.spec.ts b/test/unit/interpreter/function-imsec.spec.ts deleted file mode 100644 index 38c7f112b4..0000000000 --- a/test/unit/interpreter/function-imsec.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMSEC', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSEC()'], - ['=IMSEC(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSEC("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSEC(0)'], - ['=IMSEC("i")'], - ['=IMSEC("-3+4i")'], - ]) - - expectToBeCloseForComplex(engine, 'A1', '1') - expectToBeCloseForComplex(engine, 'A2', '0.648054273663885') - expectToBeCloseForComplex(engine, 'A3', '-0.0362534969158689-0.00516434460775318i') - }) -}) diff --git a/test/unit/interpreter/function-imsech.spec.ts b/test/unit/interpreter/function-imsech.spec.ts deleted file mode 100644 index a4ea4e1313..0000000000 --- a/test/unit/interpreter/function-imsech.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMSECH', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSECH()'], - ['=IMSECH(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSECH("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSECH(0)'], - ['=IMSECH("i")'], - ['=IMSECH("-3+4i")'], - ]) - - expectToBeCloseForComplex(engine, 'A1', '1') - expectToBeCloseForComplex(engine, 'A2', '1.85081571768093') - expectToBeCloseForComplex(engine, 'A3', '-0.0652940278579471-0.0752249603027732i') - }) -}) diff --git a/test/unit/interpreter/function-imsin.spec.ts b/test/unit/interpreter/function-imsin.spec.ts deleted file mode 100644 index bccd6e7f20..0000000000 --- a/test/unit/interpreter/function-imsin.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMSIN', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSIN()'], - ['=IMSIN(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSIN("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSIN(0)'], - ['=IMSIN("i")'], - ['=IMSIN("-3+4i")'], - ]) - - expectToBeCloseForComplex(engine, 'A1', '0') - expectToBeCloseForComplex(engine, 'A2', '1.1752011936438i') - expectToBeCloseForComplex(engine, 'A3', '-3.85373803791938-27.0168132580039i') - }) -}) diff --git a/test/unit/interpreter/function-imsinh.spec.ts b/test/unit/interpreter/function-imsinh.spec.ts deleted file mode 100644 index 5f71810254..0000000000 --- a/test/unit/interpreter/function-imsinh.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMSINH', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSINH()'], - ['=IMSINH(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSINH("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSINH(0)'], - ['=IMSINH("i")'], - ['=IMSINH("-3+4i")'], - ]) - - expectToBeCloseForComplex(engine, 'A1', '0') - expectToBeCloseForComplex(engine, 'A2', '0.841470984807897i') - expectToBeCloseForComplex(engine, 'A3', '6.548120040911-7.61923172032141i') - }) -}) diff --git a/test/unit/interpreter/function-imsqrt.spec.ts b/test/unit/interpreter/function-imsqrt.spec.ts deleted file mode 100644 index 5973b6ff8a..0000000000 --- a/test/unit/interpreter/function-imsqrt.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMSQRT', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSQRT()'], - ['=IMSQRT(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSQRT("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSQRT(0)'], - ['=IMSQRT("-4")'], - ['=IMSQRT("-3+4i")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('0') - expectToBeCloseForComplex(engine, 'A2', '2i') - expectToBeCloseForComplex(engine, 'A3', '1+2i') - }) -}) diff --git a/test/unit/interpreter/function-imsub.spec.ts b/test/unit/interpreter/function-imsub.spec.ts deleted file mode 100644 index 9ce4a11785..0000000000 --- a/test/unit/interpreter/function-imsub.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function IMSUB', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSUB(1)'], - ['=IMSUB(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSUB("foo", 1)'], - ['=IMSUB(1, "foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSUB(0, 1)'], - ['=IMSUB("i", "-i")'], - ['=IMSUB("-3+4i", "1+i")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('-1') - expect(engine.getCellValue(adr('A2'))).toEqual('2i') - expect(engine.getCellValue(adr('A3'))).toEqual('-4+3i') - }) -}) diff --git a/test/unit/interpreter/function-imsum.spec.ts b/test/unit/interpreter/function-imsum.spec.ts deleted file mode 100644 index 49b95e48d4..0000000000 --- a/test/unit/interpreter/function-imsum.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function IMSUM', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSUM()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should coerce explicit arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSUM(0)'], - ['=IMSUM("i", "-1.5")'], - ['=IMSUM("-3+4i", "1+i", 1, 2, "3")'], - ['=IMSUM("i",)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('0') - expect(engine.getCellValue(adr('A2'))).toEqual('-1.5+i') - expect(engine.getCellValue(adr('A3'))).toEqual('4+5i') - expect(engine.getCellValue(adr('A4'))).toEqual('i') - }) - - it('should fail for non-coercible explicit arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSUM(1, TRUE())'], - ['=IMSUM(2, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should not coerce range arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMSUM(B1:C1)', 1, '2+i'], - ['=IMSUM(B2:D2)', 1, null, null], - ['=IMSUM(B3:D3)', 'i', 'abcd', true], - ['=IMSUM(B4:D4,)', 'i', '=NA()', 1], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('3+i') - expect(engine.getCellValue(adr('A2'))).toEqual('1') - expect(engine.getCellValue(adr('A3'))).toEqual('i') - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-imtan.spec.ts b/test/unit/interpreter/function-imtan.spec.ts deleted file mode 100644 index b184650709..0000000000 --- a/test/unit/interpreter/function-imtan.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectToBeCloseForComplex} from '../testUtils' - -describe('Function IMTAN', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMTAN()'], - ['=IMTAN(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMTAN("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ComplexNumberExpected)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=IMTAN(0)'], - ['=IMTAN("i")'], - ['=IMTAN("-3+4i")'], - ]) - - expectToBeCloseForComplex(engine, 'A1', '0') - expectToBeCloseForComplex(engine, 'A2', '0.761594155955765i') - expectToBeCloseForComplex(engine, 'A3', '0.000187346204629478+0.999355987381473i') - }) -}) diff --git a/test/unit/interpreter/function-index.spec.ts b/test/unit/interpreter/function-index.spec.ts deleted file mode 100644 index a8e15f9202..0000000000 --- a/test/unit/interpreter/function-index.spec.ts +++ /dev/null @@ -1,118 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function INDEX', () => { - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=INDEX()'], - ['=INDEX(B1:D3, 1, 1, 42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('requires 2nd and 3rd arguments to be integers', () => { - const engine = HyperFormula.buildFromArray([ - ['=INDEX(B1:B1, "foo", 1)'], - ['=INDEX(B1:B1, 1, "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('requires 2nd argument to be in bounds of range', () => { - const engine = HyperFormula.buildFromArray([ - ['=INDEX(B1:D3, -1, 1)'], - ['=INDEX(B1:D3, 4, 1)'], - ['=INDEX(42, -1, 1)'], - ['=INDEX(42, 2, 1)'], - ['=INDEX(B1, -1, 1)'], - ['=INDEX(B1, 2, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('requires 2nd and 3rd arguments to be in bounds of range', () => { - const engine = HyperFormula.buildFromArray([ - ['=INDEX(B1:D3, 1, -1)'], - ['=INDEX(B1:D3, 1, 4)'], - ['=INDEX(42, 1, -1)'], - ['=INDEX(42, 1, 2)'], - ['=INDEX(B1, 1, -1)'], - ['=INDEX(B1, 1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('works for range and nonzero arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=INDEX(B1:C2, 1, 1)', '1', '2'], - ['=INDEX(B1:C2, 1, 2)', '3', '4'], - ['=INDEX(B1:C2, 2, 1)'], - ['=INDEX(B1:C2, 2, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqual(4) - }) - - it('should propagate errors properly', () => { - const engine = HyperFormula.buildFromArray([ - ['=INDEX(B1:C3, 1, 1/0)'], - ['=INDEX(NA(), 1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('should return VALUE error when one of the cooridnate is 0 or null', () => { - const engine = HyperFormula.buildFromArray([ - ['=INDEX(B1:D5, 0, 2)'], - ['=INDEX(B1:D5, 2, 0)'], - ['=INDEX(B1:D5,,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - }) - - it('should work for scalars too', () => { - const engine = HyperFormula.buildFromArray([ - ['foo'], - ['=INDEX(A1, 1, 1)'], - ['=INDEX(42, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual('foo') - expect(engine.getCellValue(adr('A3'))).toEqual(42) - }) - - it('should assume first column if no last argument', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2], - [3, 4], - ['=INDEX(A1:B2, 2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(3) - }) -}) diff --git a/test/unit/interpreter/function-int.spec.ts b/test/unit/interpreter/function-int.spec.ts deleted file mode 100644 index 1aa6d80128..0000000000 --- a/test/unit/interpreter/function-int.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function INT', () => { - it('number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=INT()', '=INT(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works for positive numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=INT(1.3)', '=INT(1.7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('B1'))).toBe(1) - }) - - it('works for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=INT(-1.3)', '=INT(-1.7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(-1) - expect(engine.getCellValue(adr('B1'))).toBe(-1) - }) - - it('use coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['=INT("42.3")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(42) - }) - - it('propagates error', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/0'], - ['=INT(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-interval.spec.ts b/test/unit/interpreter/function-interval.spec.ts deleted file mode 100644 index fd5e9f5da7..0000000000 --- a/test/unit/interpreter/function-interval.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function INTERVAL', () => { - it('with wrong arguments', () => { - const engine = HyperFormula.buildFromArray([['=INTERVAL("foo")', '=INTERVAL("12/30/2018")', '=INTERVAL(1, 2)', '=INTERVAL()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('with numerical arguments', () => { - const engine = HyperFormula.buildFromArray([['=INTERVAL(0)', '=INTERVAL(10000000)', '=INTERVAL(365.1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('PT') - expect(engine.getCellValue(adr('B1'))).toEqual('P3M25DT17H46M40S') - expect(engine.getCellValue(adr('C1'))).toEqual('PT6M5S') - }) - - it('with string arguments', () => { - const engine = HyperFormula.buildFromArray([['=INTERVAL("31/12/1899")', '=INTERVAL("01/01/1900")', '=INTERVAL("31/12/2018")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual('PT1S') - expect(engine.getCellValue(adr('B1'))).toEqual('PT2S') - expect(engine.getCellValue(adr('C1'))).toEqual('PT12H4M25S') - }) - - it('use datenumber coercion for 1st argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=INTERVAL(TRUE())'], - ['=INTERVAL("1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('PT1S') - expect(engine.getCellValue(adr('A2'))).toEqual('PT1S') - }) - - it('propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=INTERVAL(NA())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-ipmt.spec.ts b/test/unit/interpreter/function-ipmt.spec.ts deleted file mode 100644 index 06105395bf..0000000000 --- a/test/unit/interpreter/function-ipmt.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function IPMT', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IPMT(1, 1)', '=IPMT(1, 1, 1, 1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=IPMT(1%, 12, 360, 100000)', '=IPMT(1%, 12, 360, 100000, 30000)', '=IPMT(1%, 12, 360, 100000, 30000, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-996.690428219826) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-995.697556685774) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(-985.839165035419) - }) -}) diff --git a/test/unit/interpreter/function-irr.spec.ts b/test/unit/interpreter/function-irr.spec.ts deleted file mode 100644 index cee6e693b9..0000000000 --- a/test/unit/interpreter/function-irr.spec.ts +++ /dev/null @@ -1,464 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function IRR', () => { - const requiredFinancialPrecision = 6 // epsilon = 0.0000005 - - describe('argument validation', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=IRR()', '=IRR(A2:A3, 0.1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should accept a single argument (values)', () => { - const engine = HyperFormula.buildFromArray([ - [-1000], - [100], - [200], - [300], - [400], - [500], - ['=IRR(A1:A6)'], - ]) - - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(0.120058, requiredFinancialPrecision) - }) - - it('should accept two arguments (values, guess)', () => { - const engine = HyperFormula.buildFromArray([ - [-1000], - [100], - [200], - [300], - [400], - [500], - ['=IRR(A1:A6, 0.15)'], - ]) - - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(0.120058, requiredFinancialPrecision) - }) - }) - - describe('error handling', () => { - it('should return #NUM! if algorithm does not converge', () => { - const engine = HyperFormula.buildFromArray([ - ['=IRR({100, 100, 100})'], - ['=IRR({-100, -100, -100})'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM)) - }) - - it('should return #NUM! when derivative is too small', () => { - // At r=0 with cash flows {-0.5, 2, -1}: - // dnpv = -1*2/(1+0)^2 - 2*(-1)/(1+0)^3 = -2 + 2 = 0 - // npv = -0.5 + 2 - 1 = 0.5 != 0 - // So derivative is zero but NPV is not, Newton-Raphson cannot proceed - const engine = HyperFormula.buildFromArray([ - ['=IRR({-0.5, 2, -1}, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM)) - }) - - it('should return #NUM! when max iterations reached', () => { - // Cash flows that cause oscillation without convergence - const engine = HyperFormula.buildFromArray([ - ['=IRR({-1, 2, -1.0001}, 0.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM)) - }) - - it('should return #NUM! when rate causes factor overflow', () => { - // Very large guess with multiple periods causes Math.pow(1+rate, i) to overflow to Infinity - const engine = HyperFormula.buildFromArray([ - ['=IRR({-1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 1e100)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM)) - }) - - it('should return #NUM! when values do not contain at least one positive and one negative value', () => { - const engine = HyperFormula.buildFromArray([ - ['=IRR({1, 2, 3})'], - ['=IRR({-1, -2, -3})'], - ['=IRR({0, 0, 0})'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM)) - }) - - it('should propagate errors from values', () => { - const engine = HyperFormula.buildFromArray([ - ['=IRR(A2:A4)'], - [-1000], - ['=1/0'], - [500], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should propagate errors from guess', () => { - const engine = HyperFormula.buildFromArray([ - [-1000, '=IRR(A1:A4, 1/0)'], - [100], - [200], - [800], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should return #VALUE! error when guess is not a number', () => { - const engine = HyperFormula.buildFromArray([ - [-1000], - [100], - [200], - [800], - ['=IRR(A1:A4, "abc")'], - ]) - - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return #VALUE! error when guess is -1', () => { - const engine = HyperFormula.buildFromArray([ - ['=IRR({-100, 200, 300}, -1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE)) - }) - - it('should return #VALUE! error when guess is less than -1', () => { - const engine = HyperFormula.buildFromArray([ - ['=IRR({-100, 200, 300}, -2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE)) - }) - - it('should return #NUM! for empty range', () => { - const engine = HyperFormula.buildFromArray([ - ['=IRR(B1:B5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM)) - }) - }) - - describe('value type', () => { - it('should set the value type to percent', () => { - const engine = HyperFormula.buildFromArray([ - [-1000], - [100], - [200], - [300], - [400], - [500], - ['=IRR(A1:A6)'], - ]) - - expect(engine.getCellValueDetailedType(adr('A7'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - }) - }) - - describe('handling of text, logical, and empty values in ranges', () => { - it('should ignore text values in ranges', () => { - const engine = HyperFormula.buildFromArray([ - [-1000], - ['text'], - [100], - [200], - [300], - [400], - [500], - ['=IRR(A1:A7)'], - ]) - - expect(engine.getCellValue(adr('A8'))).toBeCloseTo(0.120058, requiredFinancialPrecision) - }) - - it('should ignore logical values in ranges', () => { - const engine = HyperFormula.buildFromArray([ - [-1000], - [true], - [100], - [200], - [300], - [false], - [400], - [500], - ['=IRR(A1:A8)'], - ]) - - expect(engine.getCellValue(adr('A9'))).toBeCloseTo(0.120058, requiredFinancialPrecision) - }) - - it('should ignore empty cells in ranges', () => { - const engine = HyperFormula.buildFromArray([ - [-1000], - [null], - [100], - [200], - [300], - [null], - [400], - [500], - ['=IRR(A1:A8)'], - ]) - - expect(engine.getCellValue(adr('A9'))).toBeCloseTo(0.120058, requiredFinancialPrecision) - }) - }) - - describe('basic calculations', () => { - it('should compute IRR for basic cash flows', () => { - const engine = HyperFormula.buildFromArray([ - [-70000], - [12000], - [15000], - [18000], - [21000], - ['=IRR(A1:A5)'], - ]) - - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(-0.0212448, requiredFinancialPrecision) - }) - - it('should compute IRR for investment with positive return', () => { - const engine = HyperFormula.buildFromArray([ - [-70000], - [12000], - [15000], - [18000], - [21000], - [26000], - ['=IRR(A1:A6)'], - ]) - - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(0.0866309, requiredFinancialPrecision) - }) - - it('should compute IRR correctly for official example', () => { - // Example from https://support.microsoft.com/en-us/office/irr-function-64925eaa-9988-495b-b290-3ad0c163c1bc - const engine = HyperFormula.buildFromArray([ - [-70000], - [12000], - [15000], - [18000], - [21000], - [26000], - ['=IRR(A1:A5)'], // Four years return: ~-2.1% - ['=IRR(A1:A6)'], // Five years return: ~8.7% - ['=IRR(A1:A3, -10%)'], // Two years with guess: ~-44.4% - ]) - - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(-0.0212448, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A8'))).toBeCloseTo(0.0866309, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A9'))).toBeCloseTo(-0.443507, requiredFinancialPrecision) - }) - - it('should work with inline arrays', () => { - const engine = HyperFormula.buildFromArray([ - ['=IRR({-100, 50, 50, 50})'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.233752, requiredFinancialPrecision) - }) - }) - - describe('guess parameter', () => { - it('should use default guess of 0.1 (10%) when not specified', () => { - const engine = HyperFormula.buildFromArray([ - [-1000], - [100], - [200], - [300], - [400], - [500], - ['=IRR(A1:A6)'], - ['=IRR(A1:A6, 0.1)'], - ]) - - expect(engine.getCellValue(adr('A7'))).toBe(engine.getCellValue(adr('A8'))) - }) - - it('should allow guess close to -1', () => { - const engine = HyperFormula.buildFromArray([ - [-1000], - [100], - [200], - [300], - [400], - [500], - ['=IRR(A1:A6, -0.9)'], - ]) - - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(0.120058, requiredFinancialPrecision) - }) - - it('should accept guess value of 0', () => { - const engine = HyperFormula.buildFromArray([ - [-1000], - [100], - [200], - [300], - [400], - [500], - ['=IRR(A1:A6, 0)'], - ]) - - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(0.120058, requiredFinancialPrecision) - }) - - it('should accept large positive guess values', () => { - const engine = HyperFormula.buildFromArray([ - [-100], - [200], - [300], - ['=IRR(A1:A3, 1)'], - ['=IRR(A1:A3, 2)'], - ]) - - // Should still find the correct IRR (or converge) - expect(typeof engine.getCellValue(adr('A4'))).toBe('number') - expect(typeof engine.getCellValue(adr('A5'))).toBe('number') - }) - }) - - describe('edge cases', () => { - it('should handle very small cash flows', () => { - const engine = HyperFormula.buildFromArray([ - ['=IRR({-0.0001, 0.00005, 0.00006})'], - ]) - - expect(typeof engine.getCellValue(adr('A1'))).toBe('number') - }) - - it('should handle very large cash flows', () => { - const engine = HyperFormula.buildFromArray([ - ['=IRR({-1000000000, 500000000, 600000000})'], - ]) - - expect(typeof engine.getCellValue(adr('A1'))).toBe('number') - }) - - it('should handle cash flows with zero values', () => { - const engine = HyperFormula.buildFromArray([ - ['=IRR({-1000, 0, 0, 0, 2000})'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.189207, 5) - }) - - it('should handle many periods', () => { - const engine = HyperFormula.buildFromArray([ - [-10000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500], - [500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500], - ['=IRR(A1:K2)'], - ]) - - expect(typeof engine.getCellValue(adr('A3'))).toBe('number') - }) - }) - - describe('vertical and horizontal ranges', () => { - it('should work with vertical range', () => { - const engine = HyperFormula.buildFromArray([ - [-1000], - [100], - [200], - [300], - [400], - [500], - ['=IRR(A1:A6)'], - ]) - - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(0.120058, requiredFinancialPrecision) - }) - - it('should work with horizontal range', () => { - const engine = HyperFormula.buildFromArray([ - [-1000, 100, 200, 300, 400, 500, '=IRR(A1:F1)'], - ]) - - expect(engine.getCellValue(adr('G1'))).toBeCloseTo(0.120058, requiredFinancialPrecision) - }) - - it('should work with 2D range (row by row)', () => { - const engine = HyperFormula.buildFromArray([ - [-1000, 100, 200], - [300, 400, 500], - ['=IRR(A1:C2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.120058, requiredFinancialPrecision) - }) - }) - - describe('cell references', () => { - it('should work with individual cell references', () => { - const engine = HyperFormula.buildFromArray([ - [-1000, 500, 600, '=IRR({A1, B1, C1})'], - ]) - - expect(typeof engine.getCellValue(adr('D1'))).toBe('number') - }) - - it('should work with named ranges', () => { - const engine = HyperFormula.buildFromArray([ - [-1000, 500, 600], - ]) - engine.addNamedExpression('cashflows', '=Sheet1!$A$1:$C$1') - engine.setCellContents(adr('A2'), [['=IRR(cashflows)']]) - - expect(typeof engine.getCellValue(adr('A2'))).toBe('number') - }) - }) - - describe('relationship with NPV', () => { - it('NPV at IRR rate should be approximately zero', () => { - const engine = HyperFormula.buildFromArray([ - [-70000], - [12000], - [15000], - [18000], - [21000], - [26000], - ['=IRR(A1:A6)'], - ['=NPV(A7, A1:A6)'], - ]) - - // NPV at IRR rate should be very close to zero - // Note: This depends on how NPV is implemented - it may need adjustment - const npvValue = engine.getCellValue(adr('A8')) - - expect(Math.abs(npvValue as number)).toBeLessThan(0.01) - }) - }) - - describe('scenarios with no solution or multiple solutions', () => { - it('should return either #NUM! or one of the solutions', () => { - // Non-conventional cash flows may have multiple IRRs - // Cash flow {-1000, 3000, -2500} has two valid IRRs: ~0.25 (25%) and ~1.0 (100%) - const engine = HyperFormula.buildFromArray([ - ['=IRR({-1000, 3000, -2500})'], - ]) - - const result = engine.getCellValue(adr('A1')) - - expect(result).toEqualError(detailedError(ErrorType.NUM)) - }) - }) -}) diff --git a/test/unit/interpreter/function-isbinary.spec.ts b/test/unit/interpreter/function-isbinary.spec.ts deleted file mode 100644 index a35200ec22..0000000000 --- a/test/unit/interpreter/function-isbinary.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {HyperFormula} from '../../../src' -import {adr} from '../testUtils' - -describe('Function ISBINARY', () => { - it('should return true for binary numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISBINARY("1010")', '=ISBINARY(1001)', '=ISBINARY(010)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(true) - expect(engine.getCellValue(adr('B1'))).toEqual(true) - expect(engine.getCellValue(adr('C1'))).toEqual(true) - }) - - it('should return false otherwise', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISBINARY("foo")', '=ISBINARY(123)', '=ISBINARY(TRUE())'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(false) - expect(engine.getCellValue(adr('B1'))).toEqual(false) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - }) -}) diff --git a/test/unit/interpreter/function-isblank.spec.ts b/test/unit/interpreter/function-isblank.spec.ts deleted file mode 100644 index 61038d6c53..0000000000 --- a/test/unit/interpreter/function-isblank.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ISBLANK', () => { - it('should return true for references to empty cells', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=ISBLANK(A1)', '=ISBLANK(A2)'], - ['=A1'], - ]) - expect(engine.getCellValue(adr('B1'))).toEqual(true) - expect(engine.getCellValue(adr('C1'))).toEqual(true) - }) - - it('should return false for empty string', () => { - const engine = HyperFormula.buildFromArray([['', '=ISBLANK(A1)']]) - expect(engine.getCellValue(adr('B1'))).toEqual(false) - }) - - it('should return false if it is not reference to empty cell', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=ISBLANK("")', '=ISBLANK(4)', '=ISBLANK(CONCATENATE(A1,A1))'], - ]) - expect(engine.getCellValue(adr('B1'))).toEqual(false) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - expect(engine.getCellValue(adr('D1'))).toEqual(false) - }) - - it('takes exactly one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISBLANK(A3, A2)', '=ISBLANK()'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('no error propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISBLANK(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(false) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['0'], - [null], - [null], - ['=ISBLANK(A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-iserr.spec.ts b/test/unit/interpreter/function-iserr.spec.ts deleted file mode 100644 index a0d61397fb..0000000000 --- a/test/unit/interpreter/function-iserr.spec.ts +++ /dev/null @@ -1,53 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ISERR', () => { - it('should return true for common errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISERR(1/0)', '=ISERR(FOO())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(true) - expect(engine.getCellValue(adr('B1'))).toEqual(true) - }) - - it('should return false for #N/A!', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISERR(TRUE(1))'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(false) - }) - - it('should return false for valid formulas', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISERR(1)', '=ISERR(TRUE())', '=ISERR("foo")', '=ISERR(ISERR(1/0))', '=ISERR(A1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(false) - expect(engine.getCellValue(adr('B1'))).toEqual(false) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - expect(engine.getCellValue(adr('D1'))).toEqual(false) - expect(engine.getCellValue(adr('E1'))).toEqual(false) - }) - - it('takes exactly one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISERR(1, 2)', '=ISERR()'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/1'], - ['=4/0'], - ['=4/2'], - ['=ISERR(A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-iserror.spec.ts b/test/unit/interpreter/function-iserror.spec.ts deleted file mode 100644 index 4095ca4feb..0000000000 --- a/test/unit/interpreter/function-iserror.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ISERROR', () => { - it('should return true for common errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISERROR(1/0)', '=ISERROR(FOO())', '=ISERROR(TRUE(1))'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(true) - expect(engine.getCellValue(adr('B1'))).toEqual(true) - expect(engine.getCellValue(adr('C1'))).toEqual(true) - }) - - it('should return false for valid formulas', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISERROR(1)', '=ISERROR(TRUE())', '=ISERROR("foo")', '=ISERROR(ISERROR(1/0))', '=ISERROR(A1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(false) - expect(engine.getCellValue(adr('B1'))).toEqual(false) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - expect(engine.getCellValue(adr('D1'))).toEqual(false) - expect(engine.getCellValue(adr('E1'))).toEqual(false) - }) - - it('takes exactly one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISERROR(1, 2)', '=ISERROR()'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/1'], - ['=4/0'], - ['=4/2'], - ['=ISERROR(A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-iseven.spec.ts b/test/unit/interpreter/function-iseven.spec.ts deleted file mode 100644 index f6ee0c8639..0000000000 --- a/test/unit/interpreter/function-iseven.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ISEVEN', () => { - it('number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISEVEN()', '=ISEVEN(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISEVEN(1)', '=ISEVEN(2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('B1'))).toBe(true) - }) - - it('use coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISEVEN("42")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - }) - - it('propagates error', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/0'], - ['=ISEVEN(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-isformula.spec.ts b/test/unit/interpreter/function-isformula.spec.ts deleted file mode 100644 index aa65579c52..0000000000 --- a/test/unit/interpreter/function-isformula.spec.ts +++ /dev/null @@ -1,88 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ISFORMULA', () => { - it('should return true for cell with formula', () => { - const engine = HyperFormula.buildFromArray([ - ['=A1', '=ISFORMULA(A1)'] - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(true) - }) - - it('should return false for cell without formula', () => { - const engine = HyperFormula.buildFromArray([ - ['foo', '=ISFORMULA(A1)', '=ISFORMULA(A2)'] - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(false) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - }) - - it('should work with start of a range', () => { - const engine = HyperFormula.buildFromArray([ - ['=A1', 2, '=ISFORMULA(A1:A2)', '=ISFORMULA(B1:B2)'] - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(true) - expect(engine.getCellValue(adr('D1'))).toEqual(false) - }) - - it('should propagate error', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISFORMULA(1/0)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should return NA otherwise', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISFORMULA()', '=ISFORMULA(A1, A2)', '=ISFORMULA("foo")', '=ISFORMULA(42)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.CellRefExpected)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.CellRefExpected)) - }) - - it('should work for itself', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISFORMULA(A1)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(true) - }) - - it('should collect dependencies of inner function and return argument type error', () => { - const engine = HyperFormula.buildFromArray([ - ['=SIN(1)'], - ['=ISFORMULA(SUM(A1,A3))'], - ['=SIN(1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.CellRefExpected)) - }) - - it('should propagate error of inner function', () => { - const engine = HyperFormula.buildFromArray([ - ['=1/0'], - ['=ISFORMULA(SUM(A1, A3))'], - ['=1/0'] - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should return #CYCLE! when cyclic reference occurs not directly in COLUMN', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISFORMULA(SUM(A1))'], - ['=ISFORMULA(A1+A2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.CYCLE)) - }) -}) diff --git a/test/unit/interpreter/function-islogical.spec.ts b/test/unit/interpreter/function-islogical.spec.ts deleted file mode 100644 index c7e43aa8cb..0000000000 --- a/test/unit/interpreter/function-islogical.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ISLOGICAL', () => { - it('should return true for boolean', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISLOGICAL(1<1)', '=ISLOGICAL(ISLOGICAL(A1))', '=ISLOGICAL(A2)'], - [false], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(true) - expect(engine.getCellValue(adr('B1'))).toEqual(true) - expect(engine.getCellValue(adr('C1'))).toEqual(true) - }) - - it('should return false for non-boolean', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISLOGICAL(-0)', '=ISLOGICAL(A2)', '=ISLOGICAL("foo")'], - [null], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(false) - expect(engine.getCellValue(adr('B1'))).toEqual(false) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - }) - - it('takes exactly one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISLOGICAL(1, 2)', '=ISLOGICAL()'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/1'], - ['=4/0'], - ['=4/2'], - ['=ISLOGICAL(A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-isna.spec.ts b/test/unit/interpreter/function-isna.spec.ts deleted file mode 100644 index c83ea192b1..0000000000 --- a/test/unit/interpreter/function-isna.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ISNA', () => { - it('should return true for #NA! error', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRUE(1)', '=ISNA(A1)', '=ISNA(TRUE(1))'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(true) - expect(engine.getCellValue(adr('C1'))).toEqual(true) - }) - - it('should return false for other values', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISNA(1)', '=ISNA(TRUE())', '=ISNA("foo")', '=ISNA(A1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(false) - expect(engine.getCellValue(adr('B1'))).toEqual(false) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - expect(engine.getCellValue(adr('D1'))).toEqual(false) - }) - - it('takes exactly one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISNA(1, 2)', '=ISNA()'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRUE(1)'], - ['=TRUE(1)'], - ['=ISNA(A1:A2)'], - ]) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-isnontext.spec.ts b/test/unit/interpreter/function-isnontext.spec.ts deleted file mode 100644 index 996e2bd19f..0000000000 --- a/test/unit/interpreter/function-isnontext.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ISNONTEXT', () => { - it('should return false for text', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISNONTEXT("abcd")', '=ISNONTEXT(A2)'], - ['abcd'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(false) - expect(engine.getCellValue(adr('B1'))).toEqual(false) - }) - - it('should return true for nontext', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISNONTEXT(-0)', '=ISNONTEXT(A2)', '=ISNONTEXT(1<1)'], - [null], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(true) - expect(engine.getCellValue(adr('B1'))).toEqual(true) - expect(engine.getCellValue(adr('C1'))).toEqual(true) - }) - - it('takes exactly one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISNONTEXT(1, 2)', '=ISNONTEXT()'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/1'], - ['=4/0'], - ['=4/2'], - ['=ISNONTEXT(A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-isnumber.spec.ts b/test/unit/interpreter/function-isnumber.spec.ts deleted file mode 100644 index 763c2aafec..0000000000 --- a/test/unit/interpreter/function-isnumber.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ISNUMBER', () => { - it('should return true for numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISNUMBER(1)', '=ISNUMBER(-0)', '=ISNUMBER(1+1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(true) - expect(engine.getCellValue(adr('B1'))).toEqual(true) - expect(engine.getCellValue(adr('C1'))).toEqual(true) - }) - - it('should return false for nonnumbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISNUMBER(1<1)', '=ISNUMBER(A2)', '=ISNUMBER("foo")'], - [null], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(false) - expect(engine.getCellValue(adr('B1'))).toEqual(false) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - }) - - it('takes exactly one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISNUMBER(1, 2)', '=ISNUMBER()'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) -}) diff --git a/test/unit/interpreter/function-isodd.spec.ts b/test/unit/interpreter/function-isodd.spec.ts deleted file mode 100644 index c33872cce4..0000000000 --- a/test/unit/interpreter/function-isodd.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ISODD', () => { - it('number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISODD()', '=ISODD(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISODD(1)', '=ISODD(2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - }) - - it('use coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISODD("42")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - }) - - it('propagates error', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/0'], - ['=ISODD(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-isoweeknum.spec.ts b/test/unit/interpreter/function-isoweeknum.spec.ts deleted file mode 100644 index 12b2baea71..0000000000 --- a/test/unit/interpreter/function-isoweeknum.spec.ts +++ /dev/null @@ -1,98 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ISOWEEKNUM', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISOWEEKNUM(1, 2)'], - ['=ISOWEEKNUM()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for wrong type of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISOWEEKNUM("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should not work for wrong value of args', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISOWEEKNUM(-1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('should work for strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISOWEEKNUM("02/08/2020")'], - ['=ISOWEEKNUM("02/08/2017")'], - ['=ISOWEEKNUM("01/01/2020")'], - ['=ISOWEEKNUM("01/01/2017")'], - ['=ISOWEEKNUM("01/01/2016")'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(31) - expect(engine.getCellValue(adr('A2'))).toEqual(31) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(52) - expect(engine.getCellValue(adr('A5'))).toEqual(53) - }) - - it('should work for numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISOWEEKNUM(0)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(52) - }) - - it('should work for strings with different nullDate', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISOWEEKNUM("02/08/2020")'], - ['=ISOWEEKNUM("02/08/2017")'], - ['=ISOWEEKNUM("01/01/2020")'], - ['=ISOWEEKNUM("01/01/2017")'], - ['=ISOWEEKNUM("01/01/2016")'], - ], {nullDate: {day: 20, month: 10, year: 1920}}) - expect(engine.getCellValue(adr('A1'))).toEqual(31) - expect(engine.getCellValue(adr('A2'))).toEqual(31) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(52) - expect(engine.getCellValue(adr('A5'))).toEqual(53) - }) - - it('should work for strings with compatibility mode', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISOWEEKNUM("02/08/2020")'], - ['=ISOWEEKNUM("02/08/2017")'], - ['=ISOWEEKNUM("01/01/2020")'], - ['=ISOWEEKNUM("01/01/2017")'], - ['=ISOWEEKNUM("01/01/2016")'], - ], {leapYear1900: true}) - expect(engine.getCellValue(adr('A1'))).toEqual(31) - expect(engine.getCellValue(adr('A2'))).toEqual(31) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(52) - expect(engine.getCellValue(adr('A5'))).toEqual(53) - }) - it('should work for strings with compatibility mode and different nullDate', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISOWEEKNUM("02/08/2020")'], - ['=ISOWEEKNUM("02/08/2017")'], - ['=ISOWEEKNUM("01/01/2020")'], - ['=ISOWEEKNUM("01/01/2017")'], - ['=ISOWEEKNUM("01/01/2016")'], - ], {leapYear1900: true, nullDate: {day: 20, month: 10, year: 1920}}) - expect(engine.getCellValue(adr('A1'))).toEqual(31) - expect(engine.getCellValue(adr('A2'))).toEqual(31) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(52) - expect(engine.getCellValue(adr('A5'))).toEqual(53) - }) -}) diff --git a/test/unit/interpreter/function-ispmt.spec.ts b/test/unit/interpreter/function-ispmt.spec.ts deleted file mode 100644 index 977f096d90..0000000000 --- a/test/unit/interpreter/function-ispmt.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ISPMT', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISPMT(1, 1, 1)', '=ISPMT(1, 1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISPMT(1, 1, 10, 1)', '=ISPMT(1, 1, 0, 1)', '=ISPMT(1, -1, 1, 1)', '=ISPMT(-1, -1, 1, -1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(-0.9) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('C1'))).toEqual(-2) - expect(engine.getCellValue(adr('D1'))).toEqual(-2) - }) -}) diff --git a/test/unit/interpreter/function-isref.spec.ts b/test/unit/interpreter/function-isref.spec.ts deleted file mode 100644 index 37a6532faa..0000000000 --- a/test/unit/interpreter/function-isref.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ISREF', () => { - it('should return true for #REF!', () => { - const engine = HyperFormula.buildFromArray([ - ['=#REF!', '=ISREF(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(true) - }) - - it('should return true for #CYCLE!', () => { - const engine = HyperFormula.buildFromArray([ - ['=A1', '=ISREF(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(true) - }) - - it('should return false for other values', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISREF(1)', '=ISREF(TRUE())', '=ISREF("foo")', '=ISREF(A1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(false) - expect(engine.getCellValue(adr('B1'))).toEqual(false) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - expect(engine.getCellValue(adr('D1'))).toEqual(false) - }) - - it('takes exactly one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISREF(1, 2)', '=ISREF()'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - // Inconsistency with Product 1 - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['=A1'], - ['=A2'], - [], - ['=ISREF(A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - // Inconsistency with Product 1 - it('returns #CYCLE! for itself', () => { - /* TODO can we handle such case correctly? */ - const engine = HyperFormula.buildFromArray([ - ['=ISREF(A1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.CYCLE)) - }) -}) diff --git a/test/unit/interpreter/function-istext.spec.ts b/test/unit/interpreter/function-istext.spec.ts deleted file mode 100644 index a0682e4682..0000000000 --- a/test/unit/interpreter/function-istext.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ISTEXT', () => { - it('should return true for text', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISTEXT("abcd")', '=ISTEXT(A2)'], - ['abcd'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(true) - expect(engine.getCellValue(adr('B1'))).toEqual(true) - }) - - it('should return false for nontext', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISTEXT(-0)', '=ISTEXT(A2)', '=ISTEXT(1<1)'], - [null], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(false) - expect(engine.getCellValue(adr('B1'))).toEqual(false) - expect(engine.getCellValue(adr('C1'))).toEqual(false) - }) - - it('takes exactly one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ISTEXT(1, 2)', '=ISTEXT()'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/1'], - ['=4/0'], - ['=4/2'], - ['=ISTEXT(A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-large.spec.ts b/test/unit/interpreter/function-large.spec.ts deleted file mode 100644 index 1372469f98..0000000000 --- a/test/unit/interpreter/function-large.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function LARGE', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=LARGE(1)'], - ['=LARGE(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=LARGE(1, "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=LARGE(A2:D2, 0)', '=LARGE(A2:D2, 1)', '=LARGE(A2:D2, 2)', '=LARGE(A2:D2, 3)', '=LARGE(A2:D2, 4)', '=LARGE(A2:D2, 5)'], - [1, 4, 2, 4], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('B1'))).toEqual(4) - expect(engine.getCellValue(adr('C1'))).toEqual(4) - expect(engine.getCellValue(adr('D1'))).toEqual(2) - expect(engine.getCellValue(adr('E1'))).toEqual(1) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('should ignore non-numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=LARGE(A2:D2, 0)', '=LARGE(A2:D2, 1)', '=LARGE(A2:D2, 2)', '=LARGE(A2:D2, 3)'], - [1, 4, true, 'abcd'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('B1'))).toEqual(4) - expect(engine.getCellValue(adr('C1'))).toEqual(1) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=LARGE(A2:D2, 0)', '=LARGE(A2:D2, 1)', '=LARGE(A2:D2, 2)', '=LARGE(A2:D2, 3)'], - [1, 4, '=NA()', 'abcd'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('should truncate second arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=LARGE(A2:D2, 0.9)', '=LARGE(A2:D2, 1.9)', '=LARGE(A2:D2, 2.9)', '=LARGE(A2:D2, 3.9)', '=LARGE(A2:D2, 4.9)', '=LARGE(A2:D2, 5.9)'], - [1, 4, 2, 4], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('B1'))).toEqual(4) - expect(engine.getCellValue(adr('C1'))).toEqual(4) - expect(engine.getCellValue(adr('D1'))).toEqual(2) - expect(engine.getCellValue(adr('E1'))).toEqual(1) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('should work for non-ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['=LARGE(1,0)', '=LARGE(1,1)', '=LARGE(1,2)', '=LARGE(TRUE(),1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - //inconsistency with product #2 - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-lcm.spec.ts b/test/unit/interpreter/function-lcm.spec.ts deleted file mode 100644 index b479dedad4..0000000000 --- a/test/unit/interpreter/function-lcm.spec.ts +++ /dev/null @@ -1,117 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function LCM', () => { - it('checks required number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=LCM()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('computes correct answer for two args', () => { - const engine = HyperFormula.buildFromArray([ - ['=LCM(2*3*5, 3*5*7)', '=LCM(0, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(2 * 3 * 5 * 7) - expect(engine.getCellValue(adr('B1'))).toBe(0) - }) - - it('computes correct answer for more than two args', () => { - const engine = HyperFormula.buildFromArray([ - ['=LCM(2*3*5, 3*5*7, 2*5*7)', '=LCM(100, 101, 102, 103, 104)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(2 * 3 * 5 * 7) - expect(engine.getCellValue(adr('B1'))).toBe(1379437800) - }) - - it('works with zeroes', () => { - const engine = HyperFormula.buildFromArray([ - ['=LCM(2*3*5, 3*5*7, 2*5*7, 0, 0, 0)', '=LCM(0, 0, 100, 101, 102, 103, 104, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - expect(engine.getCellValue(adr('B1'))).toBe(0) - }) - - it('accepts single arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=LCM(1)', '=LCM(0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('B1'))).toBe(0) - }) - - it('handles overflow', () => { - const engine = HyperFormula.buildFromArray([ - ['=LCM(1000000, 1000001, 1000002, 1000003)'], - ]) - - //inconsistency with product #1 - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('coerces to number', () => { - const engine = HyperFormula.buildFromArray([ - ['=LCM("4",2)'], - ['=LCM(B2:C2)', '\'4', 2], - ['=LCM(FALSE(),4)'], - ['=LCM(B4:C4)', false, 4], - ['=LCM(,4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(4) - expect(engine.getCellValue(adr('A2'))).toBe(4) - expect(engine.getCellValue(adr('A3'))).toBe(0) - expect(engine.getCellValue(adr('A4'))).toBe(0) - expect(engine.getCellValue(adr('A5'))).toBe(0) - }) - - it('ignores non-coercible values', () => { - const engine = HyperFormula.buildFromArray([ - ['=LCM(B1:C1)', 'abcd', 4], - ['=LCM(B2:C2)', null, 4], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(4) - expect(engine.getCellValue(adr('A2'))).toBe(4) - }) - - it('throws error for non-coercible values', () => { - const engine = HyperFormula.buildFromArray([ - ['=LCM("abcd",4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=LCM(-1, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('truncates numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=LCM(B1:C1)', 5.5, 10], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(10) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=LCM(NA(),4)'], - ['=LCM(B2:C2)', '=NA()', 4], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-left.spec.ts b/test/unit/interpreter/function-left.spec.ts deleted file mode 100644 index 42d5cb9cce..0000000000 --- a/test/unit/interpreter/function-left.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function LEFT', () => { - it('should return N/A when number of arguments is incorrect', () => { - const engine = HyperFormula.buildFromArray([ - ['=LEFT()'], - ['=LEFT("foo", 1, 2)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return VALUE when wrong type of second parameter', () => { - const engine = HyperFormula.buildFromArray([ - ['=LEFT("foo", "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work with empty argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=LEFT(, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('') - }) - - it('should return one character by default', () => { - const engine = HyperFormula.buildFromArray([ - ['=LEFT("bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('b') - }) - - it('should return VALUE when second parameter is less than 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=LEFT("foo", -1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NegativeLength)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=LEFT("", 4)'], - ['=LEFT("bar", 0)'], - ['=LEFT("bar", 1)'], - ['=LEFT("bar", 3)'], - ['=LEFT("bar", 4)'], - ['=LEFT(123, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('') - expect(engine.getCellValue(adr('A2'))).toEqual('') - expect(engine.getCellValue(adr('A3'))).toEqual('b') - expect(engine.getCellValue(adr('A4'))).toEqual('bar') - expect(engine.getCellValue(adr('A5'))).toEqual('bar') - expect(engine.getCellValue(adr('A6'))).toEqual('12') - }) - - it('should coerce other types to string', () => { - const engine = HyperFormula.buildFromArray([ - ['=LEFT(10, 1)'], - ['=LEFT(5+5, 1)'], - ['=LEFT(TRUE(), 1)'], - ['=LEFT("010", 1)'], - ['=LEFT(B5, 1)', "'010"], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - expect(engine.getCellValue(adr('A2'))).toEqual('1') - expect(engine.getCellValue(adr('A3'))).toEqual('T') - expect(engine.getCellValue(adr('A4'))).toEqual('0') - expect(engine.getCellValue(adr('A5'))).toEqual('0') - }) -}) diff --git a/test/unit/interpreter/function-len.spec.ts b/test/unit/interpreter/function-len.spec.ts deleted file mode 100644 index dd66ba5e78..0000000000 --- a/test/unit/interpreter/function-len.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function LEN', () => { - it('should return N/A when number of arguments is incorrect', () => { - const engine = HyperFormula.buildFromArray([ - ['=LEN()'], - ['=LEN("foo", "bar")'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=LEN("foo")'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('should coerce other types to string', () => { - const engine = HyperFormula.buildFromArray([ - ['=LEN(1)'], - ['=LEN(5+5)'], - ['=LEN(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - expect(engine.getCellValue(adr('A3'))).toEqual(4) - }) -}) diff --git a/test/unit/interpreter/function-ln.spec.ts b/test/unit/interpreter/function-ln.spec.ts deleted file mode 100644 index c8221f67ef..0000000000 --- a/test/unit/interpreter/function-ln.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function LN', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=LN(2.718281828459045)']]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=LN("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('for zero', () => { - const engine = HyperFormula.buildFromArray([['=LN(0)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('for negative arguments', () => { - const engine = HyperFormula.buildFromArray([['=LN(-42)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=LN()', '=LN(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="2.718281828459045"', '=LN(A1)'], - ['', '=LN(A2)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBe(1) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=LN(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-log.spec.ts b/test/unit/interpreter/function-log.spec.ts deleted file mode 100644 index 460dcdecb2..0000000000 --- a/test/unit/interpreter/function-log.spec.ts +++ /dev/null @@ -1,89 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function LOG', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=LOG(4, 2)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(2) - }) - - it('logarithmic base has default', () => { - const engine = HyperFormula.buildFromArray([['=LOG(10)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOG("foo", 42)'], - ['=LOG(42, "foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('for zero', () => { - const engine = HyperFormula.buildFromArray([['=LOG(0, 42)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('for negative value', () => { - const engine = HyperFormula.buildFromArray([['=LOG(-42, 42)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('for zero base', () => { - const engine = HyperFormula.buildFromArray([['=LOG(42, 0)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('for 1 base', () => { - const engine = HyperFormula.buildFromArray([['=LOG(42, 1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('for negative base', () => { - const engine = HyperFormula.buildFromArray([['=LOG(42, -42)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=LOG()', '=LOG(42, 42, 42)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="10"', '=LOG(A1, 10)', '=LOG(10, A1)'], - ['', '=LOG(A2, 42)', '=LOG(42, 0)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBe(1) - expect(engine.getCellValue(adr('C1'))).toBe(1) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('C2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOG(4/0, 42)'], - ['=LOG(42, 4/0)'], - ['=LOG(4/0, FOOBAR())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-log10.spec.ts b/test/unit/interpreter/function-log10.spec.ts deleted file mode 100644 index f5efebb7cc..0000000000 --- a/test/unit/interpreter/function-log10.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function LOG10', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=LOG10(10)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=LOG10("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('for zero', () => { - const engine = HyperFormula.buildFromArray([['=LOG10(0)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('for negative arguments', () => { - const engine = HyperFormula.buildFromArray([['=LOG10(-42)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=LOG10()', '=LOG10(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="10"', '=LOG10(A1)'], - ['', '=LOG10(A2)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBe(1) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOG10(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-lognorm.dist.spec.ts b/test/unit/interpreter/function-lognorm.dist.spec.ts deleted file mode 100644 index 0de52621ff..0000000000 --- a/test/unit/interpreter/function-lognorm.dist.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function LOGNORM.DIST', () => { - //in product #1, this function takes 3 arguments - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOGNORM.DIST(1, 2, 3)'], - ['=LOGNORM.DIST(1, 2, 3, 4, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - //in product #1, this function takes 3 arguments - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOGNORM.DIST("foo", 2, 3, TRUE())'], - ['=LOGNORM.DIST(1, "baz", 3, TRUE())'], - ['=LOGNORM.DIST(1, 2, "baz", TRUE())'], - ['=LOGNORM.DIST(1, 2, 3, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - //in product #1, this function takes 3 arguments - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOGNORM.DIST(0.1, 1, 2, TRUE())'], - ['=LOGNORM.DIST(0.5, 2, 4, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0493394267528022, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.250382425968177, 6) - }) - - //in product #1, this function takes 3 arguments - it('should work as pdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOGNORM.DIST(0.1, 1, 2, FALSE())'], - ['=LOGNORM.DIST(0.5, 2, 4, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.510234855730895, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.159017142514074, 6) - }) - - //in product #1, this function takes 3 arguments - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOGNORM.DIST(0.01, 0, 0.01, FALSE())'], - ['=LOGNORM.DIST(0, 0, 0.01, FALSE())'], - ['=LOGNORM.DIST(0.01, 0, 0, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-lognorm.inv.spec.ts b/test/unit/interpreter/function-lognorm.inv.spec.ts deleted file mode 100644 index 8fa25e1e14..0000000000 --- a/test/unit/interpreter/function-lognorm.inv.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function LOGNORM.INV', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOGNORM.INV(1, 2)'], - ['=LOGNORM.INV(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOGNORM.INV("foo", 2, 3)'], - ['=LOGNORM.INV(0.5, "baz", 3)'], - ['=LOGNORM.INV(0.5, 2, "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOGNORM.INV(0.1, 1, 2)'], - ['=LOGNORM.INV(0.5, 2, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.209485002124057, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(7.38905609893065, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOGNORM.INV(0.01, 0, 0.01)'], - ['=LOGNORM.INV(0, 0, 0.01)'], - ['=LOGNORM.INV(0.01, 0, 0)'], - ['=LOGNORM.INV(0.99, 0, 0.01)'], - ['=LOGNORM.INV(1, 0, 0.01)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.977005029803317, 6) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(1.0235361840474, 6) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-lower.spec.ts b/test/unit/interpreter/function-lower.spec.ts deleted file mode 100644 index e8c7825ca3..0000000000 --- a/test/unit/interpreter/function-lower.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function LOWER', () => { - it('should take one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOWER()'], - ['=LOWER("foo", "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should convert text to lowercase', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOWER("")'], - ['=LOWER(B1)'], - ['=LOWER("foo")'], - ['=LOWER("FOO")'], - ['=LOWER("BaR")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('') - expect(engine.getCellValue(adr('A2'))).toEqual('') - expect(engine.getCellValue(adr('A3'))).toEqual('foo') - expect(engine.getCellValue(adr('A4'))).toEqual('foo') - expect(engine.getCellValue(adr('A5'))).toEqual('bar') - }) - - it('should coerce', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOWER(TRUE())'], - ['=LOWER(0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('true') - expect(engine.getCellValue(adr('A2'))).toEqual('0') - }) -}) diff --git a/test/unit/interpreter/function-match.spec.ts b/test/unit/interpreter/function-match.spec.ts deleted file mode 100644 index ab398f6fba..0000000000 --- a/test/unit/interpreter/function-match.spec.ts +++ /dev/null @@ -1,758 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {DependencyGraph} from '../../../src/DependencyGraph' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, resetSpy} from '../testUtils' - -describe('Function MATCH', () => { - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(1)'], - ['=MATCH(1, B1:B3, 0, 42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('validates number of arguments when array arithmetics is on', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(1)'], - ['=MATCH(1, B1:B3, 0, 42)'], - ], { useArrayArithmetic: true }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('validates that 1st argument is number, string or boolean', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(C2:C3, B1:B1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('2nd argument can be a scalar', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(42, 42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('validates that 3rd argument is number', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(0, B1:B1, "a")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('validates that 3rd argument is in [-1, 0, 1]', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(0, B1:B1, -2)'], - ['=MATCH(0, B1:B1, 0.5)'], - ['=MATCH(0, B1:B1, 100)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.BadMode)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.BadMode)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.BadMode)) - }) - - it('should propagate errors properly', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(1/0, B1:B1)'], - ['=MATCH(1, B1:B1, 1/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('when MatchType is empty defaults to 1 (returns lower bound)', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(113, A2:A5)'], - ['100'], - ['110'], - ['120'], - ['130'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - describe('when MatchType = 0', () => { - describe('when search range is vertical', () => { - it('works when the result is in the first cell', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(103, A2:A5, 0)'], - ['103'], - ['200'], - ['200'], - ['200'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('works when the result is in the last cell', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(103, A2:A5, 0)'], - ['200'], - ['200'], - ['200'], - ['103'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('returns the relative position in the range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(102, A6:A9, 0)'], - [''], - [''], - [''], - [''], - ['100'], - ['101'], - ['102'], - ['103'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('returns the first matching result', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(103, A2:A5, 0)'], - ['200'], - ['103'], - ['103'], - ['200'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('doesn\'t return result from outside the search range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(103, A3:A6, 0)'], - ['103', '103'], - ['200', '103'], - ['200', '103'], - ['200', '103'], - ['200', '103'], - ['103', '103'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - }) - - describe('when search range is horizontal', () => { - it('works when the result is in first cell', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(103, A2:D2, 0)'], - ['103', '200', '200', '200'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('works when the result is in the last cell', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(103, A2:D2, 0)'], - ['200', '200', '200', '103'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('returns the relative position in the range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(102, E2:H2, 0)'], - ['', '', '', '', '100', '101', '102', '103'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('returns the first matching result', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(103, A2:D2, 0)'], - ['200', '103', '103', '200'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('doesn\'t return result from outside the search range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(103, B2:E2, 0)'], - ['103', '200', '200', '200', '200', '103'], - ['103', '103', '103', '103', '103', '103'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - }) - - it('uses indexOf', () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const spy = spyOn(DependencyGraph.prototype as any, 'computeListOfValuesInRange') - resetSpy(spy) - - const engine = HyperFormula.buildFromArray([ - ['=MATCH(400, A2:A5, 0)'], - ['100'], - ['200'], - ['300'], - ['400'], - ['500'], - ]) - - expect(spy).toHaveBeenCalled() - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('works for strings, is not case sensitive', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH("A", A2:A5, 0)'], - ['a'], - ['A'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('works for strings, is not case sensitive even if config defines case sensitivity', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH("A", A2:A5, 0)'], - ['a'], - ['A'], - ], { caseSensitive: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('works with dates', () => { - const engine = HyperFormula.buildFromArray( - [ - ['01/04/2012', '01/01/2012', '=MATCH(B1, A1:A4, 0)'], - ['01/01/2012', '01/02/2012', '=MATCH(B2, A1:A4, 0)'], - ['01/02/2012'], - ['01/03/2012'], - ] - ) - expect(engine.getCellValue(adr('C1'))).toEqual(2) - expect(engine.getCellValue(adr('C2'))).toEqual(3) - }) - }) - - describe('when MatchType = 1', () => { - describe('when search range is vertical', () => { - it('returns the lower bound if the range is sorted ascending', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(113, A2:A5, 1)'], - ['100'], - ['110'], - ['120'], - ['130'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('returns the exact match if present in the range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(120, A2:A5, 1)'], - ['100'], - ['110'], - ['120'], - ['130'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('returns the last match if there are duplicates in the search range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(110, A2:A5, 1)'], - ['110'], - ['110'], - ['110'], - ['110'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('returns the last value if all are smaller than the search value', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(1000, A2:A5, 1)'], - ['100'], - ['110'], - ['120'], - ['130'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('returns an error if all are greater than the search value', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(113, A2:A5, 1)'], - ['200'], - ['210'], - ['220'], - ['230'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('returns the relative position in the range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(113, A6:A9, 1)'], - [''], - [''], - [''], - [''], - ['100'], - ['110'], - ['120'], - ['130'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('doesn\'t return result from outside the search range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(103, A3:A6, 1)'], - ['103', '103'], - ['200', '103'], - ['200', '103'], - ['200', '103'], - ['200', '103'], - ['103', '103'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - }) - - describe('when search range is horizontal', () => { - it('returns the lower bound if the range is sorted ascending', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(113, A2:D2, 1)'], - ['100', '110', '120', '130'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('returns the exact match if present in the range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(120, A2:D2, 1)'], - ['100', '110', '120', '130'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('returns the last match if there are duplicates in the search range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(110, A2:D2, 1)'], - ['110', '110', '110', '110'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('returns the last value if all are smaller than the search value', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(1000, A2:D2, 1)'], - ['100', '110', '120', '130'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('returns an error if all are greater than the search value', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(1, A2:D2, 1)'], - ['100', '110', '120', '130'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('returns the relative position in the range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(112, E2:H2, 1)'], - ['', '', '', '', '100', '110', '120', '130'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('doesn\'t return result from outside the search range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(103, B2:E2, 1)'], - ['103', '200', '200', '200', '200', '103'], - ['103', '103', '103', '103', '103', '103'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - }) - - it('works for strings, is not case sensitive', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH("C", A2:A5, 1)'], - ['a'], - ['b'], - ['d'], - ['e'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('works for strings, is not case sensitive even if config defines case sensitivity', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH("C", A2:A5, 1)'], - ['a'], - ['b'], - ['d'], - ['e'], - ], { caseSensitive: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('works with dates', () => { - const engine = HyperFormula.buildFromArray( - [ - ['01/01/2012', '01/02/2012', '=MATCH(B1, A1:A4, 1)'], - ['01/02/2012', '10/02/2012', '=MATCH(B2, A1:A4, 1)'], - ['01/03/2012'], - ['01/04/2012'], - ] - ) - expect(engine.getCellValue(adr('C1'))).toEqual(2) - expect(engine.getCellValue(adr('C2'))).toEqual(2) - }) - - it('uses binary search', () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const spy = spyOn(DependencyGraph.prototype as any, 'computeListOfValuesInRange') - resetSpy(spy) - - const engine = HyperFormula.buildFromArray([ - ['=MATCH(113, A2:A5, 1)'], - ['100'], - ['110'], - ['120'], - ['130'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(spy).not.toHaveBeenCalled() - }) - }) - - describe('when MatchType = -1', () => { - describe('when search range is vertical', () => { - it('returns the upper bound if the range is sorted descending', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(113, A2:A5, -1)'], - ['130'], - ['120'], - ['110'], - ['100'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('returns the exact match if present in the range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(120, A2:A5, -1)'], - ['130'], - ['120'], - ['110'], - ['100'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('returns the last match if there are duplicates in the search range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(110, A2:A5, -1)'], - ['110'], - ['110'], - ['110'], - ['110'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('returns the last value if all are greater than the search value', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(1, A2:A5, -1)'], - ['130'], - ['120'], - ['110'], - ['100'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('returns an error if all are smaller than the search value', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(1000, A2:A5, -1)'], - ['130'], - ['120'], - ['110'], - ['100'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('returns the relative position in the range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(113, A6:A9, -1)'], - [''], - [''], - [''], - [''], - ['130'], - ['120'], - ['110'], - ['100'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('doesn\'t return result from outside the search range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(103, A3:A6, -1)'], - ['103', '103'], - ['100', '103'], - ['100', '103'], - ['100', '103'], - ['100', '103'], - ['103', '103'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - }) - - describe('when search range is horizontal', () => { - it('returns the lower bound if the range is sorted ascending', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(113, A2:D2, -1)'], - ['130', '120', '110', '100'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('returns the exact match if present in the range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(120, A2:D2, -1)'], - ['130', '120', '110', '100'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('returns the last match if there are duplicates in the search range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(110, A2:D2, -1)'], - ['110', '110', '110', '110'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('returns the last value if all are greater than the search value', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(1, A2:D2, -1)'], - ['130', '120', '110', '100'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('returns an error if all are smaller than the search value', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(1000, A2:D2, -1)'], - ['130', '120', '110', '100'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('returns the relative position in the range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(112, E2:H2, -1)'], - ['', '', '', '', '130', '120', '110', '100'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('doesn\'t return result from outside the search range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(103, B2:E2, -1)'], - ['103', '100', '100', '100', '100', '103'], - ['103', '103', '103', '103', '103', '103'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - }) - - it('works for strings, is not case sensitive', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH("C", A2:A5, -1)'], - ['e'], - ['d'], - ['b'], - ['a'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('works for strings, is not case sensitive even if config defines case sensitivity', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH("C", A2:A5, -1)'], - ['e'], - ['d'], - ['b'], - ['a'], - ], { caseSensitive: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('works with dates', () => { - const engine = HyperFormula.buildFromArray( - [ - ['01/04/2012', '01/02/2012', '=MATCH(B1, A1:A4, -1)'], - ['01/03/2012', '10/02/2012', '=MATCH(B2, A1:A4, -1)'], - ['01/02/2012'], - ['01/01/2012'], - ] - ) - expect(engine.getCellValue(adr('C1'))).toEqual(3) - expect(engine.getCellValue(adr('C2'))).toEqual(2) - }) - - it('uses binary search', () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const spy = spyOn(DependencyGraph.prototype as any, 'computeListOfValuesInRange') - resetSpy(spy) - - const engine = HyperFormula.buildFromArray([ - ['=MATCH(113, A2:A5, -1)'], - ['130'], - ['120'], - ['110'], - ['100'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(spy).not.toHaveBeenCalled() - }) - }) - - it('should coerce empty arg to 0', () => { - const engine = HyperFormula.buildFromArray([ - ['-5'], - ['-2'], - ['0'], - ['2'], - ['5'], - ['=MATCH(0, A1:A5)'], - ['=MATCH(, A1:A5)'], - ]) - - expect(engine.getCellValue(adr('A6'))).toEqual(3) - expect(engine.getCellValue(adr('A7'))).toEqual(3) - }) - - it('should coerce empty arg to zero when useColumnIndex = false', () => { - const engine = HyperFormula.buildFromArray([ - ['=MATCH(, A2:A4, 0)'], - [1], - [3], - [0], - ], {useColumnIndex: false}) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('should return NA when range is not a single column or row', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '1'], - ['2', '3'], - ['=MATCH(0, A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA)) - }) - - describe('works with a range reference to an empty sheet', () => { - it('- cell range', () => { - const hf = HyperFormula.buildFromSheets({ - table1: [], - table2: [['=MATCH(0, table1!A1:A10)']], - }) - - expect(hf.getCellValue(adr('A1', 1))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('- column range', () => { - const hf = HyperFormula.buildFromSheets({ - table1: [[]], - table2: [['=MATCH(0, table1!A:A)']], - }) - - expect(hf.getCellValue(adr('A1', 1))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('- row range', () => { - const hf = HyperFormula.buildFromSheets({ - table1: [], - table2: [['=MATCH(0, table1!1:1)']], - }) - - expect(hf.getCellValue(adr('A1', 1))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('- column range (called from calculateFormula)', () => { - const hf = HyperFormula.buildFromArray([]) - - expect(hf.calculateFormula('=MATCH(0, Sheet1!A:A)', 0)).toEqualError(detailedError(ErrorType.NA)) - }) - - it('- column range (called from calculateFormula without explicit sheet reference)', () => { - const hf = HyperFormula.buildFromArray([]) - - expect(hf.calculateFormula('=MATCH(0, A:A)', 0)).toEqualError(detailedError(ErrorType.NA)) - }) - }) -}) diff --git a/test/unit/interpreter/function-max.spec.ts b/test/unit/interpreter/function-max.spec.ts deleted file mode 100644 index 06b7e938d4..0000000000 --- a/test/unit/interpreter/function-max.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('MAX', () => { - it('MAX with empty args', () => { - const engine = HyperFormula.buildFromArray([['=MAX()']]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('MAX with args', () => { - const engine = HyperFormula.buildFromArray([['=MAX(1, B1)', '3.14']]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(3.14) - }) - - it('MAX with range', () => { - const engine = HyperFormula.buildFromArray([['1'], ['3'], ['2'], ['=MAX(A1:A3)']]) - expect(engine.getCellValue(adr('A4'))).toEqual(3) - }) - - it('MAX with mixed arguments', () => { - const engine = HyperFormula.buildFromArray([['1'], ['3'], ['2'], ['=MAX(4,A1:A3)']]) - expect(engine.getCellValue(adr('A4'))).toEqual(4) - }) - - it('doesnt do coercions', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['foo'], - ['=TRUE()'], - ['=CONCATENATE("1","0")'], - ['=MAX(A1:A5)'], - ]) - - expect(engine.getCellValue(adr('A6'))).toEqual(2) - }) - - it('MAX of strings and -1', () => { - const engine = HyperFormula.buildFromArray([['foo'], ['bar'], ['-1'], ['=MAX(A1:A3)']]) - expect(engine.getCellValue(adr('A4'))).toEqual(-1) - }) - - it('MAX of empty value', () => { - const engine = HyperFormula.buildFromArray([['', '=MAX(A1)']]) - expect(engine.getCellValue(adr('B1'))).toEqual(0) - }) - - it('MAX of empty value and some negative number', () => { - const engine = HyperFormula.buildFromArray([['', '-1', '=MAX(A1,B1)']]) - expect(engine.getCellValue(adr('C1'))).toEqual(-1) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=MAX(MMULT(A1:B2, A1:B2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(22) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=4/0'], - ['=FOOBAR()', '4'], - ['=MAX(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-maxa.spec.ts b/test/unit/interpreter/function-maxa.spec.ts deleted file mode 100644 index b352558590..0000000000 --- a/test/unit/interpreter/function-maxa.spec.ts +++ /dev/null @@ -1,77 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('MAXA', () => { - it('MAXA with empty args', () => { - const engine = HyperFormula.buildFromArray([['=MAXA()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('MAXA with args', () => { - const engine = HyperFormula.buildFromArray([['=MAXA(1, B1)', '3.14']]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(3.14) - }) - - it('MAXA with range', () => { - const engine = HyperFormula.buildFromArray([['1'], ['3'], ['2'], ['=MAXA(A1:A3)']]) - - expect(engine.getCellValue(adr('A4'))).toEqual(3) - }) - - it('does only boolean coercions', () => { - const engine = HyperFormula.buildFromArray([ - ['="42"', '=MAXA(A1)'], - ['=TRUE()', '=MAXA(A2)'], - ['=FALSE()', '=MAXA(A3)'], - ['="TRUE"', '=MAXA(A4)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(0) - expect(engine.getCellValue(adr('B2'))).toEqual(1) - expect(engine.getCellValue(adr('B3'))).toEqual(0) - expect(engine.getCellValue(adr('B4'))).toEqual(0) - }) - - it('MAXA of strings and -1', () => { - const engine = HyperFormula.buildFromArray([['foo'], ['bar'], ['-1'], ['=MAXA(A1:A3)']]) - expect(engine.getCellValue(adr('A4'))).toEqual(0) - }) - - it('MAXA of empty value', () => { - const engine = HyperFormula.buildFromArray([['', '=MAXA(A1)']]) - expect(engine.getCellValue(adr('B1'))).toEqual(0) - }) - - it('MAXA of empty value and some negative number', () => { - const engine = HyperFormula.buildFromArray([ - ['', '-1', '=MAXA(A1,B1)'], - [null, '-1', '=MAXA(A2,B2)'], - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(0) - expect(engine.getCellValue(adr('C2'))).toEqual(-1) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=MAXA(MMULT(A1:B2, A1:B2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(22) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=4/0'], - ['=FOOBAR()', '4'], - ['=MAXA(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-maxifs.spec.ts b/test/unit/interpreter/function-maxifs.spec.ts deleted file mode 100644 index 7543857fac..0000000000 --- a/test/unit/interpreter/function-maxifs.spec.ts +++ /dev/null @@ -1,259 +0,0 @@ -import {HyperFormula, ErrorType} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectArrayWithSameContent} from '../testUtils' -import {DateTimeHelper} from '../../../src/DateTimeHelper' -import {Config} from '../../../src/Config' - -describe('Function MAXIFS - argument validations and combinations', () => { - it('requires odd number of arguments, but at least 3', () => { - const engine = HyperFormula.buildFromArray([['=MAXIFS(C1, ">0")'], ['=MAXIFS(C1, ">0", B1, B1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('error when criterion unparsable', () => { - const engine = HyperFormula.buildFromArray([ - ['=MAXIFS(B1:B2, C1:C2, "> { - const engine = HyperFormula.buildFromArray([ - ['=MAXIFS(B1:C1, B2:D2, ">0")'], - ['=MAXIFS(B1, B2:D2, ">0")'], - ['=MAXIFS(B1:D1, B2, ">0")'], - ['=MAXIFS(B1:D1, B2:D2, ">0", B2:E2, ">0")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - }) - - it('error when different height dimension of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=MAXIFS(B1:B2, C1:C3, ">0")'], - ['=MAXIFS(B1, C1:C2, ">0")'], - ['=MAXIFS(B1:B2, C1, ">0")'], - ['=MAXIFS(B1:B2, C1:C2, ">0", C1:C3, ">0")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - }) - - it('scalars are treated like singular arrays', () => { - const engine = HyperFormula.buildFromArray([['=MAXIFS(42, 10, ">1")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(42) - }) - - it('should return 0 as max from an empty range', () => { - const engine = HyperFormula.buildFromArray([['=MAXIFS(42, -1, ">1")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) - - it('error propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=MAXIFS(4/0, 42, ">1")'], - ['=MAXIFS(0, 4/0, ">1")'], - ['=MAXIFS(0, 42, 4/0)'], - ['=MAXIFS(0, 4/0, FOOBAR())'], - ['=MAXIFS(4/0, FOOBAR(), ">1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('works when arguments are just references', () => { - const engine = HyperFormula.buildFromArray([['2', '3'], ['=MAXIFS(B1, A1, ">1")']]) - - expect(engine.getCellValue(adr('A2'))).toEqual(3) - }) - - it('works with numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['2', 'a'], - ['3', 'a'], - ['=MAXIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(3) - }) - - it('works with percents', () => { - const engine = HyperFormula.buildFromArray([ - ['2%', 'a'], - ['3%', 'a'], - ['=MAXIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(.03) - }) - - it('works with currencies', () => { - const engine = HyperFormula.buildFromArray([ - ['$2', 'a'], - ['$3', 'a'], - ['=MAXIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(3) - }) - - it('works with dates', () => { - const dateHelper = new DateTimeHelper(new Config()) - - const engine = HyperFormula.buildFromArray([ - ['20/01/2020', 'a'], - ['30/01/2020', 'a'], - ['=MAXIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(dateHelper.numberToSimpleDate(engine.getCellValue(adr('A3')) as number)).toEqual({ day: 30, month: 1, year: 2020 }) - }) - - it('strings are ignored', () => { - const engine = HyperFormula.buildFromArray([ - ['-1', 'a'], - ['abc', 'a'], - ['=MAXIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(-1) - }) - - it('empty cells are ignored', () => { - const engine = HyperFormula.buildFromArray([ - ['-1', 'a'], - ['', 'a'], - ['=MAXIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(-1) - }) - - it('booleans are ignored', () => { - const engine = HyperFormula.buildFromArray([ - ['-1', 'a'], - ['=TRUE()', 'a'], - ['=MAXIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(-1) - }) - - it('max of empty cells is zero', () => { - const engine = HyperFormula.buildFromArray([ - ['', 'a'], - ['', 'a'], - ['=MAXIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(0) - }) - - it('max of strings is zero', () => { - const engine = HyperFormula.buildFromArray([ - ['abc', 'a'], - ['cba', 'a'], - ['=MAXIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(0) - }) - - it('max of booleans is zero', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRUE()', 'a'], - ['=FALSE()', 'a'], - ['=MAXIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(0) - }) - - it('works with range values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1', '3', '5'], - ['1', '1', '7', '9'], - ['=MAXIFS(MMULT(C1:D2, C1:D2), MMULT(A1:B2, A1:B2), "=2")'], - ['=MAXIFS(MMULT(C1:D2, C1:D2), A1:B2, "=1")'], - ['=MAXIFS(C1:D2, MMULT(A1:B2, A1:B2), "=2")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(116) - expect(engine.getCellValue(adr('A4'))).toEqual(116) - expect(engine.getCellValue(adr('A5'))).toEqual(9) - }) - - it('works for mixed reference/range arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['2', '3'], - ['=MAXIFS(B1, A1:A1, ">1")'], - ['4', '5'], - ['=MAXIFS(B3:B3, A3, ">1")'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqual(5) - }) - - it('works when criterion arg is an inline array', () => { - const engine = HyperFormula.buildFromArray([[2, 'a'], [3, 'b'], ['=MAXIFS(A1:A2, B1:B2, {"a", "b"})']], { - useArrayArithmetic: true, - }) - - expect(engine.getCellValue(adr('A3'))).toEqual(2) - expect(engine.getCellValue(adr('B3'))).toEqual(3) - }) -}) - -describe('Function MAXIFS - calcultions on more than one criteria', () => { - it('works for more than one criterion/range pair', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '100', '3'], - ['1', '101', '5'], - ['2', '102', '7'], - ['=MAXIFS(C1:C3, A1:A3, ">=1", B1:B3, "<102")'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(5) - }) -}) - -describe('Function MAXIFS - cache recalculation after cruds', () => { - it('recalculates MAXIFS if changes in the first range', () => { - const sheet = [['10', '10'], ['5', '6'], ['7', '8'], ['=MAXIFS(A1:B1, A2:B2, ">=5", A3:B3, ">=7")']] - const engine = HyperFormula.buildFromArray(sheet) - - const changes = engine.setCellContents(adr('A1'), [['1', '3']]) - const newValues = changes.map(c => c.newValue) - - expect(engine.getCellValue(adr('A4'))).toEqual(3) - expectArrayWithSameContent(newValues, [1, 3, 3]) - }) - - it('recalculates MAXIFS if changes in one of the tested range', () => { - const sheet = [['20', '10'], ['5', '6'], ['7', '8'], ['=MAXIFS(A1:B1, A2:B2, ">=5", A3:B3, ">=7")']] - const engine = HyperFormula.buildFromArray(sheet) - expect(engine.getCellValue(adr('A4'))).toEqual(20) - - engine.setCellContents(adr('A3'), [['1', '7']]) - - expect(engine.getCellValue(adr('A4'))).toEqual(10) - }) -}) diff --git a/test/unit/interpreter/function-median.spec.ts b/test/unit/interpreter/function-median.spec.ts deleted file mode 100644 index 51b68bbe1a..0000000000 --- a/test/unit/interpreter/function-median.spec.ts +++ /dev/null @@ -1,108 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function MEDIAN', () => { - it('single number', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('two numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1.5) - }) - - it('more numbers (odd)', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(3, 1, 2, 5, 7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('more numbers (even)', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(3, 4, 1, 2, 5, 7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3.5) - }) - - it('works with ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['3', '5', '1'], - ['=MEDIAN(A1:C1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(3) - }) - - it('propagates error from regular argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=3/0', '=MEDIAN(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('propagates first error from range argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=3/0', '=FOO(', '=MEDIAN(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('return error when no arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('coerces only explicit arguments, ignores provided via reference', () => { - const engine = HyperFormula.buildFromArray([ - ['="12"', '="11"', '="13"', '=MEDIAN(A1:C1)'], - ['=MEDIAN(TRUE())'], - ['=MEDIAN(1,2,3,B3:C3)'], - ]) - - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.OneValue)) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - }) - - it('ignores nonnumeric values as long as theres at least one numeric value', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(TRUE(), "foobar", 42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('coerces given string arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN("12", "11", "13")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(12) - }) - - it('empty args as 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIAN(1,2,3,,)'], - ['=MEDIAN(,)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) -}) diff --git a/test/unit/interpreter/function-mid.spec.ts b/test/unit/interpreter/function-mid.spec.ts deleted file mode 100644 index 8eeec4e360..0000000000 --- a/test/unit/interpreter/function-mid.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function MID', () => { - it('should take three arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=MID()'], - ['=MID("foo", "bar")'], - ['=MID("foo", "bar", "baz", "qux")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return substring', () => { - const engine = HyperFormula.buildFromArray([ - ['=MID("foo", 1, 2)'], - ['=MID("bar", 2, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('fo') - expect(engine.getCellValue(adr('A2'))).toEqual('ar') - }) - - it('should return empty string if start is greater than text length', () => { - const engine = HyperFormula.buildFromArray([ - ['=MID("", 2, 1)'], - ['=MID("foo", 5, 42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('') - expect(engine.getCellValue(adr('A2'))).toEqual('') - }) - - it('should return chars up to string length if end start + number of chars exceeds string length', () => { - const engine = HyperFormula.buildFromArray([ - ['=MID("foo", 2, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('oo') - }) - - it('should return #VALUE! if start num is less than 1', () => { - const engine = HyperFormula.buildFromArray([ - ['=MID("foo", 0, 1)'], - ['=MID("foo", -1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - }) - - it('should return #VALUE! if number of chars is negative', () => { - const engine = HyperFormula.buildFromArray([ - ['=MID("foo", 1, -1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NegativeLength)) - }) - - it('should coerce', () => { - const engine = HyperFormula.buildFromArray([ - ['=MID(TRUE(), 2, 2)'], - ['=MID(0, 1, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('RU') - expect(engine.getCellValue(adr('A2'))).toEqual('0') - }) - - it('should return error for range', () => { - const engine = HyperFormula.buildFromArray([ - ['=MID(B2:B3, 1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-min.spec.ts b/test/unit/interpreter/function-min.spec.ts deleted file mode 100644 index 99a6b503c3..0000000000 --- a/test/unit/interpreter/function-min.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('MIN', () => { - it('MIN with empty args', () => { - const engine = HyperFormula.buildFromArray([['=MIN()']]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('MIN with args', () => { - const engine = HyperFormula.buildFromArray([['=MIN(1, B1)', '3.14']]) - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('MIN with range', () => { - const engine = HyperFormula.buildFromArray([['1'], ['3'], ['2'], ['=MIN(A1:A3)']]) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - }) - - it('MIN with mixed arguments', () => { - const engine = HyperFormula.buildFromArray([['1'], ['3'], ['2'], ['=MIN(4,A1:A3)']]) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - }) - - it('MIN of strings and number', () => { - const engine = HyperFormula.buildFromArray([['foo'], ['bar'], ['5'], ['=MIN(A1:A3)']]) - expect(engine.getCellValue(adr('A4'))).toEqual(5) - }) - - it('doesnt do coercions', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['foo'], - ['=TRUE()'], - ['=CONCATENATE("1","0")'], - ['=MIN(A1:A5)'], - ]) - - expect(engine.getCellValue(adr('A6'))).toEqual(1) - }) - - it('MIN of empty value', () => { - const engine = HyperFormula.buildFromArray([['', '=MIN(A1)']]) - expect(engine.getCellValue(adr('B1'))).toEqual(0) - }) - - it('MIN of empty value and some negative number', () => { - const engine = HyperFormula.buildFromArray([['', '1', '=MIN(A1,B1)']]) - expect(engine.getCellValue(adr('C1'))).toEqual(1) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=MIN(MMULT(A1:B2, A1:B2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(7) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=4/0'], - ['=FOOBAR()', '4'], - ['=MIN(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-mina.spec.ts b/test/unit/interpreter/function-mina.spec.ts deleted file mode 100644 index 48c8a84a6a..0000000000 --- a/test/unit/interpreter/function-mina.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('MINA', () => { - it('MINA with empty args', () => { - const engine = HyperFormula.buildFromArray([['=MINA()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('MINA with args', () => { - const engine = HyperFormula.buildFromArray([['=MINA(1, B1)', '3.14']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('MINA with range', () => { - const engine = HyperFormula.buildFromArray([['1'], ['3'], ['2'], ['=MINA(A1:A3)']]) - - expect(engine.getCellValue(adr('A4'))).toEqual(1) - }) - - it('does only boolean coercions', () => { - const engine = HyperFormula.buildFromArray([ - ['="42"', '=MINA(A1)'], - ['=TRUE()', '=MINA(A2)'], - ['=FALSE()', '=MINA(A3)'], - ['="TRUE"', '=MINA(A4)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(0) - expect(engine.getCellValue(adr('B2'))).toEqual(1) - expect(engine.getCellValue(adr('B3'))).toEqual(0) - expect(engine.getCellValue(adr('B4'))).toEqual(0) - }) - - it('MINA of empty value', () => { - const engine = HyperFormula.buildFromArray([['', '=MINA(A1)']]) - - expect(engine.getCellValue(adr('B1'))).toEqual(0) - }) - - it('MINA of empty value and some positive number', () => { - const engine = HyperFormula.buildFromArray([ - ['', '1', '=MINA(A1,B1)'], - [null, '1', '=MINA(A2,B2)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(0) - expect(engine.getCellValue(adr('C2'))).toEqual(1) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=MINA(MMULT(A1:B2, A1:B2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(7) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=4/0'], - ['=FOOBAR()', '4'], - ['=MINA(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-minifs.spec.ts b/test/unit/interpreter/function-minifs.spec.ts deleted file mode 100644 index 1beacbfa42..0000000000 --- a/test/unit/interpreter/function-minifs.spec.ts +++ /dev/null @@ -1,259 +0,0 @@ -import {HyperFormula, ErrorType} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, expectArrayWithSameContent} from '../testUtils' -import {DateTimeHelper} from '../../../src/DateTimeHelper' -import {Config} from '../../../src/Config' - -describe('Function MINIFS - argument validations and combinations', () => { - it('requires odd number of arguments, but at least 3', () => { - const engine = HyperFormula.buildFromArray([['=MINIFS(C1, ">0")'], ['=MINIFS(C1, ">0", B1, B1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('error when criterion unparsable', () => { - const engine = HyperFormula.buildFromArray([ - ['=MINIFS(B1:B2, C1:C2, "> { - const engine = HyperFormula.buildFromArray([ - ['=MINIFS(B1:C1, B2:D2, ">0")'], - ['=MINIFS(B1, B2:D2, ">0")'], - ['=MINIFS(B1:D1, B2, ">0")'], - ['=MINIFS(B1:D1, B2:D2, ">0", B2:E2, ">0")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - }) - - it('error when different height dimension of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=MINIFS(B1:B2, C1:C3, ">0")'], - ['=MINIFS(B1, C1:C2, ">0")'], - ['=MINIFS(B1:B2, C1, ">0")'], - ['=MINIFS(B1:B2, C1:C2, ">0", C1:C3, ">0")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - }) - - it('scalars are treated like singular arrays', () => { - const engine = HyperFormula.buildFromArray([['=MINIFS(42, 10, ">1")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(42) - }) - - it('should return 0 as min from an empty range', () => { - const engine = HyperFormula.buildFromArray([['=MINIFS(42, -1, ">1")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) - - it('error propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=MINIFS(4/0, 42, ">1")'], - ['=MINIFS(0, 4/0, ">1")'], - ['=MINIFS(0, 42, 4/0)'], - ['=MINIFS(0, 4/0, FOOBAR())'], - ['=MINIFS(4/0, FOOBAR(), ">1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('works when arguments are just references', () => { - const engine = HyperFormula.buildFromArray([['2', '3'], ['=MINIFS(B1, A1, ">1")']]) - - expect(engine.getCellValue(adr('A2'))).toEqual(3) - }) - - it('works with numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['2', 'a'], - ['3', 'a'], - ['=MINIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(2) - }) - - it('works with percents', () => { - const engine = HyperFormula.buildFromArray([ - ['2%', 'a'], - ['3%', 'a'], - ['=MINIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(.02) - }) - - it('works with currencies', () => { - const engine = HyperFormula.buildFromArray([ - ['$2', 'a'], - ['$3', 'a'], - ['=MINIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(2) - }) - - it('works with dates', () => { - const dateHelper = new DateTimeHelper(new Config()) - - const engine = HyperFormula.buildFromArray([ - ['20/01/2020', 'a'], - ['30/01/2020', 'a'], - ['=MINIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(dateHelper.numberToSimpleDate(engine.getCellValue(adr('A3')) as number)).toEqual({ day: 20, month: 1, year: 2020 }) - }) - - it('strings are ignored', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['abc', 'a'], - ['=MINIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(1) - }) - - it('empty cells are ignored', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['', 'a'], - ['=MINIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(1) - }) - - it('booleans are ignored', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['=TRUE()', 'a'], - ['=MINIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(1) - }) - - it('min of empty cells is zero', () => { - const engine = HyperFormula.buildFromArray([ - ['', 'a'], - ['', 'a'], - ['=MINIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(0) - }) - - it('min of strings is zero', () => { - const engine = HyperFormula.buildFromArray([ - ['abc', 'a'], - ['cba', 'a'], - ['=MINIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(0) - }) - - it('min of booleans is zero', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRUE()', 'a'], - ['=FALSE()', 'a'], - ['=MINIFS(A1:A2, B1:B2, "a")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(0) - }) - - it('works with range values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1', '3', '5'], - ['1', '1', '7', '9'], - ['=MINIFS(MMULT(C1:D2, C1:D2), MMULT(A1:B2, A1:B2), "=2")'], - ['=MINIFS(MMULT(C1:D2, C1:D2), A1:B2, "=1")'], - ['=MINIFS(C1:D2, MMULT(A1:B2, A1:B2), "=2")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(44) - expect(engine.getCellValue(adr('A4'))).toEqual(44) - expect(engine.getCellValue(adr('A5'))).toEqual(3) - }) - - it('works for mixed reference/range arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['2', '3'], - ['=MINIFS(B1, A1:A1, ">1")'], - ['4', '5'], - ['=MINIFS(B3:B3, A3, ">1")'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqual(5) - }) - - it('works when criterion arg is an inline array', () => { - const engine = HyperFormula.buildFromArray([[2, 'a'], [3, 'b'], ['=MINIFS(A1:A2, B1:B2, {"a", "b"})']], { - useArrayArithmetic: true, - }) - - expect(engine.getCellValue(adr('A3'))).toEqual(2) - expect(engine.getCellValue(adr('B3'))).toEqual(3) - }) -}) - -describe('Function MINIFS - calcultions on more than one criteria', () => { - it('works for more than one criterion/range pair', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '100', '3'], - ['1', '101', '5'], - ['2', '102', '7'], - ['=MINIFS(C1:C3, A1:A3, ">=1", B1:B3, "<102")'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(5) - }) -}) - -describe('Function MINIFS - cache recalculation after cruds', () => { - it('recalculates MINIFS if changes in the first range', () => { - const sheet = [['10', '10'], ['5', '6'], ['7', '8'], ['=MINIFS(A1:B1, A2:B2, ">=5", A3:B3, ">=7")']] - const engine = HyperFormula.buildFromArray(sheet) - - const changes = engine.setCellContents(adr('A1'), [['1', '3']]) - const newValues = changes.map(c => c.newValue) - - expect(engine.getCellValue(adr('A4'))).toEqual(1) - expectArrayWithSameContent(newValues, [1, 3, 1]) - }) - - it('recalculates MINIFS if changes in one of the tested range', () => { - const sheet = [['10', '20'], ['5', '6'], ['7', '8'], ['=MINIFS(A1:B1, A2:B2, ">=5", A3:B3, ">=7")']] - const engine = HyperFormula.buildFromArray(sheet) - expect(engine.getCellValue(adr('A4'))).toEqual(10) - - engine.setCellContents(adr('A3'), [['1', '7']]) - - expect(engine.getCellValue(adr('A4'))).toEqual(20) - }) -}) diff --git a/test/unit/interpreter/function-minute.spec.ts b/test/unit/interpreter/function-minute.spec.ts deleted file mode 100644 index 454fcb92af..0000000000 --- a/test/unit/interpreter/function-minute.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function MINUTE', () => { - it('with wrong arguments', () => { - const engine = HyperFormula.buildFromArray([['=MINUTE("foo")', '=MINUTE("12/30/2018")', '=MINUTE(1, 2)', '=MINUTE()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('with numerical arguments', () => { - const engine = HyperFormula.buildFromArray([['=MINUTE(0.5123456)', '=MINUTE(0)', '=MINUTE(0.999999)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(17) - expect(engine.getCellValue(adr('B1'))).toEqual(0) - expect(engine.getCellValue(adr('C1'))).toEqual(0) - }) - - it('with string arguments', () => { - const engine = HyperFormula.buildFromArray([['=MINUTE("14:42:59")', '=MINUTE("01/01/1900 03:01:02am")', '=MINUTE("31/12/2018")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(42) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('C1'))).toEqual(0) - }) - - it('use datenumber coercion for 1st argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=MINUTE(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) - - it('propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=MINUTE(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-mirr.spec.ts b/test/unit/interpreter/function-mirr.spec.ts deleted file mode 100644 index a374bf3183..0000000000 --- a/test/unit/interpreter/function-mirr.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function MIRR', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=MIRR(1, 1)'], - ['=MIRR(1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return correct value', () => { - const engine = HyperFormula.buildFromArray([ - ['=MIRR(B1:F1, 0.3, -0.1)', 1, 2, -3, -5, 8], - ['=MIRR(B2:C2, 1, 1)', -1, 1], - ['=MIRR(B3:E3, 0.2, 0.1)', -1, 0, -1, 1], - ['=MIRR(B4:D4, 0.2, 0.1)', -1, -1, 1], - ['=MIRR(B5:D5, -2, 1)', 3, 4, -3] - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.257018945686308, 6) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(-0.161201673643132, 6) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(-0.261451054124004, 6) // different value without 0 - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(1.58198889747161, 6) - }) - - it('should return #DIV/0! if "contains at least one positive and one negative value" condition is not met', () => { - const engine = HyperFormula.buildFromArray([ - ['=MIRR(B1:E1,0.2,0.1)', -1, 0, -1, -1], - ['=MIRR(B2:E2,0.2,0.1)', 1, 0, 1, 1], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should return #DIV/0! if any rate is -1', () => { - const engine = HyperFormula.buildFromArray([ - ['=MIRR(B1:E1,-1,0.1)', -1, 1, -1, -1], - ['=MIRR(B2:E2,0.2,-1)', 1, -1, 1, 1], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should ignore text, boolean and empty values', () => { - const engine = HyperFormula.buildFromArray([ - ['=MIRR(B1:H1,0.2,0.1)', -1, 0, 'abcd', true, null, -1, 1], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-0.161201673643132, 6) - }) - - it('should return contained error if one exists', () => { - const engine = HyperFormula.buildFromArray([ - ['=MIRR(B1:E1,-1,0.1)', -1, 1, '=CONCATENATE(FOO)', -1], - ['=MIRR(B2:E2,0.2,-1)', 1, '=FIND("h","bar")', 1, 1], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.NamedExpressionName('FOO'))) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.PatternNotFound)) - }) -}) diff --git a/test/unit/interpreter/function-modulo.spec.ts b/test/unit/interpreter/function-modulo.spec.ts deleted file mode 100644 index 1de7766a5a..0000000000 --- a/test/unit/interpreter/function-modulo.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function MOD', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=MOD(101)'], - ['=MOD(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=MOD(1, "foo")'], - ['=MOD("bar", 4)'], - ['=MOD("foo", "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return error when dividing by 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=MOD(42, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=MOD(5, 2)'], - ['=MOD(36, 6)'], - ['=MOD(10.5, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(1.5) - }) -}) diff --git a/test/unit/interpreter/function-month.spec.ts b/test/unit/interpreter/function-month.spec.ts deleted file mode 100644 index a070c83bde..0000000000 --- a/test/unit/interpreter/function-month.spec.ts +++ /dev/null @@ -1,229 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function MONTH', () => { - it('with wrong arguments', () => { - const engine = HyperFormula.buildFromArray([['=MONTH("foo")', '=MONTH("12/30/2018")', '=MONTH(1, 2)', '=MONTH()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('with numerical arguments', () => { - const engine = HyperFormula.buildFromArray([['=MONTH(0)', '=MONTH(2)', '=MONTH(43465)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(12) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('C1'))).toEqual(12) - }) - - it('with string arguments', () => { - const engine = HyperFormula.buildFromArray([['=MONTH("31/12/1899")', '=MONTH("01/01/1900")', '=MONTH("31/12/2018")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(12) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('C1'))).toEqual(12) - }) - - it('use datenumber coercion for 1st argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=MONTH(TRUE())'], - ['=MONTH(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(12) - expect(engine.getCellValue(adr('A2'))).toEqual(12) - }) - - it('propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=MONTH(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('test for days in month, start of month', () => { - const engine = HyperFormula.buildFromArray([ - ['=MONTH(DATE(2021,1,1))'], - ['=MONTH(DATE(2021,2,1))'], - ['=MONTH(DATE(2021,3,1))'], - ['=MONTH(DATE(2021,4,1))'], - ['=MONTH(DATE(2021,5,1))'], - ['=MONTH(DATE(2021,6,1))'], - ['=MONTH(DATE(2021,7,1))'], - ['=MONTH(DATE(2021,8,1))'], - ['=MONTH(DATE(2021,9,1))'], - ['=MONTH(DATE(2021,10,1))'], - ['=MONTH(DATE(2021,11,1))'], - ['=MONTH(DATE(2021,12,1))'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqual(4) - expect(engine.getCellValue(adr('A5'))).toEqual(5) - expect(engine.getCellValue(adr('A6'))).toEqual(6) - expect(engine.getCellValue(adr('A7'))).toEqual(7) - expect(engine.getCellValue(adr('A8'))).toEqual(8) - expect(engine.getCellValue(adr('A9'))).toEqual(9) - expect(engine.getCellValue(adr('A10'))).toEqual(10) - expect(engine.getCellValue(adr('A11'))).toEqual(11) - expect(engine.getCellValue(adr('A12'))).toEqual(12) - }) - - it('test for days in month, end of month', () => { - const engine = HyperFormula.buildFromArray([ - ['=MONTH(DATE(2021,1,31))'], - ['=MONTH(DATE(2021,2,28))'], - ['=MONTH(DATE(2021,3,31))'], - ['=MONTH(DATE(2021,4,30))'], - ['=MONTH(DATE(2021,5,31))'], - ['=MONTH(DATE(2021,6,30))'], - ['=MONTH(DATE(2021,7,31))'], - ['=MONTH(DATE(2021,8,31))'], - ['=MONTH(DATE(2021,9,30))'], - ['=MONTH(DATE(2021,10,31))'], - ['=MONTH(DATE(2021,11,30))'], - ['=MONTH(DATE(2021,12,31))'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqual(4) - expect(engine.getCellValue(adr('A5'))).toEqual(5) - expect(engine.getCellValue(adr('A6'))).toEqual(6) - expect(engine.getCellValue(adr('A7'))).toEqual(7) - expect(engine.getCellValue(adr('A8'))).toEqual(8) - expect(engine.getCellValue(adr('A9'))).toEqual(9) - expect(engine.getCellValue(adr('A10'))).toEqual(10) - expect(engine.getCellValue(adr('A11'))).toEqual(11) - expect(engine.getCellValue(adr('A12'))).toEqual(12) - }) - - it('test for days in month, end of month+1', () => { - const engine = HyperFormula.buildFromArray([ - ['=MONTH(DATE(2021,1,31)+1)'], - ['=MONTH(DATE(2021,2,28)+1)'], - ['=MONTH(DATE(2021,3,31)+1)'], - ['=MONTH(DATE(2021,4,30)+1)'], - ['=MONTH(DATE(2021,5,31)+1)'], - ['=MONTH(DATE(2021,6,30)+1)'], - ['=MONTH(DATE(2021,7,31)+1)'], - ['=MONTH(DATE(2021,8,31)+1)'], - ['=MONTH(DATE(2021,9,30)+1)'], - ['=MONTH(DATE(2021,10,31)+1)'], - ['=MONTH(DATE(2021,11,30)+1)'], - ['=MONTH(DATE(2021,12,31)+1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(engine.getCellValue(adr('A2'))).toEqual(3) - expect(engine.getCellValue(adr('A3'))).toEqual(4) - expect(engine.getCellValue(adr('A4'))).toEqual(5) - expect(engine.getCellValue(adr('A5'))).toEqual(6) - expect(engine.getCellValue(adr('A6'))).toEqual(7) - expect(engine.getCellValue(adr('A7'))).toEqual(8) - expect(engine.getCellValue(adr('A8'))).toEqual(9) - expect(engine.getCellValue(adr('A9'))).toEqual(10) - expect(engine.getCellValue(adr('A10'))).toEqual(11) - expect(engine.getCellValue(adr('A11'))).toEqual(12) - expect(engine.getCellValue(adr('A12'))).toEqual(1) - }) - - it('test for days in month, start of month, leap year', () => { - const engine = HyperFormula.buildFromArray([ - ['=MONTH(DATE(2020,1,1))'], - ['=MONTH(DATE(2020,2,1))'], - ['=MONTH(DATE(2020,3,1))'], - ['=MONTH(DATE(2020,4,1))'], - ['=MONTH(DATE(2020,5,1))'], - ['=MONTH(DATE(2020,6,1))'], - ['=MONTH(DATE(2020,7,1))'], - ['=MONTH(DATE(2020,8,1))'], - ['=MONTH(DATE(2020,9,1))'], - ['=MONTH(DATE(2020,10,1))'], - ['=MONTH(DATE(2020,11,1))'], - ['=MONTH(DATE(2020,12,1))'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqual(4) - expect(engine.getCellValue(adr('A5'))).toEqual(5) - expect(engine.getCellValue(adr('A6'))).toEqual(6) - expect(engine.getCellValue(adr('A7'))).toEqual(7) - expect(engine.getCellValue(adr('A8'))).toEqual(8) - expect(engine.getCellValue(adr('A9'))).toEqual(9) - expect(engine.getCellValue(adr('A10'))).toEqual(10) - expect(engine.getCellValue(adr('A11'))).toEqual(11) - expect(engine.getCellValue(adr('A12'))).toEqual(12) - }) - - it('test for days in month, end of month, leap year', () => { - const engine = HyperFormula.buildFromArray([ - ['=MONTH(DATE(2020,1,31))'], - ['=MONTH(DATE(2020,2,29))'], - ['=MONTH(DATE(2020,3,31))'], - ['=MONTH(DATE(2020,4,30))'], - ['=MONTH(DATE(2020,5,31))'], - ['=MONTH(DATE(2020,6,30))'], - ['=MONTH(DATE(2020,7,31))'], - ['=MONTH(DATE(2020,8,31))'], - ['=MONTH(DATE(2020,9,30))'], - ['=MONTH(DATE(2020,10,31))'], - ['=MONTH(DATE(2020,11,30))'], - ['=MONTH(DATE(2020,12,31))'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqual(4) - expect(engine.getCellValue(adr('A5'))).toEqual(5) - expect(engine.getCellValue(adr('A6'))).toEqual(6) - expect(engine.getCellValue(adr('A7'))).toEqual(7) - expect(engine.getCellValue(adr('A8'))).toEqual(8) - expect(engine.getCellValue(adr('A9'))).toEqual(9) - expect(engine.getCellValue(adr('A10'))).toEqual(10) - expect(engine.getCellValue(adr('A11'))).toEqual(11) - expect(engine.getCellValue(adr('A12'))).toEqual(12) - }) - - it('test for days in month, end of month+1, leap year', () => { - const engine = HyperFormula.buildFromArray([ - ['=MONTH(DATE(2020,1,31)+1)'], - ['=MONTH(DATE(2020,2,29)+1)'], - ['=MONTH(DATE(2020,3,31)+1)'], - ['=MONTH(DATE(2020,4,30)+1)'], - ['=MONTH(DATE(2020,5,31)+1)'], - ['=MONTH(DATE(2020,6,30)+1)'], - ['=MONTH(DATE(2020,7,31)+1)'], - ['=MONTH(DATE(2020,8,31)+1)'], - ['=MONTH(DATE(2020,9,30)+1)'], - ['=MONTH(DATE(2020,10,31)+1)'], - ['=MONTH(DATE(2020,11,30)+1)'], - ['=MONTH(DATE(2020,12,31)+1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(engine.getCellValue(adr('A2'))).toEqual(3) - expect(engine.getCellValue(adr('A3'))).toEqual(4) - expect(engine.getCellValue(adr('A4'))).toEqual(5) - expect(engine.getCellValue(adr('A5'))).toEqual(6) - expect(engine.getCellValue(adr('A6'))).toEqual(7) - expect(engine.getCellValue(adr('A7'))).toEqual(8) - expect(engine.getCellValue(adr('A8'))).toEqual(9) - expect(engine.getCellValue(adr('A9'))).toEqual(10) - expect(engine.getCellValue(adr('A10'))).toEqual(11) - expect(engine.getCellValue(adr('A11'))).toEqual(12) - expect(engine.getCellValue(adr('A12'))).toEqual(1) - }) -}) diff --git a/test/unit/interpreter/function-mround.spec.ts b/test/unit/interpreter/function-mround.spec.ts deleted file mode 100644 index 19ef6f5f61..0000000000 --- a/test/unit/interpreter/function-mround.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function MROUND', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=MROUND(101)'], - ['=MROUND(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=MROUND(1, "foo")'], - ['=MROUND("bar", 4)'], - ['=MROUND("foo", "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return 0 when dividing by 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=MROUND(42, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) - - it('should return error for args of different signs', () => { - const engine = HyperFormula.buildFromArray([ - ['=MROUND(42, -1)'], - ['=MROUND(-42, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.DistinctSigns)) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.DistinctSigns)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=MROUND(5, 2)'], - ['=MROUND(36, 6.5)'], - ['=MROUND(10.5, 3)'], - ['=MROUND(-5, -2)'], - ['=MROUND(-36, -6.5)'], - ['=MROUND(-10.5, -3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(6) - expect(engine.getCellValue(adr('A2'))).toEqual(39) - expect(engine.getCellValue(adr('A3'))).toEqual(12) - expect(engine.getCellValue(adr('A4'))).toEqual(-6) - expect(engine.getCellValue(adr('A5'))).toEqual(-39) - expect(engine.getCellValue(adr('A6'))).toEqual(-12) - }) - - /** - * Tests below are results of how floating point arithmetic works. - * This behavior is undefined, and this is test for consistency between platforms. - * If this test starts throwing errors, it should be disabled. - */ - it('known limitations', () => { - const engine = HyperFormula.buildFromArray([ - ['=MROUND(6.05, 0.1)'], - ['=MROUND(7.05, 0.1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(6) - expect(engine.getCellValue(adr('A2'))).toEqual(7.1) - }) -}) diff --git a/test/unit/interpreter/function-multinomial.spec.ts b/test/unit/interpreter/function-multinomial.spec.ts deleted file mode 100644 index b2192445bf..0000000000 --- a/test/unit/interpreter/function-multinomial.spec.ts +++ /dev/null @@ -1,93 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function MULTINOMIAL', () => { - it('checks required number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=MULTINOMIAL()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('computes correct answer for two args', () => { - const engine = HyperFormula.buildFromArray([ - ['=MULTINOMIAL(6,8)', '=MULTINOMIAL(0,0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(3003) - expect(engine.getCellValue(adr('B1'))).toBe(1) - }) - - it('computes correct answer for more than two args', () => { - const engine = HyperFormula.buildFromArray([ - ['=MULTINOMIAL(2,3,5,7)', '=MULTINOMIAL(10,11,12,13,14)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(49008960) - expect(engine.getCellValue(adr('B1')) as number / 2.20917676017678e+38).toBeCloseTo(1, 6) - }) - - it('accepts single arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=MULTINOMIAL(1000)', '=MULTINOMIAL(0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('B1'))).toBe(1) - }) - - it('coerces to number', () => { - const engine = HyperFormula.buildFromArray([ - ['=MULTINOMIAL("2",4)'], - ['=MULTINOMIAL(B2:C2)', '\'2', 4], - ['=MULTINOMIAL(TRUE(),4)'], - ['=MULTINOMIAL(B4:C4)', true, 4], - ['=MULTINOMIAL(,4)'], - ['=MULTINOMIAL(B6:C6)', null, 4], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(15) - expect(engine.getCellValue(adr('A2'))).toBe(15) - expect(engine.getCellValue(adr('A3'))).toBe(5) - expect(engine.getCellValue(adr('A4'))).toBe(5) - expect(engine.getCellValue(adr('A5'))).toBe(1) - expect(engine.getCellValue(adr('A6'))).toBe(1) - }) - - it('throws error for non-coercible values', () => { - const engine = HyperFormula.buildFromArray([ - ['=MULTINOMIAL(B1:C1)', 'abcd', 4], - ['=MULTINOMIAL("abcd",4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=MULTINOMIAL(-1,5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('truncates numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=MULTINOMIAL(B1:C1)', 5.5, 10.9], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(3003) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=MULTINOMIAL(NA(),4)'], - ['=MULTINOMIAL(B2:C2)', '=NA()', 4], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-n.spec.ts b/test/unit/interpreter/function-n.spec.ts deleted file mode 100644 index 63b5c84ff7..0000000000 --- a/test/unit/interpreter/function-n.spec.ts +++ /dev/null @@ -1,224 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function N', () => { - describe('Argument validation', () => { - it('should take exactly one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=N()'], - ['=N(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - }) - - describe('Numeric values', () => { - it('should return the number for numeric input', () => { - const engine = HyperFormula.buildFromArray([ - ['=N(42)'], - ['=N(-42)'], - ['=N(3.14)'], - ['=N(-3.14)'], - ['=N(0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(42) - expect(engine.getCellValue(adr('A2'))).toBe(-42) - expect(engine.getCellValue(adr('A3'))).toBe(3.14) - expect(engine.getCellValue(adr('A4'))).toBe(-3.14) - expect(engine.getCellValue(adr('A5'))).toBe(0) - }) - - it('should return number from cell reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=N(B1)', 123], - ['=N(B2)', -456.78], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(123) - expect(engine.getCellValue(adr('A2'))).toBe(-456.78) - }) - }) - - describe('Logical values', () => { - it('should return 1 for TRUE and 0 for FALSE', () => { - const engine = HyperFormula.buildFromArray([ - ['=N(TRUE())'], - ['=N(FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('A2'))).toBe(0) - }) - - it('should return 1 for TRUE and 0 for FALSE from cell reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=N(B1)', '=TRUE()'], - ['=N(B2)', '=FALSE()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('A2'))).toBe(0) - }) - }) - - describe('Text values', () => { - it('should return 0 for text strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=N("Hello")'], - ['=N("123")'], - ['=N("")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - expect(engine.getCellValue(adr('A2'))).toBe(0) - expect(engine.getCellValue(adr('A3'))).toBe(0) - }) - - it('should return 0 for boolean values as text', () => { - const engine = HyperFormula.buildFromArray([ - ['=N("TRUE")'], - ['=N("FALSE")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - expect(engine.getCellValue(adr('A2'))).toBe(0) - }) - - it('should return 0 for text from cell reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=N(B1)', 'Hello'], - ['=N(B2)', '\'123'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - expect(engine.getCellValue(adr('A2'))).toBe(0) - }) - }) - - describe('Date values', () => { - it('should return serial number for date', () => { - const engine = HyperFormula.buildFromArray([ - ['=N(DATE(2011, 4, 17))'], - ]) - - // April 17, 2011 = serial number 40650 in 1900 date system - expect(engine.getCellValue(adr('A1'))).toBe(40650) - }) - - it('should return serial number for date from cell reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=N(B1)', '=DATE(2011, 4, 17)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(40650) - }) - }) - - describe('Empty/Null values', () => { - it('should return 0 for empty cell reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=N(B1)', null], - ['=N(B2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - expect(engine.getCellValue(adr('A2'))).toBe(0) - }) - }) - - describe('Error propagation', () => { - it('should propagate error values', () => { - const engine = HyperFormula.buildFromArray([ - ['=N(1/0)'], - ['=N(NA())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('should propagate error from cell reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=N(B1)', '=1/0'], - ['=N(B2)', '=FOO()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOO'))) - }) - }) - - describe('Range values', () => { - it('should use first cell value from range', () => { - const engine = HyperFormula.buildFromArray([ - [42], - ['text'], - [100], - ['=N(A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toBe(42) - }) - - it('should return 0 when first cell of range is text', () => { - const engine = HyperFormula.buildFromArray([ - ['text'], - [42], - [100], - ['=N(A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toBe(0) - }) - - it('should propagate error from first cell of range', () => { - const engine = HyperFormula.buildFromArray([ - ['=1/0'], - [42], - [100], - ['=N(A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should return 0 when first cell of range is empty', () => { - const engine = HyperFormula.buildFromArray([ - [null], - [42], - [100], - ['=N(A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toBe(0) - }) - - it('should return 1 when first cell of range is TRUE', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRUE()'], - [42], - [100], - ['=N(A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toBe(1) - }) - }) - - describe('Nested function calls', () => { - it('should handle nested N calls', () => { - const engine = HyperFormula.buildFromArray([ - ['=N(N("text"))'], - ['=N(N(42))'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - expect(engine.getCellValue(adr('A2'))).toBe(42) - }) - }) -}) diff --git a/test/unit/interpreter/function-na.spec.ts b/test/unit/interpreter/function-na.spec.ts deleted file mode 100644 index 32940a8c2c..0000000000 --- a/test/unit/interpreter/function-na.spec.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {adr, detailedError} from '../testUtils' - -describe('Function NA', () => { - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=NA()', '=NA(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-negbinom.dist.spec.ts b/test/unit/interpreter/function-negbinom.dist.spec.ts deleted file mode 100644 index 8b42069684..0000000000 --- a/test/unit/interpreter/function-negbinom.dist.spec.ts +++ /dev/null @@ -1,82 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function NEGBINOM.DIST', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=NEGBINOM.DIST(1, 2, 3)'], - ['=NEGBINOM.DIST(1, 2, 3, 4, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=NEGBINOM.DIST("foo", 2, 1, TRUE())'], - ['=NEGBINOM.DIST(1, "baz", 1, TRUE())'], - ['=NEGBINOM.DIST(1, 2, "baz", TRUE())'], - ['=NEGBINOM.DIST(1, 2, 1, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=NEGBINOM.DIST(10, 20, 0.5, TRUE())'], - ['=NEGBINOM.DIST(2, 3, 0.1, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0493685733526945, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.00856, 6) - }) - - it('should work as pdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=NEGBINOM.DIST(10, 20, 0.5, FALSE())'], - ['=NEGBINOM.DIST(2, 3, 0.1, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0186544004827738, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.00486, 6) - }) - - it('truncates first and second arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=NEGBINOM.DIST(10.9, 20, 0.5, FALSE())'], - ['=NEGBINOM.DIST(2, 3.9, 0.1, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0186544004827738, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.00486, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=NEGBINOM.DIST(0, 1, 0.5, FALSE())'], - ['=NEGBINOM.DIST(-0.001, 1, 0.5, FALSE())'], - ['=NEGBINOM.DIST(0, 0.999, 0.5, FALSE())'], - ['=NEGBINOM.DIST(0, 1, 0, FALSE())'], - ['=NEGBINOM.DIST(0, 1, -0.01, FALSE())'], - ['=NEGBINOM.DIST(0, 1, 1, FALSE())'], - ['=NEGBINOM.DIST(0, 1, 1.01, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.5) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - //product #2 returns error - expect(engine.getCellValue(adr('A4'))).toEqual(0) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - //product #2 returns error - expect(engine.getCellValue(adr('A6'))).toEqual(1) - expect(engine.getCellValue(adr('A7'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-networkdays.intl.spec.ts b/test/unit/interpreter/function-networkdays.intl.spec.ts deleted file mode 100644 index 87fd430f3a..0000000000 --- a/test/unit/interpreter/function-networkdays.intl.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function NETWORKDAYS.INTL', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=NETWORKDAYS.INTL(1)', '=NETWORKDAYS.INTL(1, 1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should check for types or value of third argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=NETWORKDAYS.INTL(0, 1, TRUE())'], - ['=NETWORKDAYS.INTL(0, 1, "1")'], - ['=NETWORKDAYS.INTL(0, 1, "1010102")'], - ['=NETWORKDAYS.INTL(0, 1, -1)'], - ['=NETWORKDAYS.INTL(0, 1, "1111111")'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.WeekendString)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.WeekendString)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.BadMode)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.WeekendString)) - }) - - it('works correctly for first two arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=NETWORKDAYS.INTL(0, 1)'], - ['=NETWORKDAYS.INTL(0, 6)'], - ['=NETWORKDAYS.INTL(0, 6.9)'], - ['=NETWORKDAYS.INTL(6.9,0.1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(5) - expect(engine.getCellValue(adr('A3'))).toEqual(5) - expect(engine.getCellValue(adr('A4'))).toEqual(-5) - }) - - it('today', () => { - const engine = HyperFormula.buildFromArray([ - ['=NETWORKDAYS.INTL("29/09/2020", "29/09/2020")'], - ['=NETWORKDAYS.INTL("29/09/2020", "29/09/2020", 3)'], - ['=NETWORKDAYS.INTL("29/09/2020", "29/09/2020", 4)'], - ['=NETWORKDAYS.INTL("29/09/2020", "29/09/2020", 13)'], - ['=NETWORKDAYS.INTL("29/09/2020", "29/09/2020", "1011111")'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(0) - expect(engine.getCellValue(adr('A4'))).toEqual(0) - expect(engine.getCellValue(adr('A5'))).toEqual(1) - }) - - it('this year', () => { - const engine = HyperFormula.buildFromArray([ - ['29/09/2020', '=A1+0.1', '31/12/2019', '01/01/2021', '27/09/2020'], - ['=NETWORKDAYS.INTL("01/01/2020", "31/12/2020", 1)'], - ['=NETWORKDAYS.INTL("01/01/2020", "31/12/2020", 1, A1:A1)'], - ['=NETWORKDAYS.INTL("01/01/2020", "31/12/2020", 1, A1:B1)'], - ['=NETWORKDAYS.INTL("01/01/2020", "31/12/2020", 1, A1:D1)'], - ['=NETWORKDAYS.INTL("01/01/2020", "31/12/2020", 1, A1:E1)'], - ]) - expect(engine.getCellValue(adr('A2'))).toEqual(262) - expect(engine.getCellValue(adr('A3'))).toEqual(261) - expect(engine.getCellValue(adr('A4'))).toEqual(261) - expect(engine.getCellValue(adr('A5'))).toEqual(261) - expect(engine.getCellValue(adr('A6'))).toEqual(261) - }) - - it('should output correct values', () => { - const engine = HyperFormula.buildFromArray([ - ['01/01/2020', '=A1+5', '=A1+8', '=A1+9', '=A1+15', '=A1+18', '=A1+19', '=A1+32', '=A1+54', '=A1+55'], - ['=NETWORKDAYS.INTL(A1, A1+100, "0000000", A1:J1)'], - ['=NETWORKDAYS.INTL(A1+7, A1+20, "0000000", A1:J1)'], - ['=NETWORKDAYS.INTL(A1+7, A1+100, "0000000", A1:J1)'], - ['=NETWORKDAYS.INTL(A1+13, A1+50, "0000000", A1:J1)'], - ['=NETWORKDAYS.INTL(A1+50, A1+56, "0000000", A1:J1)'], - ]) - expect(engine.getCellValue(adr('A2'))).toEqual(91) - expect(engine.getCellValue(adr('A3'))).toEqual(9) - expect(engine.getCellValue(adr('A4'))).toEqual(86) - expect(engine.getCellValue(adr('A5'))).toEqual(34) - expect(engine.getCellValue(adr('A6'))).toEqual(5) - }) - - it('checks types in last argument', () => { - const engine = HyperFormula.buildFromArray([ - [true, '\'1', null, '=NA()'], - ['=NETWORKDAYS.INTL(1000, 1, 1, A1:A1)'], - ['=NETWORKDAYS.INTL(1000, 1, 1, B1:B1)'], - ['=NETWORKDAYS.INTL(1000, 1, 1, C1:C1)'], - ['=NETWORKDAYS.INTL(1000, 1, 1, A1:D1)'], - ]) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A4'))).toEqual(-715) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-networkdays.spec.ts b/test/unit/interpreter/function-networkdays.spec.ts deleted file mode 100644 index b109aff473..0000000000 --- a/test/unit/interpreter/function-networkdays.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function NETWORKDAYS', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=NETWORKDAYS(1)', '=NETWORKDAYS(1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works correctly for first two arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=NETWORKDAYS(0, 1)'], - ['=NETWORKDAYS(0, 6)'], - ['=NETWORKDAYS(0, 6.9)'], - ['=NETWORKDAYS(6.9,0.1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(5) - expect(engine.getCellValue(adr('A3'))).toEqual(5) - expect(engine.getCellValue(adr('A4'))).toEqual(-5) - }) - - it('this year', () => { - const engine = HyperFormula.buildFromArray([ - ['29/09/2020', '=A1+0.1', '31/12/2019', '01/01/2021', '27/09/2020'], - ['=NETWORKDAYS("01/01/2020", "31/12/2020")'], - ['=NETWORKDAYS("01/01/2020", "31/12/2020", A1:A1)'], - ['=NETWORKDAYS("01/01/2020", "31/12/2020", A1:B1)'], - ['=NETWORKDAYS("01/01/2020", "31/12/2020", A1:D1)'], - ['=NETWORKDAYS("01/01/2020", "31/12/2020", A1:E1)'], - ]) - expect(engine.getCellValue(adr('A2'))).toEqual(262) - expect(engine.getCellValue(adr('A3'))).toEqual(261) - expect(engine.getCellValue(adr('A4'))).toEqual(261) - expect(engine.getCellValue(adr('A5'))).toEqual(261) - expect(engine.getCellValue(adr('A6'))).toEqual(261) - }) - - it('should output correct values', () => { - const engine = HyperFormula.buildFromArray([ - ['01/01/2020', '=A1+5', '=A1+8', '=A1+9', '=A1+15', '=A1+18', '=A1+19', '=A1+32', '=A1+54', '=A1+55'], - ['=NETWORKDAYS(A1, A1+100, A1:J1)'], - ['=NETWORKDAYS(A1+7, A1+20, A1:J1)'], - ['=NETWORKDAYS(A1+7, A1+100, A1:J1)'], - ['=NETWORKDAYS(A1+13, A1+50, A1:J1)'], - ['=NETWORKDAYS(A1+50, A1+56, A1:J1)'], - ]) - expect(engine.getCellValue(adr('A2'))).toEqual(65) - expect(engine.getCellValue(adr('A3'))).toEqual(6) - expect(engine.getCellValue(adr('A4'))).toEqual(62) - expect(engine.getCellValue(adr('A5'))).toEqual(26) - expect(engine.getCellValue(adr('A6'))).toEqual(3) - }) - - it('checks types in last argument', () => { - const engine = HyperFormula.buildFromArray([ - [true, '\'1', null, '=NA()'], - ['=NETWORKDAYS(1000, 1, A1:A1)'], - ['=NETWORKDAYS(1000, 1, B1:B1)'], - ['=NETWORKDAYS(1000, 1, C1:C1)'], - ['=NETWORKDAYS(1000, 1, A1:D1)'], - ]) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A4'))).toEqual(-715) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-nominal.spec.ts b/test/unit/interpreter/function-nominal.spec.ts deleted file mode 100644 index 99c9c9ccd1..0000000000 --- a/test/unit/interpreter/function-nominal.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function NOMINAL', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=NOMINAL(1)', '=NOMINAL(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=NOMINAL(2%, 1)', '=NOMINAL(2%, 2)', '=NOMINAL(2%, 2.9)', '=NOMINAL(2%, 24)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.02, 9) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.0199009876724157, 9) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(0.0199009876724157, 9) - expect(engine.getCellValue(adr('D1'))).toBeCloseTo(0.0198107992112657, 9) - }) -}) diff --git a/test/unit/interpreter/function-norm.dist.spec.ts b/test/unit/interpreter/function-norm.dist.spec.ts deleted file mode 100644 index a40195edd6..0000000000 --- a/test/unit/interpreter/function-norm.dist.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function NORM.DIST', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.DIST(1, 2, 3)'], - ['=NORM.DIST(1, 2, 3, 4, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.DIST("foo", 2, 3, TRUE())'], - ['=NORM.DIST(1, "baz", 3, TRUE())'], - ['=NORM.DIST(1, 2, "baz", TRUE())'], - ['=NORM.DIST(1, 2, 3, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.DIST(-1, 1, 2, TRUE())'], - ['=NORM.DIST(0.5, 2, 4, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.158655253931457, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.353830233327276, 6) - }) - - it('should work as pdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.DIST(-1, 1, 2, FALSE())'], - ['=NORM.DIST(0.5, 2, 4, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.120985362259572, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0929637734674423, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.DIST(-1, -1, 0.01, FALSE())'], - ['=NORM.DIST(-1, -1, 0, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(39.8942280401433, 6) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-norm.inv.spec.ts b/test/unit/interpreter/function-norm.inv.spec.ts deleted file mode 100644 index 8700b55546..0000000000 --- a/test/unit/interpreter/function-norm.inv.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function NORM.INV', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.INV(1, 2)'], - ['=NORM.INV(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.INV("foo", 2, 3)'], - ['=NORM.INV(0.5, "baz", 3)'], - ['=NORM.INV(0.5, 2, "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.INV(0.9, 1, 2)'], - ['=NORM.INV(0.5, 2, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(3.5631031310892, 6) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.INV(0.5, -1, 0.01)'], - ['=NORM.INV(0.5, -1, 0)'], - ['=NORM.INV(0.01, -1, 0.01)'], - ['=NORM.INV(0, -1, 0.01)'], - ['=NORM.INV(0.99, -1, 0.01)'], - ['=NORM.INV(1, -1, 0.01)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(-1) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(-1.02326347874041, 6) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(-0.976736521259592, 6) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-norm.s.dist.spec.ts b/test/unit/interpreter/function-norm.s.dist.spec.ts deleted file mode 100644 index e2ad8a2c4d..0000000000 --- a/test/unit/interpreter/function-norm.s.dist.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function NORM.S.DIST', () => { - //in product #1, this function takes 1 argument - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.S.DIST(2)'], - ['=NORM.S.DIST(1, 4, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - //in product #1, this function takes 1 argument - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.S.DIST("foo", TRUE())'], - ['=NORM.S.DIST(1, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - //in product #1, this function takes 1 argument - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.S.DIST(-1, TRUE())'], - ['=NORM.S.DIST(0.5, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.158655253931457, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.691462461274013, 6) - }) - - //in product #1, this function takes 1 argument - it('should work as pdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.S.DIST(-1, FALSE())'], - ['=NORM.S.DIST(0.5, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.241970724519143, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.3520653267643, 6) - }) -}) diff --git a/test/unit/interpreter/function-norm.s.inv.spec.ts b/test/unit/interpreter/function-norm.s.inv.spec.ts deleted file mode 100644 index 3ca2d7a089..0000000000 --- a/test/unit/interpreter/function-norm.s.inv.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function NORM.S.INV', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.S.INV()'], - ['=NORM.S.INV(3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.S.INV("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.S.INV(0.9)'], - ['=NORM.S.INV(0.5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.2815515655446, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=NORM.S.INV(0.01)'], - ['=NORM.S.INV(0)'], - ['=NORM.S.INV(0.99)'], - ['=NORM.S.INV(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-2.32634787404084, 6) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(2.32634787404084, 6) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-not.spec.ts b/test/unit/interpreter/function-not.spec.ts deleted file mode 100644 index 1e95699c8c..0000000000 --- a/test/unit/interpreter/function-not.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function NOT', () => { - it('number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=NOT()', '=NOT(TRUE(), TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['=NOT(TRUE())', '=NOT(FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('B1'))).toBe(true) - }) - - it('use coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['=NOT("FALSE")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - }) - - it('propagates error', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/0'], - ['=NOT(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-now.spec.ts b/test/unit/interpreter/function-now.spec.ts deleted file mode 100644 index e35124706c..0000000000 --- a/test/unit/interpreter/function-now.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueDetailedType, ErrorType} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Interpreter - function NOW', () => { - let originalNow: () => number - - beforeEach(() => { - originalNow = Date.now - let cnt = 20 - Date.now = () => { - cnt += 1 - return Date.parse(`1985-08-16T03:45:${cnt}`) - } - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['=NOW()'], - ]) - const t1 = engine.getCellValue(adr('A1')) as number - expect(t1).toBeCloseTo(31275.1565856481) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - engine.setCellContents(adr('A2'), null) - const delta = engine.getCellValue(adr('A1')) as number - t1 - expect(delta * 24 * 60 * 60).toBeGreaterThanOrEqual(1) //internals of the engine are also using Date.now(), so the value should be actually 4 or even more - }) - - it('works #2', () => { - const engine = HyperFormula.buildFromArray([ - ['=YEAR(NOW())'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(1985) - }) - - it('works #3', () => { - const engine = HyperFormula.buildFromArray([ - ['=MONTH(NOW())'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(8) - }) - - it('works #4', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAY(NOW())'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(16) - }) - - it('works #5', () => { - const engine = HyperFormula.buildFromArray([ - ['=HOUR(NOW())'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('works #6', () => { - const engine = HyperFormula.buildFromArray([ - ['=MINUTE(NOW())'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(45) - }) - - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=NOW(42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - afterEach(() => { - Date.now = originalNow - }) -}) diff --git a/test/unit/interpreter/function-nper.spec.ts b/test/unit/interpreter/function-nper.spec.ts deleted file mode 100644 index 877a2c8dda..0000000000 --- a/test/unit/interpreter/function-nper.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function NPER', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=NPER(1,1)', '=NPER(1, 1, 1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=NPER(1%, 1, 100, 1)', '=NPER(1%, 1, 100, 1, 1)', '=NPER(1%, 1, 100, 1, 2)'], - ['=NPER(100%, -50, 100, 0, 1)', '=NPER(100%, -50, 100, -100, 1)', '=NPER(-100%, 1, 100, 1, 1)', '=NPER(-200%, 1, 100, 1, 1)'], - ['=NPER(-20%, -50, 100, 300, 1)', ], - ['=NPER(0%, -50, 100, 300, 1)', ], - ['=NPER(0%, 0, 100, 100, 1)', '=NPER(0%, 0, 100, -100, 1)'], - ['=NPER(1%, 0, 100, 100, 1)', '=NPER(1%, 0, 100, -50, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-70.67076731) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-70.16196068) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(-70.16196068) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expect(engine.getCellValue(adr('C2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expect(engine.getCellValue(adr('D2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expect(engine.getCellValue(adr('A4'))).toEqual(8) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('B5'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expect(engine.getCellValue(adr('B6'))).toBeCloseTo(-69.66071689) - }) -}) diff --git a/test/unit/interpreter/function-npv.spec.ts b/test/unit/interpreter/function-npv.spec.ts deleted file mode 100644 index ec9e55dd7a..0000000000 --- a/test/unit/interpreter/function-npv.spec.ts +++ /dev/null @@ -1,63 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function NPV', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=NPV(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should ignore logical and text values', () => { - const engine = HyperFormula.buildFromArray([ - ['=NPV(1, B1:C1)', 1, 'abcd'], - ['=NPV(1, B2:C2)', true, 1], - ['=NPV(-1, 0)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(0.5) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValue(adr('A2'))).toEqual(0.5) - expect(engine.getCellValue(adr('A3'))).toEqual(0) - }) - - it('should be compatible with product #2', () => { - const engine = HyperFormula.buildFromArray([ - ['=NPV(1, TRUE(), 1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(0.75) //product #1 returns 0.5 - }) - - it('order of arguments matters', () => { - const engine = HyperFormula.buildFromArray([ - ['=NPV(1, A2:B3)'], - [1, 2], - [3, 4], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(1.625) - }) - - it('should return correct error value', () => { - const engine = HyperFormula.buildFromArray([ - ['=NPV(1, NA())'], - ['=NPV(1, 1, "abcd")'], - ['=NPV(-1,1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - /** - * Inconsistency with products #1 and #2. - */ - it('cell reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=NPV(1,B1)', true] - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(0.5) //Both products #1 and #2 return 0 here. - }) -}) diff --git a/test/unit/interpreter/function-oct2bin.spec.ts b/test/unit/interpreter/function-oct2bin.spec.ts deleted file mode 100644 index 59c2720655..0000000000 --- a/test/unit/interpreter/function-oct2bin.spec.ts +++ /dev/null @@ -1,131 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function OCT2BIN', () => { - it('should return error when wrong number of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2BIN("foo", 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for non-oct arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2BIN("foo")'], - ['=OCT2BIN(418)'], - ['=OCT2BIN(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotOctal)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotOctal)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotOctal)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2BIN(1)'], - ['=OCT2BIN(10)'], - ['=OCT2BIN(71)'], - ['=OCT2BIN(777)'], - ['=OCT2BIN(7777777000)'], - ['=OCT2BIN(7777777042)'], - ['=OCT2BIN(7777777777)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - expect(engine.getCellValue(adr('A2'))).toEqual('1000') - expect(engine.getCellValue(adr('A3'))).toEqual('111001') - expect(engine.getCellValue(adr('A4'))).toEqual('111111111') - expect(engine.getCellValue(adr('A5'))).toEqual('1000000000') - expect(engine.getCellValue(adr('A6'))).toEqual('1000100010') - expect(engine.getCellValue(adr('A7'))).toEqual('1111111111') - }) - - it('should work for strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2BIN("456")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('100101110') - }) - - it('should work for reference', () => { - const engine = HyperFormula.buildFromArray([ - ['="123"'], - ['=OCT2BIN(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual('1010011') - }) - - it('should return string value', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2BIN(11)'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.STRING) - }) - - it('should work only for 10 digits', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2BIN(10101010101010)'], - ['=OCT2BIN(7777777042)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotOctal)) - expect(engine.getCellValue(adr('A2'))).toEqual('1000100010') - }) - - it('result cannot be longer than 10 digits', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2BIN(1000)'], - ['=OCT2BIN(7777776777)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLarge)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseSmall)) - }) - - it('should respect second argument and fill with zeros for positive arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2BIN(12, 8)'], - ['=OCT2BIN(3, "4")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('00001010') - expect(engine.getCellValue(adr('A2'))).toEqual('0011') - }) - - it('second argument should not affect negative results', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2BIN(7777777042, 1)'], - ['=OCT2BIN(7777777042, 10)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1000100010') - expect(engine.getCellValue(adr('A2'))).toEqual('1000100010') - }) - - it('should fail if the result is longer than the desired length', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2BIN(12123, 2)'], - ['=OCT2BIN(34141, "3")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLarge)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLarge)) - }) - - it('should allow for numbers from 1 to 10 as second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2BIN(2, 0)'], - ['=OCT2BIN(2, 12)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLong)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-oct2dec.spec.ts b/test/unit/interpreter/function-oct2dec.spec.ts deleted file mode 100644 index fb3a69ba40..0000000000 --- a/test/unit/interpreter/function-oct2dec.spec.ts +++ /dev/null @@ -1,85 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function OCT2DEC', () => { - it('should return error when wrong number of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2DEC("foo", 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for non-oct arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2DEC("foo")'], - ['=OCT2DEC(418)'], - ['=OCT2DEC(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotOctal)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotOctal)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotOctal)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2DEC(1)'], - ['=OCT2DEC(10)'], - ['=OCT2DEC(71)'], - ['=OCT2DEC(12345)'], - ['=OCT2DEC(4242565)'], - ['=OCT2DEC(1234567654)'], - ['=OCT2DEC(7777777000)'], - ['=OCT2DEC(7777777042)'], - ['=OCT2DEC(7777777777)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(8) - expect(engine.getCellValue(adr('A3'))).toEqual(57) - expect(engine.getCellValue(adr('A4'))).toEqual(5349) - expect(engine.getCellValue(adr('A5'))).toEqual(1131893) - expect(engine.getCellValue(adr('A6'))).toEqual(175304620) - expect(engine.getCellValue(adr('A7'))).toEqual(-512) - expect(engine.getCellValue(adr('A8'))).toEqual(-478) - expect(engine.getCellValue(adr('A9'))).toEqual(-1) - }) - - it('should work for strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2DEC("456")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(302) - }) - - it('should work for reference', () => { - const engine = HyperFormula.buildFromArray([ - ['="123"'], - ['=OCT2DEC(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(83) - }) - - it('should return a number', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2DEC(11)'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.NUMBER) - }) - - it('should work only for 10 digits', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2DEC(10107040205)'], - ['=OCT2DEC(7777777042)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotOctal)) - expect(engine.getCellValue(adr('A2'))).toEqual(-478) - }) -}) diff --git a/test/unit/interpreter/function-oct2hex.spec.ts b/test/unit/interpreter/function-oct2hex.spec.ts deleted file mode 100644 index 26c193d90c..0000000000 --- a/test/unit/interpreter/function-oct2hex.spec.ts +++ /dev/null @@ -1,125 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('function OCT2HEX', () => { - it('should return error when wrong number of argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2HEX("foo", 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for non-oct arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2HEX("foo")'], - ['=OCT2HEX(418)'], - ['=OCT2HEX(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotOctal)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotOctal)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotOctal)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2HEX(1)'], - ['=OCT2HEX(10)'], - ['=OCT2HEX(71)'], - ['=OCT2HEX(12345)'], - ['=OCT2HEX(4242565)'], - ['=OCT2HEX(1234567654)'], - ['=OCT2HEX(7777777000)'], - ['=OCT2HEX(7777777042)'], - ['=OCT2HEX(7777777777)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - expect(engine.getCellValue(adr('A2'))).toEqual('8') - expect(engine.getCellValue(adr('A3'))).toEqual('39') - expect(engine.getCellValue(adr('A4'))).toEqual('14E5') - expect(engine.getCellValue(adr('A5'))).toEqual('114575') - expect(engine.getCellValue(adr('A6'))).toEqual('A72EFAC') - expect(engine.getCellValue(adr('A7'))).toEqual('FFFFFFFE00') - expect(engine.getCellValue(adr('A8'))).toEqual('FFFFFFFE22') - expect(engine.getCellValue(adr('A9'))).toEqual('FFFFFFFFFF') - }) - - it('should work for strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2HEX("456")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('12E') - }) - - it('should work for reference', () => { - const engine = HyperFormula.buildFromArray([ - ['="123"'], - ['=OCT2HEX(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual('53') - }) - - it('should return string value', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2HEX(11)'], - ]) - - expect(engine.getCellValueType(adr('A1'))).toBe(CellValueType.STRING) - }) - - it('should work only for 10 digits', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2HEX(31030220101)'], - ['=OCT2HEX(7777777042)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NotOctal)) - expect(engine.getCellValue(adr('A2'))).toEqual('FFFFFFFE22') - }) - - it('should respect second argument and fill with zeros for positive arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2HEX(12, 8)'], - ['=OCT2HEX(3, "4")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('0000000A') - expect(engine.getCellValue(adr('A2'))).toEqual('0003') - }) - - it('should fail if the result is longer than the desired length', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2HEX(12123, 2)'], - ['=OCT2HEX(34141, "3")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLong)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLong)) - }) - - it('second argument should not affect negative results', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2HEX(7777777042, 1)'], - ['=OCT2HEX(7777777022, 10)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('FFFFFFFE22') - expect(engine.getCellValue(adr('A2'))).toEqual('FFFFFFFE12') - }) - - it('should allow for numbers from 1 to 10 as second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=OCT2HEX(2, 0)'], - ['=OCT2HEX(2, 12)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueBaseLong)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-odd.spec.ts b/test/unit/interpreter/function-odd.spec.ts deleted file mode 100644 index 774b79102e..0000000000 --- a/test/unit/interpreter/function-odd.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ODD', () => { - it('number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ODD()', '=ODD(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works for positive numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=ODD(1.3)', '=ODD(2.7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(3) - expect(engine.getCellValue(adr('B1'))).toBe(3) - }) - - it('works for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=ODD(-1.3)', '=ODD(-2.7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(-3) - expect(engine.getCellValue(adr('B1'))).toBe(-3) - }) - - it('use coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['=ODD("42.3")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(43) - }) - - it('propagates error', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/0'], - ['=ODD(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-or.spec.ts b/test/unit/interpreter/function-or.spec.ts deleted file mode 100644 index 4fb3341ca6..0000000000 --- a/test/unit/interpreter/function-or.spec.ts +++ /dev/null @@ -1,98 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function OR', () => { - it('usage', () => { - const engine = HyperFormula.buildFromArray([ - ['=OR(TRUE())', '=OR(FALSE())', '=OR(FALSE(), TRUE(), FALSE())', '=OR("asdf")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('use coercion #1', () => { - const engine = HyperFormula.buildFromArray([ - ['=OR("TRUE", 0)'], - ['=OR("foo", 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('use coercion #2', () => { - const engine = HyperFormula.buildFromArray([ - ['=OR(A4:B4)'], - ['=OR(C4:D4)'], - ['=OR(C4:D4, "foo")'], - ['TRUE', 1, 'foo', '=TRUE()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('A2'))).toBe(true) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('function OR with numerical arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=OR(1)', '=OR(0)', '=OR(FALSE(), 42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('C1'))).toBe(true) - }) - - it('function OR takes at least one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=OR()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('if error in range found, returns first one in row-by-row order', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '=4/0'], - ['=FOOBAR()', '1'], - ['=OR(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('works with ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '0'], - ['0', '1'], - ['=OR(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(true) - }) - - it('is computed eagerly', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=4/0'], - ['0', '1'], - ['=OR(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('when called with a range, ignores strings other than "true", "false" and ""', () => { - const engine = HyperFormula.buildFromArray([ - ['foo', '=TRUE()', '=OR(A1:B1)'], - ['foo', '=FALSE()', '=OR(A2:B2)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(true) - expect(engine.getCellValue(adr('C2'))).toEqual(false) - }) -}) diff --git a/test/unit/interpreter/function-pduration.spec.ts b/test/unit/interpreter/function-pduration.spec.ts deleted file mode 100644 index 657fa1df9d..0000000000 --- a/test/unit/interpreter/function-pduration.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function PDURATION', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=PDURATION(1,1)', '=PDURATION(1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=PDURATION(2%, 12, 24)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(35.00278878, 6) - }) - - it('should return proper error', () => { - const engine = HyperFormula.buildFromArray([ - ['=PDURATION(-1, 12, 24)'], - ['=PDURATION(2%, -12, -24)'], - ['=PDURATION(0, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-phi.spec.ts b/test/unit/interpreter/function-phi.spec.ts deleted file mode 100644 index 7998c14641..0000000000 --- a/test/unit/interpreter/function-phi.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function PHI', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=PHI()'], - ['=PHI(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=PHI("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=PHI(-0.5)'], - ['=PHI(0)'], - ['=PHI(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.3520653267643, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.398942280401433, 6) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.241970724519143, 6) - }) -}) diff --git a/test/unit/interpreter/function-pi.spec.ts b/test/unit/interpreter/function-pi.spec.ts deleted file mode 100644 index 5511347783..0000000000 --- a/test/unit/interpreter/function-pi.spec.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function PI', () => { - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=PI(1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - it('should return PI with proper precision', () => { - const engine = HyperFormula.buildFromArray([ - ['=PI()'], - ], {smartRounding: false}) - - expect(engine.getCellValue(adr('A1'))).toEqual(3.14159265358979) - }) -}) diff --git a/test/unit/interpreter/function-pmt.spec.ts b/test/unit/interpreter/function-pmt.spec.ts deleted file mode 100644 index cc919ba94e..0000000000 --- a/test/unit/interpreter/function-pmt.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function PMT', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=PMT(1,1)', '=PMT(1, 1, 1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=PMT(1%, 360, 10000)', '=PMT(1%, 360, 10000, 1000)', '=PMT(1%, 360, 10000, 1000, 1)'], - ['=PMT(0, 360, 10000)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-102.86125969255) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-103.147385661805) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(-102.126124417629) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(-27.777777777777) - }) - - it('should be possible to implement a mortgage calculator', () => { - // Create a HyperFormula instance - const hf = HyperFormula.buildEmpty({ licenseKey: 'gpl-v3' }) - - // Add an empty sheet - const sheetName = hf.addSheet('Mortgage Calculator') - const sheetId = hf.getSheetId(sheetName)! - - // Enter the mortgage parameters - hf.addNamedExpression('AnnualInterestRate', '8%') - hf.addNamedExpression('NumberOfMonths', 10) - hf.addNamedExpression('LoanAmount', 10000) - - // Use the PMT function to calculate the monthly payment - hf.setCellContents({ sheet: sheetId, row: 0, col: 0 }, [['Monthly Payment', '=PMT(AnnualInterestRate/12, NumberOfMonths, -LoanAmount)']]) - - // Display the result - expect(hf.getCellValue({ sheet: sheetId, row: 0, col: 1 })).toBeCloseTo(1037.03) - }) -}) diff --git a/test/unit/interpreter/function-poisson.dist.spec.ts b/test/unit/interpreter/function-poisson.dist.spec.ts deleted file mode 100644 index e85a1eb8a4..0000000000 --- a/test/unit/interpreter/function-poisson.dist.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function POISSON.DIST', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=POISSON.DIST(1, 2)'], - ['=POISSON.DIST(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=POISSON.DIST("foo", 2, TRUE())'], - ['=POISSON.DIST(1, "baz", TRUE())'], - ['=POISSON.DIST(1, 2, "BCD")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=POISSON.DIST(10, 1, TRUE())'], - ['=POISSON.DIST(5, 2, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.999999989952234, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.983436391519386, 6) - }) - - it('should work as pdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=POISSON.DIST(10, 1, FALSE())'], - ['=POISSON.DIST(5, 2, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1')) as number / 1.0137771196303e-7).toBeCloseTo(1, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0360894088630967, 6) - }) - - it('should truncate first arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=POISSON.DIST(10.9, 1, TRUE())'], - ['=POISSON.DIST(5.9, 2, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.999999989952234, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.983436391519386, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=POISSON.DIST(0, 0, FALSE())'], - ['=POISSON.DIST(-0.01, 0, FALSE())'], - ['=POISSON.DIST(0, -0.01, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - //product #1 returns value - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - //product #2 returns value - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-power.spec.ts b/test/unit/interpreter/function-power.spec.ts deleted file mode 100644 index 93f5ece844..0000000000 --- a/test/unit/interpreter/function-power.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function POWER', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=POWER(101)'], - ['=POWER(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=POWER(1, "foo")'], - ['=POWER("bar", 4)'], - ['=POWER("foo", "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return 1 for 0^0', () => { - const engine = HyperFormula.buildFromArray([ - ['=POWER(0, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('should return error for 0^N where N<0', () => { - const engine = HyperFormula.buildFromArray([ - ['=POWER(0, -2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('should return error when result too large or too small', () => { - const engine = HyperFormula.buildFromArray([ - ['=POWER(2, 1023)'], - ['=POWER(2, 1024)'], - ['=POWER(-2, 1023)'], - ['=POWER(-2, 1024)'], - ], {smartRounding: false}) - - expect(engine.getCellValue(adr('A1'))).toEqual(8.98846567431158e+307) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expect(engine.getCellValue(adr('A3'))).toEqual(-8.98846567431158e+307) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=POWER(0, 1)'], - ['=POWER(2, 0)'], - ['=POWER(2.4, 2.5)'], - ['=POWER(3, -2.5)'], - ], {smartRounding: false}) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(8.923353629661888) - expect(engine.getCellValue(adr('A4'))).toEqual(0.06415002990995841) - }) -}) diff --git a/test/unit/interpreter/function-ppmt.spec.ts b/test/unit/interpreter/function-ppmt.spec.ts deleted file mode 100644 index 177e29c04b..0000000000 --- a/test/unit/interpreter/function-ppmt.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function PPMT', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=PPMT(1,1)', '=PPMT(1, 1, 1, 1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=PPMT(1%, 1, 360, 10000)', '=PPMT(1%, 1, 360, 10000, 3000)', '=PPMT(1%, 1, 360, 10000, 3000, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-2.86125969255043) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-3.71963760031556) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(-102.692710495362) - }) -}) diff --git a/test/unit/interpreter/function-product.spec.ts b/test/unit/interpreter/function-product.spec.ts deleted file mode 100644 index 21152e71e9..0000000000 --- a/test/unit/interpreter/function-product.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function PRODUCT', () => { - it('should take at least one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=PRODUCT()'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate product', () => { - const engine = HyperFormula.buildFromArray([ - ['=PRODUCT(2, 3)'], - ['=PRODUCT(B2:D2, E2, F2)', 2, 3, 'foo', 'bar', 4], - ['=PRODUCT(5, "foo")'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(6) - expect(engine.getCellValue(adr('A2'))).toEqual(24) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) -}) diff --git a/test/unit/interpreter/function-proper.spec.ts b/test/unit/interpreter/function-proper.spec.ts deleted file mode 100644 index cb810c27c4..0000000000 --- a/test/unit/interpreter/function-proper.spec.ts +++ /dev/null @@ -1,59 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function PROPER', () => { - it('should return N/A when number of arguments is incorrect', () => { - const engine = HyperFormula.buildFromArray([ - ['=PROPER()'], - ['=PROPER("foo", "bar")'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=PROPER("foo")'], - ['=PROPER("foo bar")'], - ['=PROPER(" foo bar ")'], - ['=PROPER("fOo BAR")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('Foo') - expect(engine.getCellValue(adr('A2'))).toEqual('Foo Bar') - expect(engine.getCellValue(adr('A3'))).toEqual(' Foo Bar ') - expect(engine.getCellValue(adr('A4'))).toEqual('Foo Bar') - }) - - it('should work with punctuation marks and numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=PROPER("123aa123bb.cc.dd")'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('123Aa123Bb.Cc.Dd') - }) - - it('should work with accents', () => { - const engine = HyperFormula.buildFromArray([ - ['=PROPER("MAI ANH ĐỨC")'], - ['=PROPER("MAI CHÍ THỌ")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('Mai Anh Đức') - expect(engine.getCellValue(adr('A2'))).toEqual('Mai Chí Thọ') - }) - - it('should coerce other types to string', () => { - const engine = HyperFormula.buildFromArray([ - ['=PROPER(1)'], - ['=PROPER(5+5)'], - ['=PROPER(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - expect(engine.getCellValue(adr('A2'))).toEqual('10') - expect(engine.getCellValue(adr('A3'))).toEqual('True') - }) -}) diff --git a/test/unit/interpreter/function-pv.spec.ts b/test/unit/interpreter/function-pv.spec.ts deleted file mode 100644 index cafc26d3da..0000000000 --- a/test/unit/interpreter/function-pv.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function PV', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=PV(1,1)', '=PV(1, 1, 1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=PV(2%, 24, 100)', '=PV(2%, 24, 100, 400)', '=PV(2%, 24, 100, 400, 1)'], - ['=PV(-99%, 24, 100)', '=PV(-1, 24, 100, 400)', '=PV(-2, 24, 100, 400, 1)'], - ['=PV(0, 24, 100)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-1891.39256, 6) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-2140.081155, 5) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(-2177.909007, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(-1.01010101010099e+50, -39) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('C2'))).toBeCloseTo(-400, 6) - expect(engine.getCellValue(adr('A3'))).toEqual(-2400) - }) - - it('should return error when args are incorrect', () => { - const engine = HyperFormula.buildFromArray([ - ['=PV(-1, 0, 100, 400)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM)) - }) -}) diff --git a/test/unit/interpreter/function-quotient.spec.ts b/test/unit/interpreter/function-quotient.spec.ts deleted file mode 100644 index 1966994fd0..0000000000 --- a/test/unit/interpreter/function-quotient.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function QUOTIENT', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=QUOTIENT(101)'], - ['=QUOTIENT(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=QUOTIENT(1, "foo")'], - ['=QUOTIENT("bar", 4)'], - ['=QUOTIENT("foo", "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return error when dividing by 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=QUOTIENT(42, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=QUOTIENT(5, 2)'], - ['=QUOTIENT(36, 6.1)'], - ['=QUOTIENT(10.5, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(engine.getCellValue(adr('A2'))).toEqual(5) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - }) - - it('should work for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=QUOTIENT(-5, 2)'], - ['=QUOTIENT(5, -2)'], - ['=QUOTIENT(-5, -2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(-2) - expect(engine.getCellValue(adr('A2'))).toEqual(-2) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - }) -}) diff --git a/test/unit/interpreter/function-radians.spec.ts b/test/unit/interpreter/function-radians.spec.ts deleted file mode 100644 index bbade02daf..0000000000 --- a/test/unit/interpreter/function-radians.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function RADIANS', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([ - ['=RADIANS(0)', '=RADIANS(180.0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(3.1415) - }) - - it('given wrong argument type', () => { - const engine = HyperFormula.buildFromArray([ - ['=RADIANS("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="180"', '=RADIANS(A1)'], - ['=TRUE()', '=RADIANS(A2)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(3.1415) - expect(engine.getCellValue(adr('B2'))).toBeCloseTo(0.017453292519943295) - }) - - it('given wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=RADIANS()'], - ['=RADIANS(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=RADIANS(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-rand.spec.ts b/test/unit/interpreter/function-rand.spec.ts deleted file mode 100644 index bc429d60fb..0000000000 --- a/test/unit/interpreter/function-rand.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Interpreter - function RAND', () => { - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['=RAND()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeGreaterThanOrEqual(0.0) - expect(engine.getCellValue(adr('A1'))).toBeLessThan(1.0) - }) - - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=RAND(42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) -}) diff --git a/test/unit/interpreter/function-randbetween.spec.ts b/test/unit/interpreter/function-randbetween.spec.ts deleted file mode 100644 index 3726a7e5ff..0000000000 --- a/test/unit/interpreter/function-randbetween.spec.ts +++ /dev/null @@ -1,88 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Interpreter - function RANDBETWEEN', () => { - it('works with regular input', () => { - const arr: number[] = Array(10).fill(0) - for (let i = 0; i < 100; i++) { - const engine = HyperFormula.buildFromArray([ - ['=RANDBETWEEN(0,9)'], - ]) - const val = engine.getCellValue(adr('A1')) as number - expect(val).toBeGreaterThanOrEqual(0) - expect(val).toBeLessThanOrEqual(9) - expect(val).toEqual(Math.trunc(val)) - arr[val] += 1 - } - for (const val of arr) { - expect(val).toBeGreaterThan(0) // 10*9^100/10^100 ~= 0.0003 chance of failure if truly random - } - }) - - it('rounds arguments', () => { - const arr: number[] = Array(10).fill(0) - for (let i = 0; i < 100; i++) { - const engine = HyperFormula.buildFromArray([ - ['=RANDBETWEEN(-0.1,9.9)'], - ]) - const val = engine.getCellValue(adr('A1')) as number - expect(val).toBeGreaterThanOrEqual(0) - expect(val).toBeLessThanOrEqual(9) - expect(val).toEqual(Math.trunc(val)) - arr[val] += 1 - } - for (const val of arr) { - expect(val).toBeGreaterThan(0) - } - }) - - it('should work for short intervals', () => { - for (let i = 0; i < 10; i++) { - const engine = HyperFormula.buildFromArray([ - ['=RANDBETWEEN(0,0.5)'], - ]) - const val = engine.getCellValue(adr('A1')) as number - expect(val).toEqual(0) - } - }) - - it('should work for short intervals #2', () => { - for (let i = 0; i < 10; i++) { - const engine = HyperFormula.buildFromArray([ - ['=RANDBETWEEN(0.5,1)'], - ]) - const val = engine.getCellValue(adr('A1')) as number - expect(val).toEqual(1) - } - }) - - it('should work for short intervals #3', () => { - for (let i = 0; i < 10; i++) { - const engine = HyperFormula.buildFromArray([ - ['=RANDBETWEEN(0.5,0.6)'], - ]) - const val = engine.getCellValue(adr('A1')) as number - expect(val).toEqual(1) - } - }) - - it('validates bounds on arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=RANDBETWEEN(0.7,0.5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.WrongOrder)) - }) - - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=RANDBETWEEN(42)'], - ['=RANDBETWEEN(1,2,3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) -}) diff --git a/test/unit/interpreter/function-rate.spec.ts b/test/unit/interpreter/function-rate.spec.ts deleted file mode 100644 index 39f542cd69..0000000000 --- a/test/unit/interpreter/function-rate.spec.ts +++ /dev/null @@ -1,233 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function RATE', () => { - const requiredFinancialPrecision = 6 // epsilon = 0.0000005 - - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(1,1)', '=RATE(1, 1, 1, 1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return #NUM if algorithm does not converge', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(12, -100, 100)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM)) - }) - - it('should return #VALUE if guess param is invalid', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(12, -100, 400, 0, 0, -1)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE)) - }) - - it('should set the value type to percent', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(12, -100, 400)', ], - ]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - }) - - describe('should compute the correct result when type = 0 for', () => { - it('(12, -100, 400)', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(12, -100, 400)', ], - ['=RATE(12, -100, 400, 0, 0)', ], - ['=RATE(12, -100, 400, 100, 0)', ], - ['=RATE(12, -100, 400, -100, 0)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.228933, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.228933, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.222595, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(0.234770, requiredFinancialPrecision) - }) - - it('(12, 100, -400)', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(12, 100, -400)', ], - ['=RATE(12, 100, -400, 0, 0)', ], - ['=RATE(12, 100, -400, 100, 0)', ], - ['=RATE(12, 100, -400, -100, 0)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.228933, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.228933, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.234770, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(0.222595, requiredFinancialPrecision) - }) - - it('(12, 100, 400)', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(12, 100, 400, -2000, 0)', ], - ['=RATE(12, 100, 400, -1000, 0)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.030711, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(-0.069686, requiredFinancialPrecision) - }) - - it('(0.9, -100, 400)', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(0.9, -100, 400)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-0.796172, requiredFinancialPrecision) - }) - - it('(300, -465.96, 100000)', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(300, -465.96, 100000)'], - ['=RATE(300, -465.96, 100000, 0, 0)'], - ['=RATE(300, -465.96, 100000, 0, 0, 0.008)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.002367, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.002367, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.002367, requiredFinancialPrecision) - }) - - it('(200, -500, 200000)', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(200, -500, 200000)'], - ['=RATE(200, -500, 200000, 0, 0)'], - ['=RATE(200, -500, 200000, 0, 0, -0.001)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-0.006237, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(-0.006237, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(-0.006237, requiredFinancialPrecision) - }) - }) - - describe('should compute the correct result when type = 1 for', () => { - it('(12, -100, 400)', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(12, -100, 400, 100, 1)', ], - ['=RATE(12, -100, 400, 1, 1)', ], - ['=RATE(12, -100, 400, 0, 1)', ], - ['=RATE(12, -100, 400, 0, 1, 0.317)', ], - ['=RATE(12, -100, 400, -100, 1)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-0.499693, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(-0.990099, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(-1.000000, requiredFinancialPrecision) // noted in known-limitations - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(0.3172435, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM)) - }) - - it('(12, -100, 600)', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(12, -100, 600, 0, 1)', ], - ['=RATE(12, -100, 600, 100, 1)', ], - ['=RATE(12, -100, 600, -100, 1)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.161450, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.152452, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.169426, requiredFinancialPrecision) - }) - - it('(12, 100, -600)', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(12, 100, -600, 0, 1)', ], - ['=RATE(12, 100, -600, 100, 1)', ], - ['=RATE(12, 100, -600, -100, 1)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.161450, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.169426, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.152452, requiredFinancialPrecision) - }) - - it('(12, 100, 400)', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(12, 100, 400, -2000, 1)', ], - ['=RATE(12, 100, 400, -1000, 1)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.028023, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(-0.061540, requiredFinancialPrecision) - }) - }) - - describe('should compute the correct result when guess is provided for', () => { - it('(12, -100, 400)', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(12, -100, 400, 0, 0, 0.23)', ], - ['=RATE(12, -100, 400, 100, 0, 0.22)', ], - ['=RATE(12, -100, 400, -100, 0, 0.23)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.228933, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.222595, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.234770, requiredFinancialPrecision) - }) - - it('(12, -100, 600)', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(12, -100, 600, 0, 1, 0.16)', ], - ['=RATE(12, -100, 600, 100, 1, 0.1)', ], - ['=RATE(12, -100, 600, -100, 1, 0.2)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.161450, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.152452, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.169426, requiredFinancialPrecision) - }) - - it('(12, 100, -400)', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(12, 100, -400, 0, 0, 0.23)', ], - ['=RATE(12, 100, -400, 100, 0, 0.23)', ], - ['=RATE(12, 100, -400, -100, 0, 0.22)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.228933, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.234770, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.222595, requiredFinancialPrecision) - }) - - it('(12, 100, 400)', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(12, 100, 400, -2000, 0, 0.03)', ], - ['=RATE(12, 100, 400, -1000, 0, -0.07)', ], - ['=RATE(12, 100, 400, -2000, 1, 0.01)', ], - ['=RATE(12, 100, 400, -1000, 1, -0.01)', ], - ['=RATE(12, 100, 400, -1000, 1, -0.0000001)', ], - ['=RATE(12, 100, 400, -1000, 1, -0.00000001)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.030711, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(-0.069686, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.028023, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(-0.061540, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(-0.061540, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(-0.061540, requiredFinancialPrecision) - }) - - it('(0.9, -100, 400)', () => { - const engine = HyperFormula.buildFromArray([ - ['=RATE(0.9, -100, 400, 0, 0, -0.8)', ], - ['=RATE(0.9, -100, 400, 0, 0, -0.0000001)', ], - ['=RATE(0.9, -100, 400, 0, 0, -0.00000001)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-0.796172, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(-0.796172, requiredFinancialPrecision) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(-0.796172, requiredFinancialPrecision) - }) - }) -}) diff --git a/test/unit/interpreter/function-replace.spec.ts b/test/unit/interpreter/function-replace.spec.ts deleted file mode 100644 index fba875840c..0000000000 --- a/test/unit/interpreter/function-replace.spec.ts +++ /dev/null @@ -1,79 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function REPLACE', () => { - it('should take 4 parameters', () => { - const engine = HyperFormula.buildFromArray([ - ['=REPLACE("foobar", 1, 2)', ], - ['=REPLACE("foobar", 1, 2, "baz", 3)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should replace characters in text based on given position and number of chars', () => { - const engine = HyperFormula.buildFromArray([ - ['=REPLACE("foobar", 2, 2, "uu")', ], - ['=REPLACE("foobar", 2, 10, "uu")', ], - ['=REPLACE("foobar", 3, 2, "uuuu")', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('fuubar') - expect(engine.getCellValue(adr('A2'))).toEqual('fuu') - expect(engine.getCellValue(adr('A3'))).toEqual('fouuuuar') - }) - - it('should insert text before position if number of chars is 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=REPLACE("foobar", 4, 0, "uu")', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('foouubar') - }) - - it('should append new text if start position is greater than text length', () => { - const engine = HyperFormula.buildFromArray([ - ['=REPLACE("foobar", 7, 15, "uu")', ], - ['=REPLACE("foobar", 28, 0, "uu")', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('foobaruu') - expect(engine.getCellValue(adr('A2'))).toEqual('foobaruu') - }) - - it('should coerce', () => { - const engine = HyperFormula.buildFromArray([ - ['=REPLACE("foobar", 1, 3, TRUE())', ], - ['=REPLACE(12345, 3, 2, 123)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('TRUEbar') - expect(engine.getCellValue(adr('A2'))).toEqual('121235') - }) - - it('should return #VALUE! if parameters out of range', () => { - const engine = HyperFormula.buildFromArray([ - ['=REPLACE("foobar", 0, 2, TRUE())', ], - ['=REPLACE("foobar", 1, -1, "uu")', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NegativeLength)) - }) - - it('should return value when arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=REPLACE("foobar", "o", 1, "bar")'], - ['=REPLACE("foobar", 1, "f", "bar")'], - ['=REPLACE(B1:B2, 1, 2, "bar")'], - ['=REPLACE("foobar", 1, 2, B1:B2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-rept.spec.ts b/test/unit/interpreter/function-rept.spec.ts deleted file mode 100644 index 1c72d0bc07..0000000000 --- a/test/unit/interpreter/function-rept.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function REPT', () => { - it('should return N/A when number of arguments is incorrect', () => { - const engine = HyperFormula.buildFromArray([ - ['=REPT()'], - ['=REPT("foo")'], - ['=REPT("foo", 1, 2)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return VALUE when wrong type of second parameter', () => { - const engine = HyperFormula.buildFromArray([ - ['=REPT("foo", "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return VALUE when second parameter is less than 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=REPT("foo", -1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NegativeCount)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=REPT("foo", 0)'], - ['=REPT("foo", 3)'], - ['=REPT(1, 5)'], - ['=REPT(, 5)'], - ['=REPT("Na", 7)&" Batman!"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('') - expect(engine.getCellValue(adr('A2'))).toEqual('foofoofoo') - expect(engine.getCellValue(adr('A3'))).toEqual('11111') - expect(engine.getCellValue(adr('A4'))).toEqual('') - expect(engine.getCellValue(adr('A5'))).toEqual('NaNaNaNaNaNaNa Batman!') - }) - - it('should coerce other types to string', () => { - const engine = HyperFormula.buildFromArray([ - ['=REPT(1, 1)'], - ['=REPT(5+5, 1)'], - ['=REPT(TRUE(), 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - expect(engine.getCellValue(adr('A2'))).toEqual('10') - expect(engine.getCellValue(adr('A3'))).toEqual('TRUE') - }) -}) diff --git a/test/unit/interpreter/function-right.spec.ts b/test/unit/interpreter/function-right.spec.ts deleted file mode 100644 index 9a64e4e492..0000000000 --- a/test/unit/interpreter/function-right.spec.ts +++ /dev/null @@ -1,77 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function RIGHT', () => { - it('should return N/A when number of arguments is incorrect', () => { - const engine = HyperFormula.buildFromArray([ - ['=RIGHT()'], - ['=RIGHT("foo", 1, 2)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return VALUE when wrong type of second parameter', () => { - const engine = HyperFormula.buildFromArray([ - ['=RIGHT("foo", "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work with empty argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=RIGHT(, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('') - }) - - it('should return one character by default', () => { - const engine = HyperFormula.buildFromArray([ - ['=RIGHT("bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('r') - }) - - it('should return VALUE when second parameter is less than 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=RIGHT("foo", -1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NegativeLength)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=RIGHT("", 4)'], - ['=RIGHT("bar", 0)'], - ['=RIGHT("bar", 1)'], - ['=RIGHT("bar", 3)'], - ['=RIGHT("bar", 4)'], - ['=RIGHT(123, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('') - expect(engine.getCellValue(adr('A2'))).toEqual('') - expect(engine.getCellValue(adr('A3'))).toEqual('r') - expect(engine.getCellValue(adr('A4'))).toEqual('bar') - expect(engine.getCellValue(adr('A5'))).toEqual('bar') - expect(engine.getCellValue(adr('A6'))).toEqual('23') - }) - - it('should coerce other types to string', () => { - const engine = HyperFormula.buildFromArray([ - ['=RIGHT(1, 1)'], - ['=RIGHT(5+5, 1)'], - ['=RIGHT(TRUE(), 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - expect(engine.getCellValue(adr('A2'))).toEqual('0') - expect(engine.getCellValue(adr('A3'))).toEqual('E') - }) -}) diff --git a/test/unit/interpreter/function-roman.spec.ts b/test/unit/interpreter/function-roman.spec.ts deleted file mode 100644 index d0ea814761..0000000000 --- a/test/unit/interpreter/function-roman.spec.ts +++ /dev/null @@ -1,85 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ROMAN', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROMAN()', '=ROMAN(1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should properly truncate values and use defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROMAN(499)'], - ['=ROMAN(499, TRUE())'], - ['=ROMAN(499, FALSE())'], - ['=ROMAN(499.9)'], - ['=ROMAN(499, 1.1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual('CDXCIX') - expect(engine.getCellValue(adr('A2'))).toEqual('CDXCIX') - expect(engine.getCellValue(adr('A3'))).toEqual('ID') - expect(engine.getCellValue(adr('A4'))).toEqual('CDXCIX') - expect(engine.getCellValue(adr('A5'))).toEqual('LDVLIV') - }) - - it('should throw correct error if arguments are out of bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROMAN(0)'], - ['=ROMAN(4000)'], - ['=ROMAN(-1)'], - ['=ROMAN(1, "a")'], - ['=ROMAN(1, 5)'], - ['=ROMAN(1, -1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ValueSmall)) - }) - - it('should output correct value for mode 0', () => { - const engine = HyperFormula.buildFromArray([input(0)]) - expect(engine.getSheetValues(0)).toEqual([mode0]) - }) - - it('should output correct value for mode 1', () => { - const engine = HyperFormula.buildFromArray([input(1)]) - expect(engine.getSheetValues(0)).toEqual([mode1]) - }) - - it('should output correct value for mode 2', () => { - const engine = HyperFormula.buildFromArray([input(2)]) - expect(engine.getSheetValues(0)).toEqual([mode2]) - }) - - it('should output correct value for mode 3', () => { - const engine = HyperFormula.buildFromArray([input(3)]) - expect(engine.getSheetValues(0)).toEqual([mode3]) - }) - - it('should output correct value for mode 4', () => { - const engine = HyperFormula.buildFromArray([input(4)]) - expect(engine.getSheetValues(0)).toEqual([mode4]) - }) -}) - -function input(mode: number) { - const ret = [] - for (let i = 1; i < 4000; i++) { - ret.push(`=ROMAN(${i},${mode})`) - } - return ret -} - -const mode0 = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX', 'XXI', 'XXII', 'XXIII', 'XXIV', 'XXV', 'XXVI', 'XXVII', 'XXVIII', 'XXIX', 'XXX', 'XXXI', 'XXXII', 'XXXIII', 'XXXIV', 'XXXV', 'XXXVI', 'XXXVII', 'XXXVIII', 'XXXIX', 'XL', 'XLI', 'XLII', 'XLIII', 'XLIV', 'XLV', 'XLVI', 'XLVII', 'XLVIII', 'XLIX', 'L', 'LI', 'LII', 'LIII', 'LIV', 'LV', 'LVI', 'LVII', 'LVIII', 'LIX', 'LX', 'LXI', 'LXII', 'LXIII', 'LXIV', 'LXV', 'LXVI', 'LXVII', 'LXVIII', 'LXIX', 'LXX', 'LXXI', 'LXXII', 'LXXIII', 'LXXIV', 'LXXV', 'LXXVI', 'LXXVII', 'LXXVIII', 'LXXIX', 'LXXX', 'LXXXI', 'LXXXII', 'LXXXIII', 'LXXXIV', 'LXXXV', 'LXXXVI', 'LXXXVII', 'LXXXVIII', 'LXXXIX', 'XC', 'XCI', 'XCII', 'XCIII', 'XCIV', 'XCV', 'XCVI', 'XCVII', 'XCVIII', 'XCIX', 'C', 'CI', 'CII', 'CIII', 'CIV', 'CV', 'CVI', 'CVII', 'CVIII', 'CIX', 'CX', 'CXI', 'CXII', 'CXIII', 'CXIV', 'CXV', 'CXVI', 'CXVII', 'CXVIII', 'CXIX', 'CXX', 'CXXI', 'CXXII', 'CXXIII', 'CXXIV', 'CXXV', 'CXXVI', 'CXXVII', 'CXXVIII', 'CXXIX', 'CXXX', 'CXXXI', 'CXXXII', 'CXXXIII', 'CXXXIV', 'CXXXV', 'CXXXVI', 'CXXXVII', 'CXXXVIII', 'CXXXIX', 'CXL', 'CXLI', 'CXLII', 'CXLIII', 'CXLIV', 'CXLV', 'CXLVI', 'CXLVII', 'CXLVIII', 'CXLIX', 'CL', 'CLI', 'CLII', 'CLIII', 'CLIV', 'CLV', 'CLVI', 'CLVII', 'CLVIII', 'CLIX', 'CLX', 'CLXI', 'CLXII', 'CLXIII', 'CLXIV', 'CLXV', 'CLXVI', 'CLXVII', 'CLXVIII', 'CLXIX', 'CLXX', 'CLXXI', 'CLXXII', 'CLXXIII', 'CLXXIV', 'CLXXV', 'CLXXVI', 'CLXXVII', 'CLXXVIII', 'CLXXIX', 'CLXXX', 'CLXXXI', 'CLXXXII', 'CLXXXIII', 'CLXXXIV', 'CLXXXV', 'CLXXXVI', 'CLXXXVII', 'CLXXXVIII', 'CLXXXIX', 'CXC', 'CXCI', 'CXCII', 'CXCIII', 'CXCIV', 'CXCV', 'CXCVI', 'CXCVII', 'CXCVIII', 'CXCIX', 'CC', 'CCI', 'CCII', 'CCIII', 'CCIV', 'CCV', 'CCVI', 'CCVII', 'CCVIII', 'CCIX', 'CCX', 'CCXI', 'CCXII', 'CCXIII', 'CCXIV', 'CCXV', 'CCXVI', 'CCXVII', 'CCXVIII', 'CCXIX', 'CCXX', 'CCXXI', 'CCXXII', 'CCXXIII', 'CCXXIV', 'CCXXV', 'CCXXVI', 'CCXXVII', 'CCXXVIII', 'CCXXIX', 'CCXXX', 'CCXXXI', 'CCXXXII', 'CCXXXIII', 'CCXXXIV', 'CCXXXV', 'CCXXXVI', 'CCXXXVII', 'CCXXXVIII', 'CCXXXIX', 'CCXL', 'CCXLI', 'CCXLII', 'CCXLIII', 'CCXLIV', 'CCXLV', 'CCXLVI', 'CCXLVII', 'CCXLVIII', 'CCXLIX', 'CCL', 'CCLI', 'CCLII', 'CCLIII', 'CCLIV', 'CCLV', 'CCLVI', 'CCLVII', 'CCLVIII', 'CCLIX', 'CCLX', 'CCLXI', 'CCLXII', 'CCLXIII', 'CCLXIV', 'CCLXV', 'CCLXVI', 'CCLXVII', 'CCLXVIII', 'CCLXIX', 'CCLXX', 'CCLXXI', 'CCLXXII', 'CCLXXIII', 'CCLXXIV', 'CCLXXV', 'CCLXXVI', 'CCLXXVII', 'CCLXXVIII', 'CCLXXIX', 'CCLXXX', 'CCLXXXI', 'CCLXXXII', 'CCLXXXIII', 'CCLXXXIV', 'CCLXXXV', 'CCLXXXVI', 'CCLXXXVII', 'CCLXXXVIII', 'CCLXXXIX', 'CCXC', 'CCXCI', 'CCXCII', 'CCXCIII', 'CCXCIV', 'CCXCV', 'CCXCVI', 'CCXCVII', 'CCXCVIII', 'CCXCIX', 'CCC', 'CCCI', 'CCCII', 'CCCIII', 'CCCIV', 'CCCV', 'CCCVI', 'CCCVII', 'CCCVIII', 'CCCIX', 'CCCX', 'CCCXI', 'CCCXII', 'CCCXIII', 'CCCXIV', 'CCCXV', 'CCCXVI', 'CCCXVII', 'CCCXVIII', 'CCCXIX', 'CCCXX', 'CCCXXI', 'CCCXXII', 'CCCXXIII', 'CCCXXIV', 'CCCXXV', 'CCCXXVI', 'CCCXXVII', 'CCCXXVIII', 'CCCXXIX', 'CCCXXX', 'CCCXXXI', 'CCCXXXII', 'CCCXXXIII', 'CCCXXXIV', 'CCCXXXV', 'CCCXXXVI', 'CCCXXXVII', 'CCCXXXVIII', 'CCCXXXIX', 'CCCXL', 'CCCXLI', 'CCCXLII', 'CCCXLIII', 'CCCXLIV', 'CCCXLV', 'CCCXLVI', 'CCCXLVII', 'CCCXLVIII', 'CCCXLIX', 'CCCL', 'CCCLI', 'CCCLII', 'CCCLIII', 'CCCLIV', 'CCCLV', 'CCCLVI', 'CCCLVII', 'CCCLVIII', 'CCCLIX', 'CCCLX', 'CCCLXI', 'CCCLXII', 'CCCLXIII', 'CCCLXIV', 'CCCLXV', 'CCCLXVI', 'CCCLXVII', 'CCCLXVIII', 'CCCLXIX', 'CCCLXX', 'CCCLXXI', 'CCCLXXII', 'CCCLXXIII', 'CCCLXXIV', 'CCCLXXV', 'CCCLXXVI', 'CCCLXXVII', 'CCCLXXVIII', 'CCCLXXIX', 'CCCLXXX', 'CCCLXXXI', 'CCCLXXXII', 'CCCLXXXIII', 'CCCLXXXIV', 'CCCLXXXV', 'CCCLXXXVI', 'CCCLXXXVII', 'CCCLXXXVIII', 'CCCLXXXIX', 'CCCXC', 'CCCXCI', 'CCCXCII', 'CCCXCIII', 'CCCXCIV', 'CCCXCV', 'CCCXCVI', 'CCCXCVII', 'CCCXCVIII', 'CCCXCIX', 'CD', 'CDI', 'CDII', 'CDIII', 'CDIV', 'CDV', 'CDVI', 'CDVII', 'CDVIII', 'CDIX', 'CDX', 'CDXI', 'CDXII', 'CDXIII', 'CDXIV', 'CDXV', 'CDXVI', 'CDXVII', 'CDXVIII', 'CDXIX', 'CDXX', 'CDXXI', 'CDXXII', 'CDXXIII', 'CDXXIV', 'CDXXV', 'CDXXVI', 'CDXXVII', 'CDXXVIII', 'CDXXIX', 'CDXXX', 'CDXXXI', 'CDXXXII', 'CDXXXIII', 'CDXXXIV', 'CDXXXV', 'CDXXXVI', 'CDXXXVII', 'CDXXXVIII', 'CDXXXIX', 'CDXL', 'CDXLI', 'CDXLII', 'CDXLIII', 'CDXLIV', 'CDXLV', 'CDXLVI', 'CDXLVII', 'CDXLVIII', 'CDXLIX', 'CDL', 'CDLI', 'CDLII', 'CDLIII', 'CDLIV', 'CDLV', 'CDLVI', 'CDLVII', 'CDLVIII', 'CDLIX', 'CDLX', 'CDLXI', 'CDLXII', 'CDLXIII', 'CDLXIV', 'CDLXV', 'CDLXVI', 'CDLXVII', 'CDLXVIII', 'CDLXIX', 'CDLXX', 'CDLXXI', 'CDLXXII', 'CDLXXIII', 'CDLXXIV', 'CDLXXV', 'CDLXXVI', 'CDLXXVII', 'CDLXXVIII', 'CDLXXIX', 'CDLXXX', 'CDLXXXI', 'CDLXXXII', 'CDLXXXIII', 'CDLXXXIV', 'CDLXXXV', 'CDLXXXVI', 'CDLXXXVII', 'CDLXXXVIII', 'CDLXXXIX', 'CDXC', 'CDXCI', 'CDXCII', 'CDXCIII', 'CDXCIV', 'CDXCV', 'CDXCVI', 'CDXCVII', 'CDXCVIII', 'CDXCIX', 'D', 'DI', 'DII', 'DIII', 'DIV', 'DV', 'DVI', 'DVII', 'DVIII', 'DIX', 'DX', 'DXI', 'DXII', 'DXIII', 'DXIV', 'DXV', 'DXVI', 'DXVII', 'DXVIII', 'DXIX', 'DXX', 'DXXI', 'DXXII', 'DXXIII', 'DXXIV', 'DXXV', 'DXXVI', 'DXXVII', 'DXXVIII', 'DXXIX', 'DXXX', 'DXXXI', 'DXXXII', 'DXXXIII', 'DXXXIV', 'DXXXV', 'DXXXVI', 'DXXXVII', 'DXXXVIII', 'DXXXIX', 'DXL', 'DXLI', 'DXLII', 'DXLIII', 'DXLIV', 'DXLV', 'DXLVI', 'DXLVII', 'DXLVIII', 'DXLIX', 'DL', 'DLI', 'DLII', 'DLIII', 'DLIV', 'DLV', 'DLVI', 'DLVII', 'DLVIII', 'DLIX', 'DLX', 'DLXI', 'DLXII', 'DLXIII', 'DLXIV', 'DLXV', 'DLXVI', 'DLXVII', 'DLXVIII', 'DLXIX', 'DLXX', 'DLXXI', 'DLXXII', 'DLXXIII', 'DLXXIV', 'DLXXV', 'DLXXVI', 'DLXXVII', 'DLXXVIII', 'DLXXIX', 'DLXXX', 'DLXXXI', 'DLXXXII', 'DLXXXIII', 'DLXXXIV', 'DLXXXV', 'DLXXXVI', 'DLXXXVII', 'DLXXXVIII', 'DLXXXIX', 'DXC', 'DXCI', 'DXCII', 'DXCIII', 'DXCIV', 'DXCV', 'DXCVI', 'DXCVII', 'DXCVIII', 'DXCIX', 'DC', 'DCI', 'DCII', 'DCIII', 'DCIV', 'DCV', 'DCVI', 'DCVII', 'DCVIII', 'DCIX', 'DCX', 'DCXI', 'DCXII', 'DCXIII', 'DCXIV', 'DCXV', 'DCXVI', 'DCXVII', 'DCXVIII', 'DCXIX', 'DCXX', 'DCXXI', 'DCXXII', 'DCXXIII', 'DCXXIV', 'DCXXV', 'DCXXVI', 'DCXXVII', 'DCXXVIII', 'DCXXIX', 'DCXXX', 'DCXXXI', 'DCXXXII', 'DCXXXIII', 'DCXXXIV', 'DCXXXV', 'DCXXXVI', 'DCXXXVII', 'DCXXXVIII', 'DCXXXIX', 'DCXL', 'DCXLI', 'DCXLII', 'DCXLIII', 'DCXLIV', 'DCXLV', 'DCXLVI', 'DCXLVII', 'DCXLVIII', 'DCXLIX', 'DCL', 'DCLI', 'DCLII', 'DCLIII', 'DCLIV', 'DCLV', 'DCLVI', 'DCLVII', 'DCLVIII', 'DCLIX', 'DCLX', 'DCLXI', 'DCLXII', 'DCLXIII', 'DCLXIV', 'DCLXV', 'DCLXVI', 'DCLXVII', 'DCLXVIII', 'DCLXIX', 'DCLXX', 'DCLXXI', 'DCLXXII', 'DCLXXIII', 'DCLXXIV', 'DCLXXV', 'DCLXXVI', 'DCLXXVII', 'DCLXXVIII', 'DCLXXIX', 'DCLXXX', 'DCLXXXI', 'DCLXXXII', 'DCLXXXIII', 'DCLXXXIV', 'DCLXXXV', 'DCLXXXVI', 'DCLXXXVII', 'DCLXXXVIII', 'DCLXXXIX', 'DCXC', 'DCXCI', 'DCXCII', 'DCXCIII', 'DCXCIV', 'DCXCV', 'DCXCVI', 'DCXCVII', 'DCXCVIII', 'DCXCIX', 'DCC', 'DCCI', 'DCCII', 'DCCIII', 'DCCIV', 'DCCV', 'DCCVI', 'DCCVII', 'DCCVIII', 'DCCIX', 'DCCX', 'DCCXI', 'DCCXII', 'DCCXIII', 'DCCXIV', 'DCCXV', 'DCCXVI', 'DCCXVII', 'DCCXVIII', 'DCCXIX', 'DCCXX', 'DCCXXI', 'DCCXXII', 'DCCXXIII', 'DCCXXIV', 'DCCXXV', 'DCCXXVI', 'DCCXXVII', 'DCCXXVIII', 'DCCXXIX', 'DCCXXX', 'DCCXXXI', 'DCCXXXII', 'DCCXXXIII', 'DCCXXXIV', 'DCCXXXV', 'DCCXXXVI', 'DCCXXXVII', 'DCCXXXVIII', 'DCCXXXIX', 'DCCXL', 'DCCXLI', 'DCCXLII', 'DCCXLIII', 'DCCXLIV', 'DCCXLV', 'DCCXLVI', 'DCCXLVII', 'DCCXLVIII', 'DCCXLIX', 'DCCL', 'DCCLI', 'DCCLII', 'DCCLIII', 'DCCLIV', 'DCCLV', 'DCCLVI', 'DCCLVII', 'DCCLVIII', 'DCCLIX', 'DCCLX', 'DCCLXI', 'DCCLXII', 'DCCLXIII', 'DCCLXIV', 'DCCLXV', 'DCCLXVI', 'DCCLXVII', 'DCCLXVIII', 'DCCLXIX', 'DCCLXX', 'DCCLXXI', 'DCCLXXII', 'DCCLXXIII', 'DCCLXXIV', 'DCCLXXV', 'DCCLXXVI', 'DCCLXXVII', 'DCCLXXVIII', 'DCCLXXIX', 'DCCLXXX', 'DCCLXXXI', 'DCCLXXXII', 'DCCLXXXIII', 'DCCLXXXIV', 'DCCLXXXV', 'DCCLXXXVI', 'DCCLXXXVII', 'DCCLXXXVIII', 'DCCLXXXIX', 'DCCXC', 'DCCXCI', 'DCCXCII', 'DCCXCIII', 'DCCXCIV', 'DCCXCV', 'DCCXCVI', 'DCCXCVII', 'DCCXCVIII', 'DCCXCIX', 'DCCC', 'DCCCI', 'DCCCII', 'DCCCIII', 'DCCCIV', 'DCCCV', 'DCCCVI', 'DCCCVII', 'DCCCVIII', 'DCCCIX', 'DCCCX', 'DCCCXI', 'DCCCXII', 'DCCCXIII', 'DCCCXIV', 'DCCCXV', 'DCCCXVI', 'DCCCXVII', 'DCCCXVIII', 'DCCCXIX', 'DCCCXX', 'DCCCXXI', 'DCCCXXII', 'DCCCXXIII', 'DCCCXXIV', 'DCCCXXV', 'DCCCXXVI', 'DCCCXXVII', 'DCCCXXVIII', 'DCCCXXIX', 'DCCCXXX', 'DCCCXXXI', 'DCCCXXXII', 'DCCCXXXIII', 'DCCCXXXIV', 'DCCCXXXV', 'DCCCXXXVI', 'DCCCXXXVII', 'DCCCXXXVIII', 'DCCCXXXIX', 'DCCCXL', 'DCCCXLI', 'DCCCXLII', 'DCCCXLIII', 'DCCCXLIV', 'DCCCXLV', 'DCCCXLVI', 'DCCCXLVII', 'DCCCXLVIII', 'DCCCXLIX', 'DCCCL', 'DCCCLI', 'DCCCLII', 'DCCCLIII', 'DCCCLIV', 'DCCCLV', 'DCCCLVI', 'DCCCLVII', 'DCCCLVIII', 'DCCCLIX', 'DCCCLX', 'DCCCLXI', 'DCCCLXII', 'DCCCLXIII', 'DCCCLXIV', 'DCCCLXV', 'DCCCLXVI', 'DCCCLXVII', 'DCCCLXVIII', 'DCCCLXIX', 'DCCCLXX', 'DCCCLXXI', 'DCCCLXXII', 'DCCCLXXIII', 'DCCCLXXIV', 'DCCCLXXV', 'DCCCLXXVI', 'DCCCLXXVII', 'DCCCLXXVIII', 'DCCCLXXIX', 'DCCCLXXX', 'DCCCLXXXI', 'DCCCLXXXII', 'DCCCLXXXIII', 'DCCCLXXXIV', 'DCCCLXXXV', 'DCCCLXXXVI', 'DCCCLXXXVII', 'DCCCLXXXVIII', 'DCCCLXXXIX', 'DCCCXC', 'DCCCXCI', 'DCCCXCII', 'DCCCXCIII', 'DCCCXCIV', 'DCCCXCV', 'DCCCXCVI', 'DCCCXCVII', 'DCCCXCVIII', 'DCCCXCIX', 'CM', 'CMI', 'CMII', 'CMIII', 'CMIV', 'CMV', 'CMVI', 'CMVII', 'CMVIII', 'CMIX', 'CMX', 'CMXI', 'CMXII', 'CMXIII', 'CMXIV', 'CMXV', 'CMXVI', 'CMXVII', 'CMXVIII', 'CMXIX', 'CMXX', 'CMXXI', 'CMXXII', 'CMXXIII', 'CMXXIV', 'CMXXV', 'CMXXVI', 'CMXXVII', 'CMXXVIII', 'CMXXIX', 'CMXXX', 'CMXXXI', 'CMXXXII', 'CMXXXIII', 'CMXXXIV', 'CMXXXV', 'CMXXXVI', 'CMXXXVII', 'CMXXXVIII', 'CMXXXIX', 'CMXL', 'CMXLI', 'CMXLII', 'CMXLIII', 'CMXLIV', 'CMXLV', 'CMXLVI', 'CMXLVII', 'CMXLVIII', 'CMXLIX', 'CML', 'CMLI', 'CMLII', 'CMLIII', 'CMLIV', 'CMLV', 'CMLVI', 'CMLVII', 'CMLVIII', 'CMLIX', 'CMLX', 'CMLXI', 'CMLXII', 'CMLXIII', 'CMLXIV', 'CMLXV', 'CMLXVI', 'CMLXVII', 'CMLXVIII', 'CMLXIX', 'CMLXX', 'CMLXXI', 'CMLXXII', 'CMLXXIII', 'CMLXXIV', 'CMLXXV', 'CMLXXVI', 'CMLXXVII', 'CMLXXVIII', 'CMLXXIX', 'CMLXXX', 'CMLXXXI', 'CMLXXXII', 'CMLXXXIII', 'CMLXXXIV', 'CMLXXXV', 'CMLXXXVI', 'CMLXXXVII', 'CMLXXXVIII', 'CMLXXXIX', 'CMXC', 'CMXCI', 'CMXCII', 'CMXCIII', 'CMXCIV', 'CMXCV', 'CMXCVI', 'CMXCVII', 'CMXCVIII', 'CMXCIX', 'M', 'MI', 'MII', 'MIII', 'MIV', 'MV', 'MVI', 'MVII', 'MVIII', 'MIX', 'MX', 'MXI', 'MXII', 'MXIII', 'MXIV', 'MXV', 'MXVI', 'MXVII', 'MXVIII', 'MXIX', 'MXX', 'MXXI', 'MXXII', 'MXXIII', 'MXXIV', 'MXXV', 'MXXVI', 'MXXVII', 'MXXVIII', 'MXXIX', 'MXXX', 'MXXXI', 'MXXXII', 'MXXXIII', 'MXXXIV', 'MXXXV', 'MXXXVI', 'MXXXVII', 'MXXXVIII', 'MXXXIX', 'MXL', 'MXLI', 'MXLII', 'MXLIII', 'MXLIV', 'MXLV', 'MXLVI', 'MXLVII', 'MXLVIII', 'MXLIX', 'ML', 'MLI', 'MLII', 'MLIII', 'MLIV', 'MLV', 'MLVI', 'MLVII', 'MLVIII', 'MLIX', 'MLX', 'MLXI', 'MLXII', 'MLXIII', 'MLXIV', 'MLXV', 'MLXVI', 'MLXVII', 'MLXVIII', 'MLXIX', 'MLXX', 'MLXXI', 'MLXXII', 'MLXXIII', 'MLXXIV', 'MLXXV', 'MLXXVI', 'MLXXVII', 'MLXXVIII', 'MLXXIX', 'MLXXX', 'MLXXXI', 'MLXXXII', 'MLXXXIII', 'MLXXXIV', 'MLXXXV', 'MLXXXVI', 'MLXXXVII', 'MLXXXVIII', 'MLXXXIX', 'MXC', 'MXCI', 'MXCII', 'MXCIII', 'MXCIV', 'MXCV', 'MXCVI', 'MXCVII', 'MXCVIII', 'MXCIX', 'MC', 'MCI', 'MCII', 'MCIII', 'MCIV', 'MCV', 'MCVI', 'MCVII', 'MCVIII', 'MCIX', 'MCX', 'MCXI', 'MCXII', 'MCXIII', 'MCXIV', 'MCXV', 'MCXVI', 'MCXVII', 'MCXVIII', 'MCXIX', 'MCXX', 'MCXXI', 'MCXXII', 'MCXXIII', 'MCXXIV', 'MCXXV', 'MCXXVI', 'MCXXVII', 'MCXXVIII', 'MCXXIX', 'MCXXX', 'MCXXXI', 'MCXXXII', 'MCXXXIII', 'MCXXXIV', 'MCXXXV', 'MCXXXVI', 'MCXXXVII', 'MCXXXVIII', 'MCXXXIX', 'MCXL', 'MCXLI', 'MCXLII', 'MCXLIII', 'MCXLIV', 'MCXLV', 'MCXLVI', 'MCXLVII', 'MCXLVIII', 'MCXLIX', 'MCL', 'MCLI', 'MCLII', 'MCLIII', 'MCLIV', 'MCLV', 'MCLVI', 'MCLVII', 'MCLVIII', 'MCLIX', 'MCLX', 'MCLXI', 'MCLXII', 'MCLXIII', 'MCLXIV', 'MCLXV', 'MCLXVI', 'MCLXVII', 'MCLXVIII', 'MCLXIX', 'MCLXX', 'MCLXXI', 'MCLXXII', 'MCLXXIII', 'MCLXXIV', 'MCLXXV', 'MCLXXVI', 'MCLXXVII', 'MCLXXVIII', 'MCLXXIX', 'MCLXXX', 'MCLXXXI', 'MCLXXXII', 'MCLXXXIII', 'MCLXXXIV', 'MCLXXXV', 'MCLXXXVI', 'MCLXXXVII', 'MCLXXXVIII', 'MCLXXXIX', 'MCXC', 'MCXCI', 'MCXCII', 'MCXCIII', 'MCXCIV', 'MCXCV', 'MCXCVI', 'MCXCVII', 'MCXCVIII', 'MCXCIX', 'MCC', 'MCCI', 'MCCII', 'MCCIII', 'MCCIV', 'MCCV', 'MCCVI', 'MCCVII', 'MCCVIII', 'MCCIX', 'MCCX', 'MCCXI', 'MCCXII', 'MCCXIII', 'MCCXIV', 'MCCXV', 'MCCXVI', 'MCCXVII', 'MCCXVIII', 'MCCXIX', 'MCCXX', 'MCCXXI', 'MCCXXII', 'MCCXXIII', 'MCCXXIV', 'MCCXXV', 'MCCXXVI', 'MCCXXVII', 'MCCXXVIII', 'MCCXXIX', 'MCCXXX', 'MCCXXXI', 'MCCXXXII', 'MCCXXXIII', 'MCCXXXIV', 'MCCXXXV', 'MCCXXXVI', 'MCCXXXVII', 'MCCXXXVIII', 'MCCXXXIX', 'MCCXL', 'MCCXLI', 'MCCXLII', 'MCCXLIII', 'MCCXLIV', 'MCCXLV', 'MCCXLVI', 'MCCXLVII', 'MCCXLVIII', 'MCCXLIX', 'MCCL', 'MCCLI', 'MCCLII', 'MCCLIII', 'MCCLIV', 'MCCLV', 'MCCLVI', 'MCCLVII', 'MCCLVIII', 'MCCLIX', 'MCCLX', 'MCCLXI', 'MCCLXII', 'MCCLXIII', 'MCCLXIV', 'MCCLXV', 'MCCLXVI', 'MCCLXVII', 'MCCLXVIII', 'MCCLXIX', 'MCCLXX', 'MCCLXXI', 'MCCLXXII', 'MCCLXXIII', 'MCCLXXIV', 'MCCLXXV', 'MCCLXXVI', 'MCCLXXVII', 'MCCLXXVIII', 'MCCLXXIX', 'MCCLXXX', 'MCCLXXXI', 'MCCLXXXII', 'MCCLXXXIII', 'MCCLXXXIV', 'MCCLXXXV', 'MCCLXXXVI', 'MCCLXXXVII', 'MCCLXXXVIII', 'MCCLXXXIX', 'MCCXC', 'MCCXCI', 'MCCXCII', 'MCCXCIII', 'MCCXCIV', 'MCCXCV', 'MCCXCVI', 'MCCXCVII', 'MCCXCVIII', 'MCCXCIX', 'MCCC', 'MCCCI', 'MCCCII', 'MCCCIII', 'MCCCIV', 'MCCCV', 'MCCCVI', 'MCCCVII', 'MCCCVIII', 'MCCCIX', 'MCCCX', 'MCCCXI', 'MCCCXII', 'MCCCXIII', 'MCCCXIV', 'MCCCXV', 'MCCCXVI', 'MCCCXVII', 'MCCCXVIII', 'MCCCXIX', 'MCCCXX', 'MCCCXXI', 'MCCCXXII', 'MCCCXXIII', 'MCCCXXIV', 'MCCCXXV', 'MCCCXXVI', 'MCCCXXVII', 'MCCCXXVIII', 'MCCCXXIX', 'MCCCXXX', 'MCCCXXXI', 'MCCCXXXII', 'MCCCXXXIII', 'MCCCXXXIV', 'MCCCXXXV', 'MCCCXXXVI', 'MCCCXXXVII', 'MCCCXXXVIII', 'MCCCXXXIX', 'MCCCXL', 'MCCCXLI', 'MCCCXLII', 'MCCCXLIII', 'MCCCXLIV', 'MCCCXLV', 'MCCCXLVI', 'MCCCXLVII', 'MCCCXLVIII', 'MCCCXLIX', 'MCCCL', 'MCCCLI', 'MCCCLII', 'MCCCLIII', 'MCCCLIV', 'MCCCLV', 'MCCCLVI', 'MCCCLVII', 'MCCCLVIII', 'MCCCLIX', 'MCCCLX', 'MCCCLXI', 'MCCCLXII', 'MCCCLXIII', 'MCCCLXIV', 'MCCCLXV', 'MCCCLXVI', 'MCCCLXVII', 'MCCCLXVIII', 'MCCCLXIX', 'MCCCLXX', 'MCCCLXXI', 'MCCCLXXII', 'MCCCLXXIII', 'MCCCLXXIV', 'MCCCLXXV', 'MCCCLXXVI', 'MCCCLXXVII', 'MCCCLXXVIII', 'MCCCLXXIX', 'MCCCLXXX', 'MCCCLXXXI', 'MCCCLXXXII', 'MCCCLXXXIII', 'MCCCLXXXIV', 'MCCCLXXXV', 'MCCCLXXXVI', 'MCCCLXXXVII', 'MCCCLXXXVIII', 'MCCCLXXXIX', 'MCCCXC', 'MCCCXCI', 'MCCCXCII', 'MCCCXCIII', 'MCCCXCIV', 'MCCCXCV', 'MCCCXCVI', 'MCCCXCVII', 'MCCCXCVIII', 'MCCCXCIX', 'MCD', 'MCDI', 'MCDII', 'MCDIII', 'MCDIV', 'MCDV', 'MCDVI', 'MCDVII', 'MCDVIII', 'MCDIX', 'MCDX', 'MCDXI', 'MCDXII', 'MCDXIII', 'MCDXIV', 'MCDXV', 'MCDXVI', 'MCDXVII', 'MCDXVIII', 'MCDXIX', 'MCDXX', 'MCDXXI', 'MCDXXII', 'MCDXXIII', 'MCDXXIV', 'MCDXXV', 'MCDXXVI', 'MCDXXVII', 'MCDXXVIII', 'MCDXXIX', 'MCDXXX', 'MCDXXXI', 'MCDXXXII', 'MCDXXXIII', 'MCDXXXIV', 'MCDXXXV', 'MCDXXXVI', 'MCDXXXVII', 'MCDXXXVIII', 'MCDXXXIX', 'MCDXL', 'MCDXLI', 'MCDXLII', 'MCDXLIII', 'MCDXLIV', 'MCDXLV', 'MCDXLVI', 'MCDXLVII', 'MCDXLVIII', 'MCDXLIX', 'MCDL', 'MCDLI', 'MCDLII', 'MCDLIII', 'MCDLIV', 'MCDLV', 'MCDLVI', 'MCDLVII', 'MCDLVIII', 'MCDLIX', 'MCDLX', 'MCDLXI', 'MCDLXII', 'MCDLXIII', 'MCDLXIV', 'MCDLXV', 'MCDLXVI', 'MCDLXVII', 'MCDLXVIII', 'MCDLXIX', 'MCDLXX', 'MCDLXXI', 'MCDLXXII', 'MCDLXXIII', 'MCDLXXIV', 'MCDLXXV', 'MCDLXXVI', 'MCDLXXVII', 'MCDLXXVIII', 'MCDLXXIX', 'MCDLXXX', 'MCDLXXXI', 'MCDLXXXII', 'MCDLXXXIII', 'MCDLXXXIV', 'MCDLXXXV', 'MCDLXXXVI', 'MCDLXXXVII', 'MCDLXXXVIII', 'MCDLXXXIX', 'MCDXC', 'MCDXCI', 'MCDXCII', 'MCDXCIII', 'MCDXCIV', 'MCDXCV', 'MCDXCVI', 'MCDXCVII', 'MCDXCVIII', 'MCDXCIX', 'MD', 'MDI', 'MDII', 'MDIII', 'MDIV', 'MDV', 'MDVI', 'MDVII', 'MDVIII', 'MDIX', 'MDX', 'MDXI', 'MDXII', 'MDXIII', 'MDXIV', 'MDXV', 'MDXVI', 'MDXVII', 'MDXVIII', 'MDXIX', 'MDXX', 'MDXXI', 'MDXXII', 'MDXXIII', 'MDXXIV', 'MDXXV', 'MDXXVI', 'MDXXVII', 'MDXXVIII', 'MDXXIX', 'MDXXX', 'MDXXXI', 'MDXXXII', 'MDXXXIII', 'MDXXXIV', 'MDXXXV', 'MDXXXVI', 'MDXXXVII', 'MDXXXVIII', 'MDXXXIX', 'MDXL', 'MDXLI', 'MDXLII', 'MDXLIII', 'MDXLIV', 'MDXLV', 'MDXLVI', 'MDXLVII', 'MDXLVIII', 'MDXLIX', 'MDL', 'MDLI', 'MDLII', 'MDLIII', 'MDLIV', 'MDLV', 'MDLVI', 'MDLVII', 'MDLVIII', 'MDLIX', 'MDLX', 'MDLXI', 'MDLXII', 'MDLXIII', 'MDLXIV', 'MDLXV', 'MDLXVI', 'MDLXVII', 'MDLXVIII', 'MDLXIX', 'MDLXX', 'MDLXXI', 'MDLXXII', 'MDLXXIII', 'MDLXXIV', 'MDLXXV', 'MDLXXVI', 'MDLXXVII', 'MDLXXVIII', 'MDLXXIX', 'MDLXXX', 'MDLXXXI', 'MDLXXXII', 'MDLXXXIII', 'MDLXXXIV', 'MDLXXXV', 'MDLXXXVI', 'MDLXXXVII', 'MDLXXXVIII', 'MDLXXXIX', 'MDXC', 'MDXCI', 'MDXCII', 'MDXCIII', 'MDXCIV', 'MDXCV', 'MDXCVI', 'MDXCVII', 'MDXCVIII', 'MDXCIX', 'MDC', 'MDCI', 'MDCII', 'MDCIII', 'MDCIV', 'MDCV', 'MDCVI', 'MDCVII', 'MDCVIII', 'MDCIX', 'MDCX', 'MDCXI', 'MDCXII', 'MDCXIII', 'MDCXIV', 'MDCXV', 'MDCXVI', 'MDCXVII', 'MDCXVIII', 'MDCXIX', 'MDCXX', 'MDCXXI', 'MDCXXII', 'MDCXXIII', 'MDCXXIV', 'MDCXXV', 'MDCXXVI', 'MDCXXVII', 'MDCXXVIII', 'MDCXXIX', 'MDCXXX', 'MDCXXXI', 'MDCXXXII', 'MDCXXXIII', 'MDCXXXIV', 'MDCXXXV', 'MDCXXXVI', 'MDCXXXVII', 'MDCXXXVIII', 'MDCXXXIX', 'MDCXL', 'MDCXLI', 'MDCXLII', 'MDCXLIII', 'MDCXLIV', 'MDCXLV', 'MDCXLVI', 'MDCXLVII', 'MDCXLVIII', 'MDCXLIX', 'MDCL', 'MDCLI', 'MDCLII', 'MDCLIII', 'MDCLIV', 'MDCLV', 'MDCLVI', 'MDCLVII', 'MDCLVIII', 'MDCLIX', 'MDCLX', 'MDCLXI', 'MDCLXII', 'MDCLXIII', 'MDCLXIV', 'MDCLXV', 'MDCLXVI', 'MDCLXVII', 'MDCLXVIII', 'MDCLXIX', 'MDCLXX', 'MDCLXXI', 'MDCLXXII', 'MDCLXXIII', 'MDCLXXIV', 'MDCLXXV', 'MDCLXXVI', 'MDCLXXVII', 'MDCLXXVIII', 'MDCLXXIX', 'MDCLXXX', 'MDCLXXXI', 'MDCLXXXII', 'MDCLXXXIII', 'MDCLXXXIV', 'MDCLXXXV', 'MDCLXXXVI', 'MDCLXXXVII', 'MDCLXXXVIII', 'MDCLXXXIX', 'MDCXC', 'MDCXCI', 'MDCXCII', 'MDCXCIII', 'MDCXCIV', 'MDCXCV', 'MDCXCVI', 'MDCXCVII', 'MDCXCVIII', 'MDCXCIX', 'MDCC', 'MDCCI', 'MDCCII', 'MDCCIII', 'MDCCIV', 'MDCCV', 'MDCCVI', 'MDCCVII', 'MDCCVIII', 'MDCCIX', 'MDCCX', 'MDCCXI', 'MDCCXII', 'MDCCXIII', 'MDCCXIV', 'MDCCXV', 'MDCCXVI', 'MDCCXVII', 'MDCCXVIII', 'MDCCXIX', 'MDCCXX', 'MDCCXXI', 'MDCCXXII', 'MDCCXXIII', 'MDCCXXIV', 'MDCCXXV', 'MDCCXXVI', 'MDCCXXVII', 'MDCCXXVIII', 'MDCCXXIX', 'MDCCXXX', 'MDCCXXXI', 'MDCCXXXII', 'MDCCXXXIII', 'MDCCXXXIV', 'MDCCXXXV', 'MDCCXXXVI', 'MDCCXXXVII', 'MDCCXXXVIII', 'MDCCXXXIX', 'MDCCXL', 'MDCCXLI', 'MDCCXLII', 'MDCCXLIII', 'MDCCXLIV', 'MDCCXLV', 'MDCCXLVI', 'MDCCXLVII', 'MDCCXLVIII', 'MDCCXLIX', 'MDCCL', 'MDCCLI', 'MDCCLII', 'MDCCLIII', 'MDCCLIV', 'MDCCLV', 'MDCCLVI', 'MDCCLVII', 'MDCCLVIII', 'MDCCLIX', 'MDCCLX', 'MDCCLXI', 'MDCCLXII', 'MDCCLXIII', 'MDCCLXIV', 'MDCCLXV', 'MDCCLXVI', 'MDCCLXVII', 'MDCCLXVIII', 'MDCCLXIX', 'MDCCLXX', 'MDCCLXXI', 'MDCCLXXII', 'MDCCLXXIII', 'MDCCLXXIV', 'MDCCLXXV', 'MDCCLXXVI', 'MDCCLXXVII', 'MDCCLXXVIII', 'MDCCLXXIX', 'MDCCLXXX', 'MDCCLXXXI', 'MDCCLXXXII', 'MDCCLXXXIII', 'MDCCLXXXIV', 'MDCCLXXXV', 'MDCCLXXXVI', 'MDCCLXXXVII', 'MDCCLXXXVIII', 'MDCCLXXXIX', 'MDCCXC', 'MDCCXCI', 'MDCCXCII', 'MDCCXCIII', 'MDCCXCIV', 'MDCCXCV', 'MDCCXCVI', 'MDCCXCVII', 'MDCCXCVIII', 'MDCCXCIX', 'MDCCC', 'MDCCCI', 'MDCCCII', 'MDCCCIII', 'MDCCCIV', 'MDCCCV', 'MDCCCVI', 'MDCCCVII', 'MDCCCVIII', 'MDCCCIX', 'MDCCCX', 'MDCCCXI', 'MDCCCXII', 'MDCCCXIII', 'MDCCCXIV', 'MDCCCXV', 'MDCCCXVI', 'MDCCCXVII', 'MDCCCXVIII', 'MDCCCXIX', 'MDCCCXX', 'MDCCCXXI', 'MDCCCXXII', 'MDCCCXXIII', 'MDCCCXXIV', 'MDCCCXXV', 'MDCCCXXVI', 'MDCCCXXVII', 'MDCCCXXVIII', 'MDCCCXXIX', 'MDCCCXXX', 'MDCCCXXXI', 'MDCCCXXXII', 'MDCCCXXXIII', 'MDCCCXXXIV', 'MDCCCXXXV', 'MDCCCXXXVI', 'MDCCCXXXVII', 'MDCCCXXXVIII', 'MDCCCXXXIX', 'MDCCCXL', 'MDCCCXLI', 'MDCCCXLII', 'MDCCCXLIII', 'MDCCCXLIV', 'MDCCCXLV', 'MDCCCXLVI', 'MDCCCXLVII', 'MDCCCXLVIII', 'MDCCCXLIX', 'MDCCCL', 'MDCCCLI', 'MDCCCLII', 'MDCCCLIII', 'MDCCCLIV', 'MDCCCLV', 'MDCCCLVI', 'MDCCCLVII', 'MDCCCLVIII', 'MDCCCLIX', 'MDCCCLX', 'MDCCCLXI', 'MDCCCLXII', 'MDCCCLXIII', 'MDCCCLXIV', 'MDCCCLXV', 'MDCCCLXVI', 'MDCCCLXVII', 'MDCCCLXVIII', 'MDCCCLXIX', 'MDCCCLXX', 'MDCCCLXXI', 'MDCCCLXXII', 'MDCCCLXXIII', 'MDCCCLXXIV', 'MDCCCLXXV', 'MDCCCLXXVI', 'MDCCCLXXVII', 'MDCCCLXXVIII', 'MDCCCLXXIX', 'MDCCCLXXX', 'MDCCCLXXXI', 'MDCCCLXXXII', 'MDCCCLXXXIII', 'MDCCCLXXXIV', 'MDCCCLXXXV', 'MDCCCLXXXVI', 'MDCCCLXXXVII', 'MDCCCLXXXVIII', 'MDCCCLXXXIX', 'MDCCCXC', 'MDCCCXCI', 'MDCCCXCII', 'MDCCCXCIII', 'MDCCCXCIV', 'MDCCCXCV', 'MDCCCXCVI', 'MDCCCXCVII', 'MDCCCXCVIII', 'MDCCCXCIX', 'MCM', 'MCMI', 'MCMII', 'MCMIII', 'MCMIV', 'MCMV', 'MCMVI', 'MCMVII', 'MCMVIII', 'MCMIX', 'MCMX', 'MCMXI', 'MCMXII', 'MCMXIII', 'MCMXIV', 'MCMXV', 'MCMXVI', 'MCMXVII', 'MCMXVIII', 'MCMXIX', 'MCMXX', 'MCMXXI', 'MCMXXII', 'MCMXXIII', 'MCMXXIV', 'MCMXXV', 'MCMXXVI', 'MCMXXVII', 'MCMXXVIII', 'MCMXXIX', 'MCMXXX', 'MCMXXXI', 'MCMXXXII', 'MCMXXXIII', 'MCMXXXIV', 'MCMXXXV', 'MCMXXXVI', 'MCMXXXVII', 'MCMXXXVIII', 'MCMXXXIX', 'MCMXL', 'MCMXLI', 'MCMXLII', 'MCMXLIII', 'MCMXLIV', 'MCMXLV', 'MCMXLVI', 'MCMXLVII', 'MCMXLVIII', 'MCMXLIX', 'MCML', 'MCMLI', 'MCMLII', 'MCMLIII', 'MCMLIV', 'MCMLV', 'MCMLVI', 'MCMLVII', 'MCMLVIII', 'MCMLIX', 'MCMLX', 'MCMLXI', 'MCMLXII', 'MCMLXIII', 'MCMLXIV', 'MCMLXV', 'MCMLXVI', 'MCMLXVII', 'MCMLXVIII', 'MCMLXIX', 'MCMLXX', 'MCMLXXI', 'MCMLXXII', 'MCMLXXIII', 'MCMLXXIV', 'MCMLXXV', 'MCMLXXVI', 'MCMLXXVII', 'MCMLXXVIII', 'MCMLXXIX', 'MCMLXXX', 'MCMLXXXI', 'MCMLXXXII', 'MCMLXXXIII', 'MCMLXXXIV', 'MCMLXXXV', 'MCMLXXXVI', 'MCMLXXXVII', 'MCMLXXXVIII', 'MCMLXXXIX', 'MCMXC', 'MCMXCI', 'MCMXCII', 'MCMXCIII', 'MCMXCIV', 'MCMXCV', 'MCMXCVI', 'MCMXCVII', 'MCMXCVIII', 'MCMXCIX', 'MM', 'MMI', 'MMII', 'MMIII', 'MMIV', 'MMV', 'MMVI', 'MMVII', 'MMVIII', 'MMIX', 'MMX', 'MMXI', 'MMXII', 'MMXIII', 'MMXIV', 'MMXV', 'MMXVI', 'MMXVII', 'MMXVIII', 'MMXIX', 'MMXX', 'MMXXI', 'MMXXII', 'MMXXIII', 'MMXXIV', 'MMXXV', 'MMXXVI', 'MMXXVII', 'MMXXVIII', 'MMXXIX', 'MMXXX', 'MMXXXI', 'MMXXXII', 'MMXXXIII', 'MMXXXIV', 'MMXXXV', 'MMXXXVI', 'MMXXXVII', 'MMXXXVIII', 'MMXXXIX', 'MMXL', 'MMXLI', 'MMXLII', 'MMXLIII', 'MMXLIV', 'MMXLV', 'MMXLVI', 'MMXLVII', 'MMXLVIII', 'MMXLIX', 'MML', 'MMLI', 'MMLII', 'MMLIII', 'MMLIV', 'MMLV', 'MMLVI', 'MMLVII', 'MMLVIII', 'MMLIX', 'MMLX', 'MMLXI', 'MMLXII', 'MMLXIII', 'MMLXIV', 'MMLXV', 'MMLXVI', 'MMLXVII', 'MMLXVIII', 'MMLXIX', 'MMLXX', 'MMLXXI', 'MMLXXII', 'MMLXXIII', 'MMLXXIV', 'MMLXXV', 'MMLXXVI', 'MMLXXVII', 'MMLXXVIII', 'MMLXXIX', 'MMLXXX', 'MMLXXXI', 'MMLXXXII', 'MMLXXXIII', 'MMLXXXIV', 'MMLXXXV', 'MMLXXXVI', 'MMLXXXVII', 'MMLXXXVIII', 'MMLXXXIX', 'MMXC', 'MMXCI', 'MMXCII', 'MMXCIII', 'MMXCIV', 'MMXCV', 'MMXCVI', 'MMXCVII', 'MMXCVIII', 'MMXCIX', 'MMC', 'MMCI', 'MMCII', 'MMCIII', 'MMCIV', 'MMCV', 'MMCVI', 'MMCVII', 'MMCVIII', 'MMCIX', 'MMCX', 'MMCXI', 'MMCXII', 'MMCXIII', 'MMCXIV', 'MMCXV', 'MMCXVI', 'MMCXVII', 'MMCXVIII', 'MMCXIX', 'MMCXX', 'MMCXXI', 'MMCXXII', 'MMCXXIII', 'MMCXXIV', 'MMCXXV', 'MMCXXVI', 'MMCXXVII', 'MMCXXVIII', 'MMCXXIX', 'MMCXXX', 'MMCXXXI', 'MMCXXXII', 'MMCXXXIII', 'MMCXXXIV', 'MMCXXXV', 'MMCXXXVI', 'MMCXXXVII', 'MMCXXXVIII', 'MMCXXXIX', 'MMCXL', 'MMCXLI', 'MMCXLII', 'MMCXLIII', 'MMCXLIV', 'MMCXLV', 'MMCXLVI', 'MMCXLVII', 'MMCXLVIII', 'MMCXLIX', 'MMCL', 'MMCLI', 'MMCLII', 'MMCLIII', 'MMCLIV', 'MMCLV', 'MMCLVI', 'MMCLVII', 'MMCLVIII', 'MMCLIX', 'MMCLX', 'MMCLXI', 'MMCLXII', 'MMCLXIII', 'MMCLXIV', 'MMCLXV', 'MMCLXVI', 'MMCLXVII', 'MMCLXVIII', 'MMCLXIX', 'MMCLXX', 'MMCLXXI', 'MMCLXXII', 'MMCLXXIII', 'MMCLXXIV', 'MMCLXXV', 'MMCLXXVI', 'MMCLXXVII', 'MMCLXXVIII', 'MMCLXXIX', 'MMCLXXX', 'MMCLXXXI', 'MMCLXXXII', 'MMCLXXXIII', 'MMCLXXXIV', 'MMCLXXXV', 'MMCLXXXVI', 'MMCLXXXVII', 'MMCLXXXVIII', 'MMCLXXXIX', 'MMCXC', 'MMCXCI', 'MMCXCII', 'MMCXCIII', 'MMCXCIV', 'MMCXCV', 'MMCXCVI', 'MMCXCVII', 'MMCXCVIII', 'MMCXCIX', 'MMCC', 'MMCCI', 'MMCCII', 'MMCCIII', 'MMCCIV', 'MMCCV', 'MMCCVI', 'MMCCVII', 'MMCCVIII', 'MMCCIX', 'MMCCX', 'MMCCXI', 'MMCCXII', 'MMCCXIII', 'MMCCXIV', 'MMCCXV', 'MMCCXVI', 'MMCCXVII', 'MMCCXVIII', 'MMCCXIX', 'MMCCXX', 'MMCCXXI', 'MMCCXXII', 'MMCCXXIII', 'MMCCXXIV', 'MMCCXXV', 'MMCCXXVI', 'MMCCXXVII', 'MMCCXXVIII', 'MMCCXXIX', 'MMCCXXX', 'MMCCXXXI', 'MMCCXXXII', 'MMCCXXXIII', 'MMCCXXXIV', 'MMCCXXXV', 'MMCCXXXVI', 'MMCCXXXVII', 'MMCCXXXVIII', 'MMCCXXXIX', 'MMCCXL', 'MMCCXLI', 'MMCCXLII', 'MMCCXLIII', 'MMCCXLIV', 'MMCCXLV', 'MMCCXLVI', 'MMCCXLVII', 'MMCCXLVIII', 'MMCCXLIX', 'MMCCL', 'MMCCLI', 'MMCCLII', 'MMCCLIII', 'MMCCLIV', 'MMCCLV', 'MMCCLVI', 'MMCCLVII', 'MMCCLVIII', 'MMCCLIX', 'MMCCLX', 'MMCCLXI', 'MMCCLXII', 'MMCCLXIII', 'MMCCLXIV', 'MMCCLXV', 'MMCCLXVI', 'MMCCLXVII', 'MMCCLXVIII', 'MMCCLXIX', 'MMCCLXX', 'MMCCLXXI', 'MMCCLXXII', 'MMCCLXXIII', 'MMCCLXXIV', 'MMCCLXXV', 'MMCCLXXVI', 'MMCCLXXVII', 'MMCCLXXVIII', 'MMCCLXXIX', 'MMCCLXXX', 'MMCCLXXXI', 'MMCCLXXXII', 'MMCCLXXXIII', 'MMCCLXXXIV', 'MMCCLXXXV', 'MMCCLXXXVI', 'MMCCLXXXVII', 'MMCCLXXXVIII', 'MMCCLXXXIX', 'MMCCXC', 'MMCCXCI', 'MMCCXCII', 'MMCCXCIII', 'MMCCXCIV', 'MMCCXCV', 'MMCCXCVI', 'MMCCXCVII', 'MMCCXCVIII', 'MMCCXCIX', 'MMCCC', 'MMCCCI', 'MMCCCII', 'MMCCCIII', 'MMCCCIV', 'MMCCCV', 'MMCCCVI', 'MMCCCVII', 'MMCCCVIII', 'MMCCCIX', 'MMCCCX', 'MMCCCXI', 'MMCCCXII', 'MMCCCXIII', 'MMCCCXIV', 'MMCCCXV', 'MMCCCXVI', 'MMCCCXVII', 'MMCCCXVIII', 'MMCCCXIX', 'MMCCCXX', 'MMCCCXXI', 'MMCCCXXII', 'MMCCCXXIII', 'MMCCCXXIV', 'MMCCCXXV', 'MMCCCXXVI', 'MMCCCXXVII', 'MMCCCXXVIII', 'MMCCCXXIX', 'MMCCCXXX', 'MMCCCXXXI', 'MMCCCXXXII', 'MMCCCXXXIII', 'MMCCCXXXIV', 'MMCCCXXXV', 'MMCCCXXXVI', 'MMCCCXXXVII', 'MMCCCXXXVIII', 'MMCCCXXXIX', 'MMCCCXL', 'MMCCCXLI', 'MMCCCXLII', 'MMCCCXLIII', 'MMCCCXLIV', 'MMCCCXLV', 'MMCCCXLVI', 'MMCCCXLVII', 'MMCCCXLVIII', 'MMCCCXLIX', 'MMCCCL', 'MMCCCLI', 'MMCCCLII', 'MMCCCLIII', 'MMCCCLIV', 'MMCCCLV', 'MMCCCLVI', 'MMCCCLVII', 'MMCCCLVIII', 'MMCCCLIX', 'MMCCCLX', 'MMCCCLXI', 'MMCCCLXII', 'MMCCCLXIII', 'MMCCCLXIV', 'MMCCCLXV', 'MMCCCLXVI', 'MMCCCLXVII', 'MMCCCLXVIII', 'MMCCCLXIX', 'MMCCCLXX', 'MMCCCLXXI', 'MMCCCLXXII', 'MMCCCLXXIII', 'MMCCCLXXIV', 'MMCCCLXXV', 'MMCCCLXXVI', 'MMCCCLXXVII', 'MMCCCLXXVIII', 'MMCCCLXXIX', 'MMCCCLXXX', 'MMCCCLXXXI', 'MMCCCLXXXII', 'MMCCCLXXXIII', 'MMCCCLXXXIV', 'MMCCCLXXXV', 'MMCCCLXXXVI', 'MMCCCLXXXVII', 'MMCCCLXXXVIII', 'MMCCCLXXXIX', 'MMCCCXC', 'MMCCCXCI', 'MMCCCXCII', 'MMCCCXCIII', 'MMCCCXCIV', 'MMCCCXCV', 'MMCCCXCVI', 'MMCCCXCVII', 'MMCCCXCVIII', 'MMCCCXCIX', 'MMCD', 'MMCDI', 'MMCDII', 'MMCDIII', 'MMCDIV', 'MMCDV', 'MMCDVI', 'MMCDVII', 'MMCDVIII', 'MMCDIX', 'MMCDX', 'MMCDXI', 'MMCDXII', 'MMCDXIII', 'MMCDXIV', 'MMCDXV', 'MMCDXVI', 'MMCDXVII', 'MMCDXVIII', 'MMCDXIX', 'MMCDXX', 'MMCDXXI', 'MMCDXXII', 'MMCDXXIII', 'MMCDXXIV', 'MMCDXXV', 'MMCDXXVI', 'MMCDXXVII', 'MMCDXXVIII', 'MMCDXXIX', 'MMCDXXX', 'MMCDXXXI', 'MMCDXXXII', 'MMCDXXXIII', 'MMCDXXXIV', 'MMCDXXXV', 'MMCDXXXVI', 'MMCDXXXVII', 'MMCDXXXVIII', 'MMCDXXXIX', 'MMCDXL', 'MMCDXLI', 'MMCDXLII', 'MMCDXLIII', 'MMCDXLIV', 'MMCDXLV', 'MMCDXLVI', 'MMCDXLVII', 'MMCDXLVIII', 'MMCDXLIX', 'MMCDL', 'MMCDLI', 'MMCDLII', 'MMCDLIII', 'MMCDLIV', 'MMCDLV', 'MMCDLVI', 'MMCDLVII', 'MMCDLVIII', 'MMCDLIX', 'MMCDLX', 'MMCDLXI', 'MMCDLXII', 'MMCDLXIII', 'MMCDLXIV', 'MMCDLXV', 'MMCDLXVI', 'MMCDLXVII', 'MMCDLXVIII', 'MMCDLXIX', 'MMCDLXX', 'MMCDLXXI', 'MMCDLXXII', 'MMCDLXXIII', 'MMCDLXXIV', 'MMCDLXXV', 'MMCDLXXVI', 'MMCDLXXVII', 'MMCDLXXVIII', 'MMCDLXXIX', 'MMCDLXXX', 'MMCDLXXXI', 'MMCDLXXXII', 'MMCDLXXXIII', 'MMCDLXXXIV', 'MMCDLXXXV', 'MMCDLXXXVI', 'MMCDLXXXVII', 'MMCDLXXXVIII', 'MMCDLXXXIX', 'MMCDXC', 'MMCDXCI', 'MMCDXCII', 'MMCDXCIII', 'MMCDXCIV', 'MMCDXCV', 'MMCDXCVI', 'MMCDXCVII', 'MMCDXCVIII', 'MMCDXCIX', 'MMD', 'MMDI', 'MMDII', 'MMDIII', 'MMDIV', 'MMDV', 'MMDVI', 'MMDVII', 'MMDVIII', 'MMDIX', 'MMDX', 'MMDXI', 'MMDXII', 'MMDXIII', 'MMDXIV', 'MMDXV', 'MMDXVI', 'MMDXVII', 'MMDXVIII', 'MMDXIX', 'MMDXX', 'MMDXXI', 'MMDXXII', 'MMDXXIII', 'MMDXXIV', 'MMDXXV', 'MMDXXVI', 'MMDXXVII', 'MMDXXVIII', 'MMDXXIX', 'MMDXXX', 'MMDXXXI', 'MMDXXXII', 'MMDXXXIII', 'MMDXXXIV', 'MMDXXXV', 'MMDXXXVI', 'MMDXXXVII', 'MMDXXXVIII', 'MMDXXXIX', 'MMDXL', 'MMDXLI', 'MMDXLII', 'MMDXLIII', 'MMDXLIV', 'MMDXLV', 'MMDXLVI', 'MMDXLVII', 'MMDXLVIII', 'MMDXLIX', 'MMDL', 'MMDLI', 'MMDLII', 'MMDLIII', 'MMDLIV', 'MMDLV', 'MMDLVI', 'MMDLVII', 'MMDLVIII', 'MMDLIX', 'MMDLX', 'MMDLXI', 'MMDLXII', 'MMDLXIII', 'MMDLXIV', 'MMDLXV', 'MMDLXVI', 'MMDLXVII', 'MMDLXVIII', 'MMDLXIX', 'MMDLXX', 'MMDLXXI', 'MMDLXXII', 'MMDLXXIII', 'MMDLXXIV', 'MMDLXXV', 'MMDLXXVI', 'MMDLXXVII', 'MMDLXXVIII', 'MMDLXXIX', 'MMDLXXX', 'MMDLXXXI', 'MMDLXXXII', 'MMDLXXXIII', 'MMDLXXXIV', 'MMDLXXXV', 'MMDLXXXVI', 'MMDLXXXVII', 'MMDLXXXVIII', 'MMDLXXXIX', 'MMDXC', 'MMDXCI', 'MMDXCII', 'MMDXCIII', 'MMDXCIV', 'MMDXCV', 'MMDXCVI', 'MMDXCVII', 'MMDXCVIII', 'MMDXCIX', 'MMDC', 'MMDCI', 'MMDCII', 'MMDCIII', 'MMDCIV', 'MMDCV', 'MMDCVI', 'MMDCVII', 'MMDCVIII', 'MMDCIX', 'MMDCX', 'MMDCXI', 'MMDCXII', 'MMDCXIII', 'MMDCXIV', 'MMDCXV', 'MMDCXVI', 'MMDCXVII', 'MMDCXVIII', 'MMDCXIX', 'MMDCXX', 'MMDCXXI', 'MMDCXXII', 'MMDCXXIII', 'MMDCXXIV', 'MMDCXXV', 'MMDCXXVI', 'MMDCXXVII', 'MMDCXXVIII', 'MMDCXXIX', 'MMDCXXX', 'MMDCXXXI', 'MMDCXXXII', 'MMDCXXXIII', 'MMDCXXXIV', 'MMDCXXXV', 'MMDCXXXVI', 'MMDCXXXVII', 'MMDCXXXVIII', 'MMDCXXXIX', 'MMDCXL', 'MMDCXLI', 'MMDCXLII', 'MMDCXLIII', 'MMDCXLIV', 'MMDCXLV', 'MMDCXLVI', 'MMDCXLVII', 'MMDCXLVIII', 'MMDCXLIX', 'MMDCL', 'MMDCLI', 'MMDCLII', 'MMDCLIII', 'MMDCLIV', 'MMDCLV', 'MMDCLVI', 'MMDCLVII', 'MMDCLVIII', 'MMDCLIX', 'MMDCLX', 'MMDCLXI', 'MMDCLXII', 'MMDCLXIII', 'MMDCLXIV', 'MMDCLXV', 'MMDCLXVI', 'MMDCLXVII', 'MMDCLXVIII', 'MMDCLXIX', 'MMDCLXX', 'MMDCLXXI', 'MMDCLXXII', 'MMDCLXXIII', 'MMDCLXXIV', 'MMDCLXXV', 'MMDCLXXVI', 'MMDCLXXVII', 'MMDCLXXVIII', 'MMDCLXXIX', 'MMDCLXXX', 'MMDCLXXXI', 'MMDCLXXXII', 'MMDCLXXXIII', 'MMDCLXXXIV', 'MMDCLXXXV', 'MMDCLXXXVI', 'MMDCLXXXVII', 'MMDCLXXXVIII', 'MMDCLXXXIX', 'MMDCXC', 'MMDCXCI', 'MMDCXCII', 'MMDCXCIII', 'MMDCXCIV', 'MMDCXCV', 'MMDCXCVI', 'MMDCXCVII', 'MMDCXCVIII', 'MMDCXCIX', 'MMDCC', 'MMDCCI', 'MMDCCII', 'MMDCCIII', 'MMDCCIV', 'MMDCCV', 'MMDCCVI', 'MMDCCVII', 'MMDCCVIII', 'MMDCCIX', 'MMDCCX', 'MMDCCXI', 'MMDCCXII', 'MMDCCXIII', 'MMDCCXIV', 'MMDCCXV', 'MMDCCXVI', 'MMDCCXVII', 'MMDCCXVIII', 'MMDCCXIX', 'MMDCCXX', 'MMDCCXXI', 'MMDCCXXII', 'MMDCCXXIII', 'MMDCCXXIV', 'MMDCCXXV', 'MMDCCXXVI', 'MMDCCXXVII', 'MMDCCXXVIII', 'MMDCCXXIX', 'MMDCCXXX', 'MMDCCXXXI', 'MMDCCXXXII', 'MMDCCXXXIII', 'MMDCCXXXIV', 'MMDCCXXXV', 'MMDCCXXXVI', 'MMDCCXXXVII', 'MMDCCXXXVIII', 'MMDCCXXXIX', 'MMDCCXL', 'MMDCCXLI', 'MMDCCXLII', 'MMDCCXLIII', 'MMDCCXLIV', 'MMDCCXLV', 'MMDCCXLVI', 'MMDCCXLVII', 'MMDCCXLVIII', 'MMDCCXLIX', 'MMDCCL', 'MMDCCLI', 'MMDCCLII', 'MMDCCLIII', 'MMDCCLIV', 'MMDCCLV', 'MMDCCLVI', 'MMDCCLVII', 'MMDCCLVIII', 'MMDCCLIX', 'MMDCCLX', 'MMDCCLXI', 'MMDCCLXII', 'MMDCCLXIII', 'MMDCCLXIV', 'MMDCCLXV', 'MMDCCLXVI', 'MMDCCLXVII', 'MMDCCLXVIII', 'MMDCCLXIX', 'MMDCCLXX', 'MMDCCLXXI', 'MMDCCLXXII', 'MMDCCLXXIII', 'MMDCCLXXIV', 'MMDCCLXXV', 'MMDCCLXXVI', 'MMDCCLXXVII', 'MMDCCLXXVIII', 'MMDCCLXXIX', 'MMDCCLXXX', 'MMDCCLXXXI', 'MMDCCLXXXII', 'MMDCCLXXXIII', 'MMDCCLXXXIV', 'MMDCCLXXXV', 'MMDCCLXXXVI', 'MMDCCLXXXVII', 'MMDCCLXXXVIII', 'MMDCCLXXXIX', 'MMDCCXC', 'MMDCCXCI', 'MMDCCXCII', 'MMDCCXCIII', 'MMDCCXCIV', 'MMDCCXCV', 'MMDCCXCVI', 'MMDCCXCVII', 'MMDCCXCVIII', 'MMDCCXCIX', 'MMDCCC', 'MMDCCCI', 'MMDCCCII', 'MMDCCCIII', 'MMDCCCIV', 'MMDCCCV', 'MMDCCCVI', 'MMDCCCVII', 'MMDCCCVIII', 'MMDCCCIX', 'MMDCCCX', 'MMDCCCXI', 'MMDCCCXII', 'MMDCCCXIII', 'MMDCCCXIV', 'MMDCCCXV', 'MMDCCCXVI', 'MMDCCCXVII', 'MMDCCCXVIII', 'MMDCCCXIX', 'MMDCCCXX', 'MMDCCCXXI', 'MMDCCCXXII', 'MMDCCCXXIII', 'MMDCCCXXIV', 'MMDCCCXXV', 'MMDCCCXXVI', 'MMDCCCXXVII', 'MMDCCCXXVIII', 'MMDCCCXXIX', 'MMDCCCXXX', 'MMDCCCXXXI', 'MMDCCCXXXII', 'MMDCCCXXXIII', 'MMDCCCXXXIV', 'MMDCCCXXXV', 'MMDCCCXXXVI', 'MMDCCCXXXVII', 'MMDCCCXXXVIII', 'MMDCCCXXXIX', 'MMDCCCXL', 'MMDCCCXLI', 'MMDCCCXLII', 'MMDCCCXLIII', 'MMDCCCXLIV', 'MMDCCCXLV', 'MMDCCCXLVI', 'MMDCCCXLVII', 'MMDCCCXLVIII', 'MMDCCCXLIX', 'MMDCCCL', 'MMDCCCLI', 'MMDCCCLII', 'MMDCCCLIII', 'MMDCCCLIV', 'MMDCCCLV', 'MMDCCCLVI', 'MMDCCCLVII', 'MMDCCCLVIII', 'MMDCCCLIX', 'MMDCCCLX', 'MMDCCCLXI', 'MMDCCCLXII', 'MMDCCCLXIII', 'MMDCCCLXIV', 'MMDCCCLXV', 'MMDCCCLXVI', 'MMDCCCLXVII', 'MMDCCCLXVIII', 'MMDCCCLXIX', 'MMDCCCLXX', 'MMDCCCLXXI', 'MMDCCCLXXII', 'MMDCCCLXXIII', 'MMDCCCLXXIV', 'MMDCCCLXXV', 'MMDCCCLXXVI', 'MMDCCCLXXVII', 'MMDCCCLXXVIII', 'MMDCCCLXXIX', 'MMDCCCLXXX', 'MMDCCCLXXXI', 'MMDCCCLXXXII', 'MMDCCCLXXXIII', 'MMDCCCLXXXIV', 'MMDCCCLXXXV', 'MMDCCCLXXXVI', 'MMDCCCLXXXVII', 'MMDCCCLXXXVIII', 'MMDCCCLXXXIX', 'MMDCCCXC', 'MMDCCCXCI', 'MMDCCCXCII', 'MMDCCCXCIII', 'MMDCCCXCIV', 'MMDCCCXCV', 'MMDCCCXCVI', 'MMDCCCXCVII', 'MMDCCCXCVIII', 'MMDCCCXCIX', 'MMCM', 'MMCMI', 'MMCMII', 'MMCMIII', 'MMCMIV', 'MMCMV', 'MMCMVI', 'MMCMVII', 'MMCMVIII', 'MMCMIX', 'MMCMX', 'MMCMXI', 'MMCMXII', 'MMCMXIII', 'MMCMXIV', 'MMCMXV', 'MMCMXVI', 'MMCMXVII', 'MMCMXVIII', 'MMCMXIX', 'MMCMXX', 'MMCMXXI', 'MMCMXXII', 'MMCMXXIII', 'MMCMXXIV', 'MMCMXXV', 'MMCMXXVI', 'MMCMXXVII', 'MMCMXXVIII', 'MMCMXXIX', 'MMCMXXX', 'MMCMXXXI', 'MMCMXXXII', 'MMCMXXXIII', 'MMCMXXXIV', 'MMCMXXXV', 'MMCMXXXVI', 'MMCMXXXVII', 'MMCMXXXVIII', 'MMCMXXXIX', 'MMCMXL', 'MMCMXLI', 'MMCMXLII', 'MMCMXLIII', 'MMCMXLIV', 'MMCMXLV', 'MMCMXLVI', 'MMCMXLVII', 'MMCMXLVIII', 'MMCMXLIX', 'MMCML', 'MMCMLI', 'MMCMLII', 'MMCMLIII', 'MMCMLIV', 'MMCMLV', 'MMCMLVI', 'MMCMLVII', 'MMCMLVIII', 'MMCMLIX', 'MMCMLX', 'MMCMLXI', 'MMCMLXII', 'MMCMLXIII', 'MMCMLXIV', 'MMCMLXV', 'MMCMLXVI', 'MMCMLXVII', 'MMCMLXVIII', 'MMCMLXIX', 'MMCMLXX', 'MMCMLXXI', 'MMCMLXXII', 'MMCMLXXIII', 'MMCMLXXIV', 'MMCMLXXV', 'MMCMLXXVI', 'MMCMLXXVII', 'MMCMLXXVIII', 'MMCMLXXIX', 'MMCMLXXX', 'MMCMLXXXI', 'MMCMLXXXII', 'MMCMLXXXIII', 'MMCMLXXXIV', 'MMCMLXXXV', 'MMCMLXXXVI', 'MMCMLXXXVII', 'MMCMLXXXVIII', 'MMCMLXXXIX', 'MMCMXC', 'MMCMXCI', 'MMCMXCII', 'MMCMXCIII', 'MMCMXCIV', 'MMCMXCV', 'MMCMXCVI', 'MMCMXCVII', 'MMCMXCVIII', 'MMCMXCIX', 'MMM', 'MMMI', 'MMMII', 'MMMIII', 'MMMIV', 'MMMV', 'MMMVI', 'MMMVII', 'MMMVIII', 'MMMIX', 'MMMX', 'MMMXI', 'MMMXII', 'MMMXIII', 'MMMXIV', 'MMMXV', 'MMMXVI', 'MMMXVII', 'MMMXVIII', 'MMMXIX', 'MMMXX', 'MMMXXI', 'MMMXXII', 'MMMXXIII', 'MMMXXIV', 'MMMXXV', 'MMMXXVI', 'MMMXXVII', 'MMMXXVIII', 'MMMXXIX', 'MMMXXX', 'MMMXXXI', 'MMMXXXII', 'MMMXXXIII', 'MMMXXXIV', 'MMMXXXV', 'MMMXXXVI', 'MMMXXXVII', 'MMMXXXVIII', 'MMMXXXIX', 'MMMXL', 'MMMXLI', 'MMMXLII', 'MMMXLIII', 'MMMXLIV', 'MMMXLV', 'MMMXLVI', 'MMMXLVII', 'MMMXLVIII', 'MMMXLIX', 'MMML', 'MMMLI', 'MMMLII', 'MMMLIII', 'MMMLIV', 'MMMLV', 'MMMLVI', 'MMMLVII', 'MMMLVIII', 'MMMLIX', 'MMMLX', 'MMMLXI', 'MMMLXII', 'MMMLXIII', 'MMMLXIV', 'MMMLXV', 'MMMLXVI', 'MMMLXVII', 'MMMLXVIII', 'MMMLXIX', 'MMMLXX', 'MMMLXXI', 'MMMLXXII', 'MMMLXXIII', 'MMMLXXIV', 'MMMLXXV', 'MMMLXXVI', 'MMMLXXVII', 'MMMLXXVIII', 'MMMLXXIX', 'MMMLXXX', 'MMMLXXXI', 'MMMLXXXII', 'MMMLXXXIII', 'MMMLXXXIV', 'MMMLXXXV', 'MMMLXXXVI', 'MMMLXXXVII', 'MMMLXXXVIII', 'MMMLXXXIX', 'MMMXC', 'MMMXCI', 'MMMXCII', 'MMMXCIII', 'MMMXCIV', 'MMMXCV', 'MMMXCVI', 'MMMXCVII', 'MMMXCVIII', 'MMMXCIX', 'MMMC', 'MMMCI', 'MMMCII', 'MMMCIII', 'MMMCIV', 'MMMCV', 'MMMCVI', 'MMMCVII', 'MMMCVIII', 'MMMCIX', 'MMMCX', 'MMMCXI', 'MMMCXII', 'MMMCXIII', 'MMMCXIV', 'MMMCXV', 'MMMCXVI', 'MMMCXVII', 'MMMCXVIII', 'MMMCXIX', 'MMMCXX', 'MMMCXXI', 'MMMCXXII', 'MMMCXXIII', 'MMMCXXIV', 'MMMCXXV', 'MMMCXXVI', 'MMMCXXVII', 'MMMCXXVIII', 'MMMCXXIX', 'MMMCXXX', 'MMMCXXXI', 'MMMCXXXII', 'MMMCXXXIII', 'MMMCXXXIV', 'MMMCXXXV', 'MMMCXXXVI', 'MMMCXXXVII', 'MMMCXXXVIII', 'MMMCXXXIX', 'MMMCXL', 'MMMCXLI', 'MMMCXLII', 'MMMCXLIII', 'MMMCXLIV', 'MMMCXLV', 'MMMCXLVI', 'MMMCXLVII', 'MMMCXLVIII', 'MMMCXLIX', 'MMMCL', 'MMMCLI', 'MMMCLII', 'MMMCLIII', 'MMMCLIV', 'MMMCLV', 'MMMCLVI', 'MMMCLVII', 'MMMCLVIII', 'MMMCLIX', 'MMMCLX', 'MMMCLXI', 'MMMCLXII', 'MMMCLXIII', 'MMMCLXIV', 'MMMCLXV', 'MMMCLXVI', 'MMMCLXVII', 'MMMCLXVIII', 'MMMCLXIX', 'MMMCLXX', 'MMMCLXXI', 'MMMCLXXII', 'MMMCLXXIII', 'MMMCLXXIV', 'MMMCLXXV', 'MMMCLXXVI', 'MMMCLXXVII', 'MMMCLXXVIII', 'MMMCLXXIX', 'MMMCLXXX', 'MMMCLXXXI', 'MMMCLXXXII', 'MMMCLXXXIII', 'MMMCLXXXIV', 'MMMCLXXXV', 'MMMCLXXXVI', 'MMMCLXXXVII', 'MMMCLXXXVIII', 'MMMCLXXXIX', 'MMMCXC', 'MMMCXCI', 'MMMCXCII', 'MMMCXCIII', 'MMMCXCIV', 'MMMCXCV', 'MMMCXCVI', 'MMMCXCVII', 'MMMCXCVIII', 'MMMCXCIX', 'MMMCC', 'MMMCCI', 'MMMCCII', 'MMMCCIII', 'MMMCCIV', 'MMMCCV', 'MMMCCVI', 'MMMCCVII', 'MMMCCVIII', 'MMMCCIX', 'MMMCCX', 'MMMCCXI', 'MMMCCXII', 'MMMCCXIII', 'MMMCCXIV', 'MMMCCXV', 'MMMCCXVI', 'MMMCCXVII', 'MMMCCXVIII', 'MMMCCXIX', 'MMMCCXX', 'MMMCCXXI', 'MMMCCXXII', 'MMMCCXXIII', 'MMMCCXXIV', 'MMMCCXXV', 'MMMCCXXVI', 'MMMCCXXVII', 'MMMCCXXVIII', 'MMMCCXXIX', 'MMMCCXXX', 'MMMCCXXXI', 'MMMCCXXXII', 'MMMCCXXXIII', 'MMMCCXXXIV', 'MMMCCXXXV', 'MMMCCXXXVI', 'MMMCCXXXVII', 'MMMCCXXXVIII', 'MMMCCXXXIX', 'MMMCCXL', 'MMMCCXLI', 'MMMCCXLII', 'MMMCCXLIII', 'MMMCCXLIV', 'MMMCCXLV', 'MMMCCXLVI', 'MMMCCXLVII', 'MMMCCXLVIII', 'MMMCCXLIX', 'MMMCCL', 'MMMCCLI', 'MMMCCLII', 'MMMCCLIII', 'MMMCCLIV', 'MMMCCLV', 'MMMCCLVI', 'MMMCCLVII', 'MMMCCLVIII', 'MMMCCLIX', 'MMMCCLX', 'MMMCCLXI', 'MMMCCLXII', 'MMMCCLXIII', 'MMMCCLXIV', 'MMMCCLXV', 'MMMCCLXVI', 'MMMCCLXVII', 'MMMCCLXVIII', 'MMMCCLXIX', 'MMMCCLXX', 'MMMCCLXXI', 'MMMCCLXXII', 'MMMCCLXXIII', 'MMMCCLXXIV', 'MMMCCLXXV', 'MMMCCLXXVI', 'MMMCCLXXVII', 'MMMCCLXXVIII', 'MMMCCLXXIX', 'MMMCCLXXX', 'MMMCCLXXXI', 'MMMCCLXXXII', 'MMMCCLXXXIII', 'MMMCCLXXXIV', 'MMMCCLXXXV', 'MMMCCLXXXVI', 'MMMCCLXXXVII', 'MMMCCLXXXVIII', 'MMMCCLXXXIX', 'MMMCCXC', 'MMMCCXCI', 'MMMCCXCII', 'MMMCCXCIII', 'MMMCCXCIV', 'MMMCCXCV', 'MMMCCXCVI', 'MMMCCXCVII', 'MMMCCXCVIII', 'MMMCCXCIX', 'MMMCCC', 'MMMCCCI', 'MMMCCCII', 'MMMCCCIII', 'MMMCCCIV', 'MMMCCCV', 'MMMCCCVI', 'MMMCCCVII', 'MMMCCCVIII', 'MMMCCCIX', 'MMMCCCX', 'MMMCCCXI', 'MMMCCCXII', 'MMMCCCXIII', 'MMMCCCXIV', 'MMMCCCXV', 'MMMCCCXVI', 'MMMCCCXVII', 'MMMCCCXVIII', 'MMMCCCXIX', 'MMMCCCXX', 'MMMCCCXXI', 'MMMCCCXXII', 'MMMCCCXXIII', 'MMMCCCXXIV', 'MMMCCCXXV', 'MMMCCCXXVI', 'MMMCCCXXVII', 'MMMCCCXXVIII', 'MMMCCCXXIX', 'MMMCCCXXX', 'MMMCCCXXXI', 'MMMCCCXXXII', 'MMMCCCXXXIII', 'MMMCCCXXXIV', 'MMMCCCXXXV', 'MMMCCCXXXVI', 'MMMCCCXXXVII', 'MMMCCCXXXVIII', 'MMMCCCXXXIX', 'MMMCCCXL', 'MMMCCCXLI', 'MMMCCCXLII', 'MMMCCCXLIII', 'MMMCCCXLIV', 'MMMCCCXLV', 'MMMCCCXLVI', 'MMMCCCXLVII', 'MMMCCCXLVIII', 'MMMCCCXLIX', 'MMMCCCL', 'MMMCCCLI', 'MMMCCCLII', 'MMMCCCLIII', 'MMMCCCLIV', 'MMMCCCLV', 'MMMCCCLVI', 'MMMCCCLVII', 'MMMCCCLVIII', 'MMMCCCLIX', 'MMMCCCLX', 'MMMCCCLXI', 'MMMCCCLXII', 'MMMCCCLXIII', 'MMMCCCLXIV', 'MMMCCCLXV', 'MMMCCCLXVI', 'MMMCCCLXVII', 'MMMCCCLXVIII', 'MMMCCCLXIX', 'MMMCCCLXX', 'MMMCCCLXXI', 'MMMCCCLXXII', 'MMMCCCLXXIII', 'MMMCCCLXXIV', 'MMMCCCLXXV', 'MMMCCCLXXVI', 'MMMCCCLXXVII', 'MMMCCCLXXVIII', 'MMMCCCLXXIX', 'MMMCCCLXXX', 'MMMCCCLXXXI', 'MMMCCCLXXXII', 'MMMCCCLXXXIII', 'MMMCCCLXXXIV', 'MMMCCCLXXXV', 'MMMCCCLXXXVI', 'MMMCCCLXXXVII', 'MMMCCCLXXXVIII', 'MMMCCCLXXXIX', 'MMMCCCXC', 'MMMCCCXCI', 'MMMCCCXCII', 'MMMCCCXCIII', 'MMMCCCXCIV', 'MMMCCCXCV', 'MMMCCCXCVI', 'MMMCCCXCVII', 'MMMCCCXCVIII', 'MMMCCCXCIX', 'MMMCD', 'MMMCDI', 'MMMCDII', 'MMMCDIII', 'MMMCDIV', 'MMMCDV', 'MMMCDVI', 'MMMCDVII', 'MMMCDVIII', 'MMMCDIX', 'MMMCDX', 'MMMCDXI', 'MMMCDXII', 'MMMCDXIII', 'MMMCDXIV', 'MMMCDXV', 'MMMCDXVI', 'MMMCDXVII', 'MMMCDXVIII', 'MMMCDXIX', 'MMMCDXX', 'MMMCDXXI', 'MMMCDXXII', 'MMMCDXXIII', 'MMMCDXXIV', 'MMMCDXXV', 'MMMCDXXVI', 'MMMCDXXVII', 'MMMCDXXVIII', 'MMMCDXXIX', 'MMMCDXXX', 'MMMCDXXXI', 'MMMCDXXXII', 'MMMCDXXXIII', 'MMMCDXXXIV', 'MMMCDXXXV', 'MMMCDXXXVI', 'MMMCDXXXVII', 'MMMCDXXXVIII', 'MMMCDXXXIX', 'MMMCDXL', 'MMMCDXLI', 'MMMCDXLII', 'MMMCDXLIII', 'MMMCDXLIV', 'MMMCDXLV', 'MMMCDXLVI', 'MMMCDXLVII', 'MMMCDXLVIII', 'MMMCDXLIX', 'MMMCDL', 'MMMCDLI', 'MMMCDLII', 'MMMCDLIII', 'MMMCDLIV', 'MMMCDLV', 'MMMCDLVI', 'MMMCDLVII', 'MMMCDLVIII', 'MMMCDLIX', 'MMMCDLX', 'MMMCDLXI', 'MMMCDLXII', 'MMMCDLXIII', 'MMMCDLXIV', 'MMMCDLXV', 'MMMCDLXVI', 'MMMCDLXVII', 'MMMCDLXVIII', 'MMMCDLXIX', 'MMMCDLXX', 'MMMCDLXXI', 'MMMCDLXXII', 'MMMCDLXXIII', 'MMMCDLXXIV', 'MMMCDLXXV', 'MMMCDLXXVI', 'MMMCDLXXVII', 'MMMCDLXXVIII', 'MMMCDLXXIX', 'MMMCDLXXX', 'MMMCDLXXXI', 'MMMCDLXXXII', 'MMMCDLXXXIII', 'MMMCDLXXXIV', 'MMMCDLXXXV', 'MMMCDLXXXVI', 'MMMCDLXXXVII', 'MMMCDLXXXVIII', 'MMMCDLXXXIX', 'MMMCDXC', 'MMMCDXCI', 'MMMCDXCII', 'MMMCDXCIII', 'MMMCDXCIV', 'MMMCDXCV', 'MMMCDXCVI', 'MMMCDXCVII', 'MMMCDXCVIII', 'MMMCDXCIX', 'MMMD', 'MMMDI', 'MMMDII', 'MMMDIII', 'MMMDIV', 'MMMDV', 'MMMDVI', 'MMMDVII', 'MMMDVIII', 'MMMDIX', 'MMMDX', 'MMMDXI', 'MMMDXII', 'MMMDXIII', 'MMMDXIV', 'MMMDXV', 'MMMDXVI', 'MMMDXVII', 'MMMDXVIII', 'MMMDXIX', 'MMMDXX', 'MMMDXXI', 'MMMDXXII', 'MMMDXXIII', 'MMMDXXIV', 'MMMDXXV', 'MMMDXXVI', 'MMMDXXVII', 'MMMDXXVIII', 'MMMDXXIX', 'MMMDXXX', 'MMMDXXXI', 'MMMDXXXII', 'MMMDXXXIII', 'MMMDXXXIV', 'MMMDXXXV', 'MMMDXXXVI', 'MMMDXXXVII', 'MMMDXXXVIII', 'MMMDXXXIX', 'MMMDXL', 'MMMDXLI', 'MMMDXLII', 'MMMDXLIII', 'MMMDXLIV', 'MMMDXLV', 'MMMDXLVI', 'MMMDXLVII', 'MMMDXLVIII', 'MMMDXLIX', 'MMMDL', 'MMMDLI', 'MMMDLII', 'MMMDLIII', 'MMMDLIV', 'MMMDLV', 'MMMDLVI', 'MMMDLVII', 'MMMDLVIII', 'MMMDLIX', 'MMMDLX', 'MMMDLXI', 'MMMDLXII', 'MMMDLXIII', 'MMMDLXIV', 'MMMDLXV', 'MMMDLXVI', 'MMMDLXVII', 'MMMDLXVIII', 'MMMDLXIX', 'MMMDLXX', 'MMMDLXXI', 'MMMDLXXII', 'MMMDLXXIII', 'MMMDLXXIV', 'MMMDLXXV', 'MMMDLXXVI', 'MMMDLXXVII', 'MMMDLXXVIII', 'MMMDLXXIX', 'MMMDLXXX', 'MMMDLXXXI', 'MMMDLXXXII', 'MMMDLXXXIII', 'MMMDLXXXIV', 'MMMDLXXXV', 'MMMDLXXXVI', 'MMMDLXXXVII', 'MMMDLXXXVIII', 'MMMDLXXXIX', 'MMMDXC', 'MMMDXCI', 'MMMDXCII', 'MMMDXCIII', 'MMMDXCIV', 'MMMDXCV', 'MMMDXCVI', 'MMMDXCVII', 'MMMDXCVIII', 'MMMDXCIX', 'MMMDC', 'MMMDCI', 'MMMDCII', 'MMMDCIII', 'MMMDCIV', 'MMMDCV', 'MMMDCVI', 'MMMDCVII', 'MMMDCVIII', 'MMMDCIX', 'MMMDCX', 'MMMDCXI', 'MMMDCXII', 'MMMDCXIII', 'MMMDCXIV', 'MMMDCXV', 'MMMDCXVI', 'MMMDCXVII', 'MMMDCXVIII', 'MMMDCXIX', 'MMMDCXX', 'MMMDCXXI', 'MMMDCXXII', 'MMMDCXXIII', 'MMMDCXXIV', 'MMMDCXXV', 'MMMDCXXVI', 'MMMDCXXVII', 'MMMDCXXVIII', 'MMMDCXXIX', 'MMMDCXXX', 'MMMDCXXXI', 'MMMDCXXXII', 'MMMDCXXXIII', 'MMMDCXXXIV', 'MMMDCXXXV', 'MMMDCXXXVI', 'MMMDCXXXVII', 'MMMDCXXXVIII', 'MMMDCXXXIX', 'MMMDCXL', 'MMMDCXLI', 'MMMDCXLII', 'MMMDCXLIII', 'MMMDCXLIV', 'MMMDCXLV', 'MMMDCXLVI', 'MMMDCXLVII', 'MMMDCXLVIII', 'MMMDCXLIX', 'MMMDCL', 'MMMDCLI', 'MMMDCLII', 'MMMDCLIII', 'MMMDCLIV', 'MMMDCLV', 'MMMDCLVI', 'MMMDCLVII', 'MMMDCLVIII', 'MMMDCLIX', 'MMMDCLX', 'MMMDCLXI', 'MMMDCLXII', 'MMMDCLXIII', 'MMMDCLXIV', 'MMMDCLXV', 'MMMDCLXVI', 'MMMDCLXVII', 'MMMDCLXVIII', 'MMMDCLXIX', 'MMMDCLXX', 'MMMDCLXXI', 'MMMDCLXXII', 'MMMDCLXXIII', 'MMMDCLXXIV', 'MMMDCLXXV', 'MMMDCLXXVI', 'MMMDCLXXVII', 'MMMDCLXXVIII', 'MMMDCLXXIX', 'MMMDCLXXX', 'MMMDCLXXXI', 'MMMDCLXXXII', 'MMMDCLXXXIII', 'MMMDCLXXXIV', 'MMMDCLXXXV', 'MMMDCLXXXVI', 'MMMDCLXXXVII', 'MMMDCLXXXVIII', 'MMMDCLXXXIX', 'MMMDCXC', 'MMMDCXCI', 'MMMDCXCII', 'MMMDCXCIII', 'MMMDCXCIV', 'MMMDCXCV', 'MMMDCXCVI', 'MMMDCXCVII', 'MMMDCXCVIII', 'MMMDCXCIX', 'MMMDCC', 'MMMDCCI', 'MMMDCCII', 'MMMDCCIII', 'MMMDCCIV', 'MMMDCCV', 'MMMDCCVI', 'MMMDCCVII', 'MMMDCCVIII', 'MMMDCCIX', 'MMMDCCX', 'MMMDCCXI', 'MMMDCCXII', 'MMMDCCXIII', 'MMMDCCXIV', 'MMMDCCXV', 'MMMDCCXVI', 'MMMDCCXVII', 'MMMDCCXVIII', 'MMMDCCXIX', 'MMMDCCXX', 'MMMDCCXXI', 'MMMDCCXXII', 'MMMDCCXXIII', 'MMMDCCXXIV', 'MMMDCCXXV', 'MMMDCCXXVI', 'MMMDCCXXVII', 'MMMDCCXXVIII', 'MMMDCCXXIX', 'MMMDCCXXX', 'MMMDCCXXXI', 'MMMDCCXXXII', 'MMMDCCXXXIII', 'MMMDCCXXXIV', 'MMMDCCXXXV', 'MMMDCCXXXVI', 'MMMDCCXXXVII', 'MMMDCCXXXVIII', 'MMMDCCXXXIX', 'MMMDCCXL', 'MMMDCCXLI', 'MMMDCCXLII', 'MMMDCCXLIII', 'MMMDCCXLIV', 'MMMDCCXLV', 'MMMDCCXLVI', 'MMMDCCXLVII', 'MMMDCCXLVIII', 'MMMDCCXLIX', 'MMMDCCL', 'MMMDCCLI', 'MMMDCCLII', 'MMMDCCLIII', 'MMMDCCLIV', 'MMMDCCLV', 'MMMDCCLVI', 'MMMDCCLVII', 'MMMDCCLVIII', 'MMMDCCLIX', 'MMMDCCLX', 'MMMDCCLXI', 'MMMDCCLXII', 'MMMDCCLXIII', 'MMMDCCLXIV', 'MMMDCCLXV', 'MMMDCCLXVI', 'MMMDCCLXVII', 'MMMDCCLXVIII', 'MMMDCCLXIX', 'MMMDCCLXX', 'MMMDCCLXXI', 'MMMDCCLXXII', 'MMMDCCLXXIII', 'MMMDCCLXXIV', 'MMMDCCLXXV', 'MMMDCCLXXVI', 'MMMDCCLXXVII', 'MMMDCCLXXVIII', 'MMMDCCLXXIX', 'MMMDCCLXXX', 'MMMDCCLXXXI', 'MMMDCCLXXXII', 'MMMDCCLXXXIII', 'MMMDCCLXXXIV', 'MMMDCCLXXXV', 'MMMDCCLXXXVI', 'MMMDCCLXXXVII', 'MMMDCCLXXXVIII', 'MMMDCCLXXXIX', 'MMMDCCXC', 'MMMDCCXCI', 'MMMDCCXCII', 'MMMDCCXCIII', 'MMMDCCXCIV', 'MMMDCCXCV', 'MMMDCCXCVI', 'MMMDCCXCVII', 'MMMDCCXCVIII', 'MMMDCCXCIX', 'MMMDCCC', 'MMMDCCCI', 'MMMDCCCII', 'MMMDCCCIII', 'MMMDCCCIV', 'MMMDCCCV', 'MMMDCCCVI', 'MMMDCCCVII', 'MMMDCCCVIII', 'MMMDCCCIX', 'MMMDCCCX', 'MMMDCCCXI', 'MMMDCCCXII', 'MMMDCCCXIII', 'MMMDCCCXIV', 'MMMDCCCXV', 'MMMDCCCXVI', 'MMMDCCCXVII', 'MMMDCCCXVIII', 'MMMDCCCXIX', 'MMMDCCCXX', 'MMMDCCCXXI', 'MMMDCCCXXII', 'MMMDCCCXXIII', 'MMMDCCCXXIV', 'MMMDCCCXXV', 'MMMDCCCXXVI', 'MMMDCCCXXVII', 'MMMDCCCXXVIII', 'MMMDCCCXXIX', 'MMMDCCCXXX', 'MMMDCCCXXXI', 'MMMDCCCXXXII', 'MMMDCCCXXXIII', 'MMMDCCCXXXIV', 'MMMDCCCXXXV', 'MMMDCCCXXXVI', 'MMMDCCCXXXVII', 'MMMDCCCXXXVIII', 'MMMDCCCXXXIX', 'MMMDCCCXL', 'MMMDCCCXLI', 'MMMDCCCXLII', 'MMMDCCCXLIII', 'MMMDCCCXLIV', 'MMMDCCCXLV', 'MMMDCCCXLVI', 'MMMDCCCXLVII', 'MMMDCCCXLVIII', 'MMMDCCCXLIX', 'MMMDCCCL', 'MMMDCCCLI', 'MMMDCCCLII', 'MMMDCCCLIII', 'MMMDCCCLIV', 'MMMDCCCLV', 'MMMDCCCLVI', 'MMMDCCCLVII', 'MMMDCCCLVIII', 'MMMDCCCLIX', 'MMMDCCCLX', 'MMMDCCCLXI', 'MMMDCCCLXII', 'MMMDCCCLXIII', 'MMMDCCCLXIV', 'MMMDCCCLXV', 'MMMDCCCLXVI', 'MMMDCCCLXVII', 'MMMDCCCLXVIII', 'MMMDCCCLXIX', 'MMMDCCCLXX', 'MMMDCCCLXXI', 'MMMDCCCLXXII', 'MMMDCCCLXXIII', 'MMMDCCCLXXIV', 'MMMDCCCLXXV', 'MMMDCCCLXXVI', 'MMMDCCCLXXVII', 'MMMDCCCLXXVIII', 'MMMDCCCLXXIX', 'MMMDCCCLXXX', 'MMMDCCCLXXXI', 'MMMDCCCLXXXII', 'MMMDCCCLXXXIII', 'MMMDCCCLXXXIV', 'MMMDCCCLXXXV', 'MMMDCCCLXXXVI', 'MMMDCCCLXXXVII', 'MMMDCCCLXXXVIII', 'MMMDCCCLXXXIX', 'MMMDCCCXC', 'MMMDCCCXCI', 'MMMDCCCXCII', 'MMMDCCCXCIII', 'MMMDCCCXCIV', 'MMMDCCCXCV', 'MMMDCCCXCVI', 'MMMDCCCXCVII', 'MMMDCCCXCVIII', 'MMMDCCCXCIX', 'MMMCM', 'MMMCMI', 'MMMCMII', 'MMMCMIII', 'MMMCMIV', 'MMMCMV', 'MMMCMVI', 'MMMCMVII', 'MMMCMVIII', 'MMMCMIX', 'MMMCMX', 'MMMCMXI', 'MMMCMXII', 'MMMCMXIII', 'MMMCMXIV', 'MMMCMXV', 'MMMCMXVI', 'MMMCMXVII', 'MMMCMXVIII', 'MMMCMXIX', 'MMMCMXX', 'MMMCMXXI', 'MMMCMXXII', 'MMMCMXXIII', 'MMMCMXXIV', 'MMMCMXXV', 'MMMCMXXVI', 'MMMCMXXVII', 'MMMCMXXVIII', 'MMMCMXXIX', 'MMMCMXXX', 'MMMCMXXXI', 'MMMCMXXXII', 'MMMCMXXXIII', 'MMMCMXXXIV', 'MMMCMXXXV', 'MMMCMXXXVI', 'MMMCMXXXVII', 'MMMCMXXXVIII', 'MMMCMXXXIX', 'MMMCMXL', 'MMMCMXLI', 'MMMCMXLII', 'MMMCMXLIII', 'MMMCMXLIV', 'MMMCMXLV', 'MMMCMXLVI', 'MMMCMXLVII', 'MMMCMXLVIII', 'MMMCMXLIX', 'MMMCML', 'MMMCMLI', 'MMMCMLII', 'MMMCMLIII', 'MMMCMLIV', 'MMMCMLV', 'MMMCMLVI', 'MMMCMLVII', 'MMMCMLVIII', 'MMMCMLIX', 'MMMCMLX', 'MMMCMLXI', 'MMMCMLXII', 'MMMCMLXIII', 'MMMCMLXIV', 'MMMCMLXV', 'MMMCMLXVI', 'MMMCMLXVII', 'MMMCMLXVIII', 'MMMCMLXIX', 'MMMCMLXX', 'MMMCMLXXI', 'MMMCMLXXII', 'MMMCMLXXIII', 'MMMCMLXXIV', 'MMMCMLXXV', 'MMMCMLXXVI', 'MMMCMLXXVII', 'MMMCMLXXVIII', 'MMMCMLXXIX', 'MMMCMLXXX', 'MMMCMLXXXI', 'MMMCMLXXXII', 'MMMCMLXXXIII', 'MMMCMLXXXIV', 'MMMCMLXXXV', 'MMMCMLXXXVI', 'MMMCMLXXXVII', 'MMMCMLXXXVIII', 'MMMCMLXXXIX', 'MMMCMXC', 'MMMCMXCI', 'MMMCMXCII', 'MMMCMXCIII', 'MMMCMXCIV', 'MMMCMXCV', 'MMMCMXCVI', 'MMMCMXCVII', 'MMMCMXCVIII', 'MMMCMXCIX', ] -const mode1 = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX', 'XXI', 'XXII', 'XXIII', 'XXIV', 'XXV', 'XXVI', 'XXVII', 'XXVIII', 'XXIX', 'XXX', 'XXXI', 'XXXII', 'XXXIII', 'XXXIV', 'XXXV', 'XXXVI', 'XXXVII', 'XXXVIII', 'XXXIX', 'XL', 'XLI', 'XLII', 'XLIII', 'XLIV', 'VL', 'VLI', 'VLII', 'VLIII', 'VLIV', 'L', 'LI', 'LII', 'LIII', 'LIV', 'LV', 'LVI', 'LVII', 'LVIII', 'LIX', 'LX', 'LXI', 'LXII', 'LXIII', 'LXIV', 'LXV', 'LXVI', 'LXVII', 'LXVIII', 'LXIX', 'LXX', 'LXXI', 'LXXII', 'LXXIII', 'LXXIV', 'LXXV', 'LXXVI', 'LXXVII', 'LXXVIII', 'LXXIX', 'LXXX', 'LXXXI', 'LXXXII', 'LXXXIII', 'LXXXIV', 'LXXXV', 'LXXXVI', 'LXXXVII', 'LXXXVIII', 'LXXXIX', 'XC', 'XCI', 'XCII', 'XCIII', 'XCIV', 'VC', 'VCI', 'VCII', 'VCIII', 'VCIV', 'C', 'CI', 'CII', 'CIII', 'CIV', 'CV', 'CVI', 'CVII', 'CVIII', 'CIX', 'CX', 'CXI', 'CXII', 'CXIII', 'CXIV', 'CXV', 'CXVI', 'CXVII', 'CXVIII', 'CXIX', 'CXX', 'CXXI', 'CXXII', 'CXXIII', 'CXXIV', 'CXXV', 'CXXVI', 'CXXVII', 'CXXVIII', 'CXXIX', 'CXXX', 'CXXXI', 'CXXXII', 'CXXXIII', 'CXXXIV', 'CXXXV', 'CXXXVI', 'CXXXVII', 'CXXXVIII', 'CXXXIX', 'CXL', 'CXLI', 'CXLII', 'CXLIII', 'CXLIV', 'CVL', 'CVLI', 'CVLII', 'CVLIII', 'CVLIV', 'CL', 'CLI', 'CLII', 'CLIII', 'CLIV', 'CLV', 'CLVI', 'CLVII', 'CLVIII', 'CLIX', 'CLX', 'CLXI', 'CLXII', 'CLXIII', 'CLXIV', 'CLXV', 'CLXVI', 'CLXVII', 'CLXVIII', 'CLXIX', 'CLXX', 'CLXXI', 'CLXXII', 'CLXXIII', 'CLXXIV', 'CLXXV', 'CLXXVI', 'CLXXVII', 'CLXXVIII', 'CLXXIX', 'CLXXX', 'CLXXXI', 'CLXXXII', 'CLXXXIII', 'CLXXXIV', 'CLXXXV', 'CLXXXVI', 'CLXXXVII', 'CLXXXVIII', 'CLXXXIX', 'CXC', 'CXCI', 'CXCII', 'CXCIII', 'CXCIV', 'CVC', 'CVCI', 'CVCII', 'CVCIII', 'CVCIV', 'CC', 'CCI', 'CCII', 'CCIII', 'CCIV', 'CCV', 'CCVI', 'CCVII', 'CCVIII', 'CCIX', 'CCX', 'CCXI', 'CCXII', 'CCXIII', 'CCXIV', 'CCXV', 'CCXVI', 'CCXVII', 'CCXVIII', 'CCXIX', 'CCXX', 'CCXXI', 'CCXXII', 'CCXXIII', 'CCXXIV', 'CCXXV', 'CCXXVI', 'CCXXVII', 'CCXXVIII', 'CCXXIX', 'CCXXX', 'CCXXXI', 'CCXXXII', 'CCXXXIII', 'CCXXXIV', 'CCXXXV', 'CCXXXVI', 'CCXXXVII', 'CCXXXVIII', 'CCXXXIX', 'CCXL', 'CCXLI', 'CCXLII', 'CCXLIII', 'CCXLIV', 'CCVL', 'CCVLI', 'CCVLII', 'CCVLIII', 'CCVLIV', 'CCL', 'CCLI', 'CCLII', 'CCLIII', 'CCLIV', 'CCLV', 'CCLVI', 'CCLVII', 'CCLVIII', 'CCLIX', 'CCLX', 'CCLXI', 'CCLXII', 'CCLXIII', 'CCLXIV', 'CCLXV', 'CCLXVI', 'CCLXVII', 'CCLXVIII', 'CCLXIX', 'CCLXX', 'CCLXXI', 'CCLXXII', 'CCLXXIII', 'CCLXXIV', 'CCLXXV', 'CCLXXVI', 'CCLXXVII', 'CCLXXVIII', 'CCLXXIX', 'CCLXXX', 'CCLXXXI', 'CCLXXXII', 'CCLXXXIII', 'CCLXXXIV', 'CCLXXXV', 'CCLXXXVI', 'CCLXXXVII', 'CCLXXXVIII', 'CCLXXXIX', 'CCXC', 'CCXCI', 'CCXCII', 'CCXCIII', 'CCXCIV', 'CCVC', 'CCVCI', 'CCVCII', 'CCVCIII', 'CCVCIV', 'CCC', 'CCCI', 'CCCII', 'CCCIII', 'CCCIV', 'CCCV', 'CCCVI', 'CCCVII', 'CCCVIII', 'CCCIX', 'CCCX', 'CCCXI', 'CCCXII', 'CCCXIII', 'CCCXIV', 'CCCXV', 'CCCXVI', 'CCCXVII', 'CCCXVIII', 'CCCXIX', 'CCCXX', 'CCCXXI', 'CCCXXII', 'CCCXXIII', 'CCCXXIV', 'CCCXXV', 'CCCXXVI', 'CCCXXVII', 'CCCXXVIII', 'CCCXXIX', 'CCCXXX', 'CCCXXXI', 'CCCXXXII', 'CCCXXXIII', 'CCCXXXIV', 'CCCXXXV', 'CCCXXXVI', 'CCCXXXVII', 'CCCXXXVIII', 'CCCXXXIX', 'CCCXL', 'CCCXLI', 'CCCXLII', 'CCCXLIII', 'CCCXLIV', 'CCCVL', 'CCCVLI', 'CCCVLII', 'CCCVLIII', 'CCCVLIV', 'CCCL', 'CCCLI', 'CCCLII', 'CCCLIII', 'CCCLIV', 'CCCLV', 'CCCLVI', 'CCCLVII', 'CCCLVIII', 'CCCLIX', 'CCCLX', 'CCCLXI', 'CCCLXII', 'CCCLXIII', 'CCCLXIV', 'CCCLXV', 'CCCLXVI', 'CCCLXVII', 'CCCLXVIII', 'CCCLXIX', 'CCCLXX', 'CCCLXXI', 'CCCLXXII', 'CCCLXXIII', 'CCCLXXIV', 'CCCLXXV', 'CCCLXXVI', 'CCCLXXVII', 'CCCLXXVIII', 'CCCLXXIX', 'CCCLXXX', 'CCCLXXXI', 'CCCLXXXII', 'CCCLXXXIII', 'CCCLXXXIV', 'CCCLXXXV', 'CCCLXXXVI', 'CCCLXXXVII', 'CCCLXXXVIII', 'CCCLXXXIX', 'CCCXC', 'CCCXCI', 'CCCXCII', 'CCCXCIII', 'CCCXCIV', 'CCCVC', 'CCCVCI', 'CCCVCII', 'CCCVCIII', 'CCCVCIV', 'CD', 'CDI', 'CDII', 'CDIII', 'CDIV', 'CDV', 'CDVI', 'CDVII', 'CDVIII', 'CDIX', 'CDX', 'CDXI', 'CDXII', 'CDXIII', 'CDXIV', 'CDXV', 'CDXVI', 'CDXVII', 'CDXVIII', 'CDXIX', 'CDXX', 'CDXXI', 'CDXXII', 'CDXXIII', 'CDXXIV', 'CDXXV', 'CDXXVI', 'CDXXVII', 'CDXXVIII', 'CDXXIX', 'CDXXX', 'CDXXXI', 'CDXXXII', 'CDXXXIII', 'CDXXXIV', 'CDXXXV', 'CDXXXVI', 'CDXXXVII', 'CDXXXVIII', 'CDXXXIX', 'CDXL', 'CDXLI', 'CDXLII', 'CDXLIII', 'CDXLIV', 'CDVL', 'CDVLI', 'CDVLII', 'CDVLIII', 'CDVLIV', 'LD', 'LDI', 'LDII', 'LDIII', 'LDIV', 'LDV', 'LDVI', 'LDVII', 'LDVIII', 'LDIX', 'LDX', 'LDXI', 'LDXII', 'LDXIII', 'LDXIV', 'LDXV', 'LDXVI', 'LDXVII', 'LDXVIII', 'LDXIX', 'LDXX', 'LDXXI', 'LDXXII', 'LDXXIII', 'LDXXIV', 'LDXXV', 'LDXXVI', 'LDXXVII', 'LDXXVIII', 'LDXXIX', 'LDXXX', 'LDXXXI', 'LDXXXII', 'LDXXXIII', 'LDXXXIV', 'LDXXXV', 'LDXXXVI', 'LDXXXVII', 'LDXXXVIII', 'LDXXXIX', 'LDXL', 'LDXLI', 'LDXLII', 'LDXLIII', 'LDXLIV', 'LDVL', 'LDVLI', 'LDVLII', 'LDVLIII', 'LDVLIV', 'D', 'DI', 'DII', 'DIII', 'DIV', 'DV', 'DVI', 'DVII', 'DVIII', 'DIX', 'DX', 'DXI', 'DXII', 'DXIII', 'DXIV', 'DXV', 'DXVI', 'DXVII', 'DXVIII', 'DXIX', 'DXX', 'DXXI', 'DXXII', 'DXXIII', 'DXXIV', 'DXXV', 'DXXVI', 'DXXVII', 'DXXVIII', 'DXXIX', 'DXXX', 'DXXXI', 'DXXXII', 'DXXXIII', 'DXXXIV', 'DXXXV', 'DXXXVI', 'DXXXVII', 'DXXXVIII', 'DXXXIX', 'DXL', 'DXLI', 'DXLII', 'DXLIII', 'DXLIV', 'DVL', 'DVLI', 'DVLII', 'DVLIII', 'DVLIV', 'DL', 'DLI', 'DLII', 'DLIII', 'DLIV', 'DLV', 'DLVI', 'DLVII', 'DLVIII', 'DLIX', 'DLX', 'DLXI', 'DLXII', 'DLXIII', 'DLXIV', 'DLXV', 'DLXVI', 'DLXVII', 'DLXVIII', 'DLXIX', 'DLXX', 'DLXXI', 'DLXXII', 'DLXXIII', 'DLXXIV', 'DLXXV', 'DLXXVI', 'DLXXVII', 'DLXXVIII', 'DLXXIX', 'DLXXX', 'DLXXXI', 'DLXXXII', 'DLXXXIII', 'DLXXXIV', 'DLXXXV', 'DLXXXVI', 'DLXXXVII', 'DLXXXVIII', 'DLXXXIX', 'DXC', 'DXCI', 'DXCII', 'DXCIII', 'DXCIV', 'DVC', 'DVCI', 'DVCII', 'DVCIII', 'DVCIV', 'DC', 'DCI', 'DCII', 'DCIII', 'DCIV', 'DCV', 'DCVI', 'DCVII', 'DCVIII', 'DCIX', 'DCX', 'DCXI', 'DCXII', 'DCXIII', 'DCXIV', 'DCXV', 'DCXVI', 'DCXVII', 'DCXVIII', 'DCXIX', 'DCXX', 'DCXXI', 'DCXXII', 'DCXXIII', 'DCXXIV', 'DCXXV', 'DCXXVI', 'DCXXVII', 'DCXXVIII', 'DCXXIX', 'DCXXX', 'DCXXXI', 'DCXXXII', 'DCXXXIII', 'DCXXXIV', 'DCXXXV', 'DCXXXVI', 'DCXXXVII', 'DCXXXVIII', 'DCXXXIX', 'DCXL', 'DCXLI', 'DCXLII', 'DCXLIII', 'DCXLIV', 'DCVL', 'DCVLI', 'DCVLII', 'DCVLIII', 'DCVLIV', 'DCL', 'DCLI', 'DCLII', 'DCLIII', 'DCLIV', 'DCLV', 'DCLVI', 'DCLVII', 'DCLVIII', 'DCLIX', 'DCLX', 'DCLXI', 'DCLXII', 'DCLXIII', 'DCLXIV', 'DCLXV', 'DCLXVI', 'DCLXVII', 'DCLXVIII', 'DCLXIX', 'DCLXX', 'DCLXXI', 'DCLXXII', 'DCLXXIII', 'DCLXXIV', 'DCLXXV', 'DCLXXVI', 'DCLXXVII', 'DCLXXVIII', 'DCLXXIX', 'DCLXXX', 'DCLXXXI', 'DCLXXXII', 'DCLXXXIII', 'DCLXXXIV', 'DCLXXXV', 'DCLXXXVI', 'DCLXXXVII', 'DCLXXXVIII', 'DCLXXXIX', 'DCXC', 'DCXCI', 'DCXCII', 'DCXCIII', 'DCXCIV', 'DCVC', 'DCVCI', 'DCVCII', 'DCVCIII', 'DCVCIV', 'DCC', 'DCCI', 'DCCII', 'DCCIII', 'DCCIV', 'DCCV', 'DCCVI', 'DCCVII', 'DCCVIII', 'DCCIX', 'DCCX', 'DCCXI', 'DCCXII', 'DCCXIII', 'DCCXIV', 'DCCXV', 'DCCXVI', 'DCCXVII', 'DCCXVIII', 'DCCXIX', 'DCCXX', 'DCCXXI', 'DCCXXII', 'DCCXXIII', 'DCCXXIV', 'DCCXXV', 'DCCXXVI', 'DCCXXVII', 'DCCXXVIII', 'DCCXXIX', 'DCCXXX', 'DCCXXXI', 'DCCXXXII', 'DCCXXXIII', 'DCCXXXIV', 'DCCXXXV', 'DCCXXXVI', 'DCCXXXVII', 'DCCXXXVIII', 'DCCXXXIX', 'DCCXL', 'DCCXLI', 'DCCXLII', 'DCCXLIII', 'DCCXLIV', 'DCCVL', 'DCCVLI', 'DCCVLII', 'DCCVLIII', 'DCCVLIV', 'DCCL', 'DCCLI', 'DCCLII', 'DCCLIII', 'DCCLIV', 'DCCLV', 'DCCLVI', 'DCCLVII', 'DCCLVIII', 'DCCLIX', 'DCCLX', 'DCCLXI', 'DCCLXII', 'DCCLXIII', 'DCCLXIV', 'DCCLXV', 'DCCLXVI', 'DCCLXVII', 'DCCLXVIII', 'DCCLXIX', 'DCCLXX', 'DCCLXXI', 'DCCLXXII', 'DCCLXXIII', 'DCCLXXIV', 'DCCLXXV', 'DCCLXXVI', 'DCCLXXVII', 'DCCLXXVIII', 'DCCLXXIX', 'DCCLXXX', 'DCCLXXXI', 'DCCLXXXII', 'DCCLXXXIII', 'DCCLXXXIV', 'DCCLXXXV', 'DCCLXXXVI', 'DCCLXXXVII', 'DCCLXXXVIII', 'DCCLXXXIX', 'DCCXC', 'DCCXCI', 'DCCXCII', 'DCCXCIII', 'DCCXCIV', 'DCCVC', 'DCCVCI', 'DCCVCII', 'DCCVCIII', 'DCCVCIV', 'DCCC', 'DCCCI', 'DCCCII', 'DCCCIII', 'DCCCIV', 'DCCCV', 'DCCCVI', 'DCCCVII', 'DCCCVIII', 'DCCCIX', 'DCCCX', 'DCCCXI', 'DCCCXII', 'DCCCXIII', 'DCCCXIV', 'DCCCXV', 'DCCCXVI', 'DCCCXVII', 'DCCCXVIII', 'DCCCXIX', 'DCCCXX', 'DCCCXXI', 'DCCCXXII', 'DCCCXXIII', 'DCCCXXIV', 'DCCCXXV', 'DCCCXXVI', 'DCCCXXVII', 'DCCCXXVIII', 'DCCCXXIX', 'DCCCXXX', 'DCCCXXXI', 'DCCCXXXII', 'DCCCXXXIII', 'DCCCXXXIV', 'DCCCXXXV', 'DCCCXXXVI', 'DCCCXXXVII', 'DCCCXXXVIII', 'DCCCXXXIX', 'DCCCXL', 'DCCCXLI', 'DCCCXLII', 'DCCCXLIII', 'DCCCXLIV', 'DCCCVL', 'DCCCVLI', 'DCCCVLII', 'DCCCVLIII', 'DCCCVLIV', 'DCCCL', 'DCCCLI', 'DCCCLII', 'DCCCLIII', 'DCCCLIV', 'DCCCLV', 'DCCCLVI', 'DCCCLVII', 'DCCCLVIII', 'DCCCLIX', 'DCCCLX', 'DCCCLXI', 'DCCCLXII', 'DCCCLXIII', 'DCCCLXIV', 'DCCCLXV', 'DCCCLXVI', 'DCCCLXVII', 'DCCCLXVIII', 'DCCCLXIX', 'DCCCLXX', 'DCCCLXXI', 'DCCCLXXII', 'DCCCLXXIII', 'DCCCLXXIV', 'DCCCLXXV', 'DCCCLXXVI', 'DCCCLXXVII', 'DCCCLXXVIII', 'DCCCLXXIX', 'DCCCLXXX', 'DCCCLXXXI', 'DCCCLXXXII', 'DCCCLXXXIII', 'DCCCLXXXIV', 'DCCCLXXXV', 'DCCCLXXXVI', 'DCCCLXXXVII', 'DCCCLXXXVIII', 'DCCCLXXXIX', 'DCCCXC', 'DCCCXCI', 'DCCCXCII', 'DCCCXCIII', 'DCCCXCIV', 'DCCCVC', 'DCCCVCI', 'DCCCVCII', 'DCCCVCIII', 'DCCCVCIV', 'CM', 'CMI', 'CMII', 'CMIII', 'CMIV', 'CMV', 'CMVI', 'CMVII', 'CMVIII', 'CMIX', 'CMX', 'CMXI', 'CMXII', 'CMXIII', 'CMXIV', 'CMXV', 'CMXVI', 'CMXVII', 'CMXVIII', 'CMXIX', 'CMXX', 'CMXXI', 'CMXXII', 'CMXXIII', 'CMXXIV', 'CMXXV', 'CMXXVI', 'CMXXVII', 'CMXXVIII', 'CMXXIX', 'CMXXX', 'CMXXXI', 'CMXXXII', 'CMXXXIII', 'CMXXXIV', 'CMXXXV', 'CMXXXVI', 'CMXXXVII', 'CMXXXVIII', 'CMXXXIX', 'CMXL', 'CMXLI', 'CMXLII', 'CMXLIII', 'CMXLIV', 'CMVL', 'CMVLI', 'CMVLII', 'CMVLIII', 'CMVLIV', 'LM', 'LMI', 'LMII', 'LMIII', 'LMIV', 'LMV', 'LMVI', 'LMVII', 'LMVIII', 'LMIX', 'LMX', 'LMXI', 'LMXII', 'LMXIII', 'LMXIV', 'LMXV', 'LMXVI', 'LMXVII', 'LMXVIII', 'LMXIX', 'LMXX', 'LMXXI', 'LMXXII', 'LMXXIII', 'LMXXIV', 'LMXXV', 'LMXXVI', 'LMXXVII', 'LMXXVIII', 'LMXXIX', 'LMXXX', 'LMXXXI', 'LMXXXII', 'LMXXXIII', 'LMXXXIV', 'LMXXXV', 'LMXXXVI', 'LMXXXVII', 'LMXXXVIII', 'LMXXXIX', 'LMXL', 'LMXLI', 'LMXLII', 'LMXLIII', 'LMXLIV', 'LMVL', 'LMVLI', 'LMVLII', 'LMVLIII', 'LMVLIV', 'M', 'MI', 'MII', 'MIII', 'MIV', 'MV', 'MVI', 'MVII', 'MVIII', 'MIX', 'MX', 'MXI', 'MXII', 'MXIII', 'MXIV', 'MXV', 'MXVI', 'MXVII', 'MXVIII', 'MXIX', 'MXX', 'MXXI', 'MXXII', 'MXXIII', 'MXXIV', 'MXXV', 'MXXVI', 'MXXVII', 'MXXVIII', 'MXXIX', 'MXXX', 'MXXXI', 'MXXXII', 'MXXXIII', 'MXXXIV', 'MXXXV', 'MXXXVI', 'MXXXVII', 'MXXXVIII', 'MXXXIX', 'MXL', 'MXLI', 'MXLII', 'MXLIII', 'MXLIV', 'MVL', 'MVLI', 'MVLII', 'MVLIII', 'MVLIV', 'ML', 'MLI', 'MLII', 'MLIII', 'MLIV', 'MLV', 'MLVI', 'MLVII', 'MLVIII', 'MLIX', 'MLX', 'MLXI', 'MLXII', 'MLXIII', 'MLXIV', 'MLXV', 'MLXVI', 'MLXVII', 'MLXVIII', 'MLXIX', 'MLXX', 'MLXXI', 'MLXXII', 'MLXXIII', 'MLXXIV', 'MLXXV', 'MLXXVI', 'MLXXVII', 'MLXXVIII', 'MLXXIX', 'MLXXX', 'MLXXXI', 'MLXXXII', 'MLXXXIII', 'MLXXXIV', 'MLXXXV', 'MLXXXVI', 'MLXXXVII', 'MLXXXVIII', 'MLXXXIX', 'MXC', 'MXCI', 'MXCII', 'MXCIII', 'MXCIV', 'MVC', 'MVCI', 'MVCII', 'MVCIII', 'MVCIV', 'MC', 'MCI', 'MCII', 'MCIII', 'MCIV', 'MCV', 'MCVI', 'MCVII', 'MCVIII', 'MCIX', 'MCX', 'MCXI', 'MCXII', 'MCXIII', 'MCXIV', 'MCXV', 'MCXVI', 'MCXVII', 'MCXVIII', 'MCXIX', 'MCXX', 'MCXXI', 'MCXXII', 'MCXXIII', 'MCXXIV', 'MCXXV', 'MCXXVI', 'MCXXVII', 'MCXXVIII', 'MCXXIX', 'MCXXX', 'MCXXXI', 'MCXXXII', 'MCXXXIII', 'MCXXXIV', 'MCXXXV', 'MCXXXVI', 'MCXXXVII', 'MCXXXVIII', 'MCXXXIX', 'MCXL', 'MCXLI', 'MCXLII', 'MCXLIII', 'MCXLIV', 'MCVL', 'MCVLI', 'MCVLII', 'MCVLIII', 'MCVLIV', 'MCL', 'MCLI', 'MCLII', 'MCLIII', 'MCLIV', 'MCLV', 'MCLVI', 'MCLVII', 'MCLVIII', 'MCLIX', 'MCLX', 'MCLXI', 'MCLXII', 'MCLXIII', 'MCLXIV', 'MCLXV', 'MCLXVI', 'MCLXVII', 'MCLXVIII', 'MCLXIX', 'MCLXX', 'MCLXXI', 'MCLXXII', 'MCLXXIII', 'MCLXXIV', 'MCLXXV', 'MCLXXVI', 'MCLXXVII', 'MCLXXVIII', 'MCLXXIX', 'MCLXXX', 'MCLXXXI', 'MCLXXXII', 'MCLXXXIII', 'MCLXXXIV', 'MCLXXXV', 'MCLXXXVI', 'MCLXXXVII', 'MCLXXXVIII', 'MCLXXXIX', 'MCXC', 'MCXCI', 'MCXCII', 'MCXCIII', 'MCXCIV', 'MCVC', 'MCVCI', 'MCVCII', 'MCVCIII', 'MCVCIV', 'MCC', 'MCCI', 'MCCII', 'MCCIII', 'MCCIV', 'MCCV', 'MCCVI', 'MCCVII', 'MCCVIII', 'MCCIX', 'MCCX', 'MCCXI', 'MCCXII', 'MCCXIII', 'MCCXIV', 'MCCXV', 'MCCXVI', 'MCCXVII', 'MCCXVIII', 'MCCXIX', 'MCCXX', 'MCCXXI', 'MCCXXII', 'MCCXXIII', 'MCCXXIV', 'MCCXXV', 'MCCXXVI', 'MCCXXVII', 'MCCXXVIII', 'MCCXXIX', 'MCCXXX', 'MCCXXXI', 'MCCXXXII', 'MCCXXXIII', 'MCCXXXIV', 'MCCXXXV', 'MCCXXXVI', 'MCCXXXVII', 'MCCXXXVIII', 'MCCXXXIX', 'MCCXL', 'MCCXLI', 'MCCXLII', 'MCCXLIII', 'MCCXLIV', 'MCCVL', 'MCCVLI', 'MCCVLII', 'MCCVLIII', 'MCCVLIV', 'MCCL', 'MCCLI', 'MCCLII', 'MCCLIII', 'MCCLIV', 'MCCLV', 'MCCLVI', 'MCCLVII', 'MCCLVIII', 'MCCLIX', 'MCCLX', 'MCCLXI', 'MCCLXII', 'MCCLXIII', 'MCCLXIV', 'MCCLXV', 'MCCLXVI', 'MCCLXVII', 'MCCLXVIII', 'MCCLXIX', 'MCCLXX', 'MCCLXXI', 'MCCLXXII', 'MCCLXXIII', 'MCCLXXIV', 'MCCLXXV', 'MCCLXXVI', 'MCCLXXVII', 'MCCLXXVIII', 'MCCLXXIX', 'MCCLXXX', 'MCCLXXXI', 'MCCLXXXII', 'MCCLXXXIII', 'MCCLXXXIV', 'MCCLXXXV', 'MCCLXXXVI', 'MCCLXXXVII', 'MCCLXXXVIII', 'MCCLXXXIX', 'MCCXC', 'MCCXCI', 'MCCXCII', 'MCCXCIII', 'MCCXCIV', 'MCCVC', 'MCCVCI', 'MCCVCII', 'MCCVCIII', 'MCCVCIV', 'MCCC', 'MCCCI', 'MCCCII', 'MCCCIII', 'MCCCIV', 'MCCCV', 'MCCCVI', 'MCCCVII', 'MCCCVIII', 'MCCCIX', 'MCCCX', 'MCCCXI', 'MCCCXII', 'MCCCXIII', 'MCCCXIV', 'MCCCXV', 'MCCCXVI', 'MCCCXVII', 'MCCCXVIII', 'MCCCXIX', 'MCCCXX', 'MCCCXXI', 'MCCCXXII', 'MCCCXXIII', 'MCCCXXIV', 'MCCCXXV', 'MCCCXXVI', 'MCCCXXVII', 'MCCCXXVIII', 'MCCCXXIX', 'MCCCXXX', 'MCCCXXXI', 'MCCCXXXII', 'MCCCXXXIII', 'MCCCXXXIV', 'MCCCXXXV', 'MCCCXXXVI', 'MCCCXXXVII', 'MCCCXXXVIII', 'MCCCXXXIX', 'MCCCXL', 'MCCCXLI', 'MCCCXLII', 'MCCCXLIII', 'MCCCXLIV', 'MCCCVL', 'MCCCVLI', 'MCCCVLII', 'MCCCVLIII', 'MCCCVLIV', 'MCCCL', 'MCCCLI', 'MCCCLII', 'MCCCLIII', 'MCCCLIV', 'MCCCLV', 'MCCCLVI', 'MCCCLVII', 'MCCCLVIII', 'MCCCLIX', 'MCCCLX', 'MCCCLXI', 'MCCCLXII', 'MCCCLXIII', 'MCCCLXIV', 'MCCCLXV', 'MCCCLXVI', 'MCCCLXVII', 'MCCCLXVIII', 'MCCCLXIX', 'MCCCLXX', 'MCCCLXXI', 'MCCCLXXII', 'MCCCLXXIII', 'MCCCLXXIV', 'MCCCLXXV', 'MCCCLXXVI', 'MCCCLXXVII', 'MCCCLXXVIII', 'MCCCLXXIX', 'MCCCLXXX', 'MCCCLXXXI', 'MCCCLXXXII', 'MCCCLXXXIII', 'MCCCLXXXIV', 'MCCCLXXXV', 'MCCCLXXXVI', 'MCCCLXXXVII', 'MCCCLXXXVIII', 'MCCCLXXXIX', 'MCCCXC', 'MCCCXCI', 'MCCCXCII', 'MCCCXCIII', 'MCCCXCIV', 'MCCCVC', 'MCCCVCI', 'MCCCVCII', 'MCCCVCIII', 'MCCCVCIV', 'MCD', 'MCDI', 'MCDII', 'MCDIII', 'MCDIV', 'MCDV', 'MCDVI', 'MCDVII', 'MCDVIII', 'MCDIX', 'MCDX', 'MCDXI', 'MCDXII', 'MCDXIII', 'MCDXIV', 'MCDXV', 'MCDXVI', 'MCDXVII', 'MCDXVIII', 'MCDXIX', 'MCDXX', 'MCDXXI', 'MCDXXII', 'MCDXXIII', 'MCDXXIV', 'MCDXXV', 'MCDXXVI', 'MCDXXVII', 'MCDXXVIII', 'MCDXXIX', 'MCDXXX', 'MCDXXXI', 'MCDXXXII', 'MCDXXXIII', 'MCDXXXIV', 'MCDXXXV', 'MCDXXXVI', 'MCDXXXVII', 'MCDXXXVIII', 'MCDXXXIX', 'MCDXL', 'MCDXLI', 'MCDXLII', 'MCDXLIII', 'MCDXLIV', 'MCDVL', 'MCDVLI', 'MCDVLII', 'MCDVLIII', 'MCDVLIV', 'MLD', 'MLDI', 'MLDII', 'MLDIII', 'MLDIV', 'MLDV', 'MLDVI', 'MLDVII', 'MLDVIII', 'MLDIX', 'MLDX', 'MLDXI', 'MLDXII', 'MLDXIII', 'MLDXIV', 'MLDXV', 'MLDXVI', 'MLDXVII', 'MLDXVIII', 'MLDXIX', 'MLDXX', 'MLDXXI', 'MLDXXII', 'MLDXXIII', 'MLDXXIV', 'MLDXXV', 'MLDXXVI', 'MLDXXVII', 'MLDXXVIII', 'MLDXXIX', 'MLDXXX', 'MLDXXXI', 'MLDXXXII', 'MLDXXXIII', 'MLDXXXIV', 'MLDXXXV', 'MLDXXXVI', 'MLDXXXVII', 'MLDXXXVIII', 'MLDXXXIX', 'MLDXL', 'MLDXLI', 'MLDXLII', 'MLDXLIII', 'MLDXLIV', 'MLDVL', 'MLDVLI', 'MLDVLII', 'MLDVLIII', 'MLDVLIV', 'MD', 'MDI', 'MDII', 'MDIII', 'MDIV', 'MDV', 'MDVI', 'MDVII', 'MDVIII', 'MDIX', 'MDX', 'MDXI', 'MDXII', 'MDXIII', 'MDXIV', 'MDXV', 'MDXVI', 'MDXVII', 'MDXVIII', 'MDXIX', 'MDXX', 'MDXXI', 'MDXXII', 'MDXXIII', 'MDXXIV', 'MDXXV', 'MDXXVI', 'MDXXVII', 'MDXXVIII', 'MDXXIX', 'MDXXX', 'MDXXXI', 'MDXXXII', 'MDXXXIII', 'MDXXXIV', 'MDXXXV', 'MDXXXVI', 'MDXXXVII', 'MDXXXVIII', 'MDXXXIX', 'MDXL', 'MDXLI', 'MDXLII', 'MDXLIII', 'MDXLIV', 'MDVL', 'MDVLI', 'MDVLII', 'MDVLIII', 'MDVLIV', 'MDL', 'MDLI', 'MDLII', 'MDLIII', 'MDLIV', 'MDLV', 'MDLVI', 'MDLVII', 'MDLVIII', 'MDLIX', 'MDLX', 'MDLXI', 'MDLXII', 'MDLXIII', 'MDLXIV', 'MDLXV', 'MDLXVI', 'MDLXVII', 'MDLXVIII', 'MDLXIX', 'MDLXX', 'MDLXXI', 'MDLXXII', 'MDLXXIII', 'MDLXXIV', 'MDLXXV', 'MDLXXVI', 'MDLXXVII', 'MDLXXVIII', 'MDLXXIX', 'MDLXXX', 'MDLXXXI', 'MDLXXXII', 'MDLXXXIII', 'MDLXXXIV', 'MDLXXXV', 'MDLXXXVI', 'MDLXXXVII', 'MDLXXXVIII', 'MDLXXXIX', 'MDXC', 'MDXCI', 'MDXCII', 'MDXCIII', 'MDXCIV', 'MDVC', 'MDVCI', 'MDVCII', 'MDVCIII', 'MDVCIV', 'MDC', 'MDCI', 'MDCII', 'MDCIII', 'MDCIV', 'MDCV', 'MDCVI', 'MDCVII', 'MDCVIII', 'MDCIX', 'MDCX', 'MDCXI', 'MDCXII', 'MDCXIII', 'MDCXIV', 'MDCXV', 'MDCXVI', 'MDCXVII', 'MDCXVIII', 'MDCXIX', 'MDCXX', 'MDCXXI', 'MDCXXII', 'MDCXXIII', 'MDCXXIV', 'MDCXXV', 'MDCXXVI', 'MDCXXVII', 'MDCXXVIII', 'MDCXXIX', 'MDCXXX', 'MDCXXXI', 'MDCXXXII', 'MDCXXXIII', 'MDCXXXIV', 'MDCXXXV', 'MDCXXXVI', 'MDCXXXVII', 'MDCXXXVIII', 'MDCXXXIX', 'MDCXL', 'MDCXLI', 'MDCXLII', 'MDCXLIII', 'MDCXLIV', 'MDCVL', 'MDCVLI', 'MDCVLII', 'MDCVLIII', 'MDCVLIV', 'MDCL', 'MDCLI', 'MDCLII', 'MDCLIII', 'MDCLIV', 'MDCLV', 'MDCLVI', 'MDCLVII', 'MDCLVIII', 'MDCLIX', 'MDCLX', 'MDCLXI', 'MDCLXII', 'MDCLXIII', 'MDCLXIV', 'MDCLXV', 'MDCLXVI', 'MDCLXVII', 'MDCLXVIII', 'MDCLXIX', 'MDCLXX', 'MDCLXXI', 'MDCLXXII', 'MDCLXXIII', 'MDCLXXIV', 'MDCLXXV', 'MDCLXXVI', 'MDCLXXVII', 'MDCLXXVIII', 'MDCLXXIX', 'MDCLXXX', 'MDCLXXXI', 'MDCLXXXII', 'MDCLXXXIII', 'MDCLXXXIV', 'MDCLXXXV', 'MDCLXXXVI', 'MDCLXXXVII', 'MDCLXXXVIII', 'MDCLXXXIX', 'MDCXC', 'MDCXCI', 'MDCXCII', 'MDCXCIII', 'MDCXCIV', 'MDCVC', 'MDCVCI', 'MDCVCII', 'MDCVCIII', 'MDCVCIV', 'MDCC', 'MDCCI', 'MDCCII', 'MDCCIII', 'MDCCIV', 'MDCCV', 'MDCCVI', 'MDCCVII', 'MDCCVIII', 'MDCCIX', 'MDCCX', 'MDCCXI', 'MDCCXII', 'MDCCXIII', 'MDCCXIV', 'MDCCXV', 'MDCCXVI', 'MDCCXVII', 'MDCCXVIII', 'MDCCXIX', 'MDCCXX', 'MDCCXXI', 'MDCCXXII', 'MDCCXXIII', 'MDCCXXIV', 'MDCCXXV', 'MDCCXXVI', 'MDCCXXVII', 'MDCCXXVIII', 'MDCCXXIX', 'MDCCXXX', 'MDCCXXXI', 'MDCCXXXII', 'MDCCXXXIII', 'MDCCXXXIV', 'MDCCXXXV', 'MDCCXXXVI', 'MDCCXXXVII', 'MDCCXXXVIII', 'MDCCXXXIX', 'MDCCXL', 'MDCCXLI', 'MDCCXLII', 'MDCCXLIII', 'MDCCXLIV', 'MDCCVL', 'MDCCVLI', 'MDCCVLII', 'MDCCVLIII', 'MDCCVLIV', 'MDCCL', 'MDCCLI', 'MDCCLII', 'MDCCLIII', 'MDCCLIV', 'MDCCLV', 'MDCCLVI', 'MDCCLVII', 'MDCCLVIII', 'MDCCLIX', 'MDCCLX', 'MDCCLXI', 'MDCCLXII', 'MDCCLXIII', 'MDCCLXIV', 'MDCCLXV', 'MDCCLXVI', 'MDCCLXVII', 'MDCCLXVIII', 'MDCCLXIX', 'MDCCLXX', 'MDCCLXXI', 'MDCCLXXII', 'MDCCLXXIII', 'MDCCLXXIV', 'MDCCLXXV', 'MDCCLXXVI', 'MDCCLXXVII', 'MDCCLXXVIII', 'MDCCLXXIX', 'MDCCLXXX', 'MDCCLXXXI', 'MDCCLXXXII', 'MDCCLXXXIII', 'MDCCLXXXIV', 'MDCCLXXXV', 'MDCCLXXXVI', 'MDCCLXXXVII', 'MDCCLXXXVIII', 'MDCCLXXXIX', 'MDCCXC', 'MDCCXCI', 'MDCCXCII', 'MDCCXCIII', 'MDCCXCIV', 'MDCCVC', 'MDCCVCI', 'MDCCVCII', 'MDCCVCIII', 'MDCCVCIV', 'MDCCC', 'MDCCCI', 'MDCCCII', 'MDCCCIII', 'MDCCCIV', 'MDCCCV', 'MDCCCVI', 'MDCCCVII', 'MDCCCVIII', 'MDCCCIX', 'MDCCCX', 'MDCCCXI', 'MDCCCXII', 'MDCCCXIII', 'MDCCCXIV', 'MDCCCXV', 'MDCCCXVI', 'MDCCCXVII', 'MDCCCXVIII', 'MDCCCXIX', 'MDCCCXX', 'MDCCCXXI', 'MDCCCXXII', 'MDCCCXXIII', 'MDCCCXXIV', 'MDCCCXXV', 'MDCCCXXVI', 'MDCCCXXVII', 'MDCCCXXVIII', 'MDCCCXXIX', 'MDCCCXXX', 'MDCCCXXXI', 'MDCCCXXXII', 'MDCCCXXXIII', 'MDCCCXXXIV', 'MDCCCXXXV', 'MDCCCXXXVI', 'MDCCCXXXVII', 'MDCCCXXXVIII', 'MDCCCXXXIX', 'MDCCCXL', 'MDCCCXLI', 'MDCCCXLII', 'MDCCCXLIII', 'MDCCCXLIV', 'MDCCCVL', 'MDCCCVLI', 'MDCCCVLII', 'MDCCCVLIII', 'MDCCCVLIV', 'MDCCCL', 'MDCCCLI', 'MDCCCLII', 'MDCCCLIII', 'MDCCCLIV', 'MDCCCLV', 'MDCCCLVI', 'MDCCCLVII', 'MDCCCLVIII', 'MDCCCLIX', 'MDCCCLX', 'MDCCCLXI', 'MDCCCLXII', 'MDCCCLXIII', 'MDCCCLXIV', 'MDCCCLXV', 'MDCCCLXVI', 'MDCCCLXVII', 'MDCCCLXVIII', 'MDCCCLXIX', 'MDCCCLXX', 'MDCCCLXXI', 'MDCCCLXXII', 'MDCCCLXXIII', 'MDCCCLXXIV', 'MDCCCLXXV', 'MDCCCLXXVI', 'MDCCCLXXVII', 'MDCCCLXXVIII', 'MDCCCLXXIX', 'MDCCCLXXX', 'MDCCCLXXXI', 'MDCCCLXXXII', 'MDCCCLXXXIII', 'MDCCCLXXXIV', 'MDCCCLXXXV', 'MDCCCLXXXVI', 'MDCCCLXXXVII', 'MDCCCLXXXVIII', 'MDCCCLXXXIX', 'MDCCCXC', 'MDCCCXCI', 'MDCCCXCII', 'MDCCCXCIII', 'MDCCCXCIV', 'MDCCCVC', 'MDCCCVCI', 'MDCCCVCII', 'MDCCCVCIII', 'MDCCCVCIV', 'MCM', 'MCMI', 'MCMII', 'MCMIII', 'MCMIV', 'MCMV', 'MCMVI', 'MCMVII', 'MCMVIII', 'MCMIX', 'MCMX', 'MCMXI', 'MCMXII', 'MCMXIII', 'MCMXIV', 'MCMXV', 'MCMXVI', 'MCMXVII', 'MCMXVIII', 'MCMXIX', 'MCMXX', 'MCMXXI', 'MCMXXII', 'MCMXXIII', 'MCMXXIV', 'MCMXXV', 'MCMXXVI', 'MCMXXVII', 'MCMXXVIII', 'MCMXXIX', 'MCMXXX', 'MCMXXXI', 'MCMXXXII', 'MCMXXXIII', 'MCMXXXIV', 'MCMXXXV', 'MCMXXXVI', 'MCMXXXVII', 'MCMXXXVIII', 'MCMXXXIX', 'MCMXL', 'MCMXLI', 'MCMXLII', 'MCMXLIII', 'MCMXLIV', 'MCMVL', 'MCMVLI', 'MCMVLII', 'MCMVLIII', 'MCMVLIV', 'MLM', 'MLMI', 'MLMII', 'MLMIII', 'MLMIV', 'MLMV', 'MLMVI', 'MLMVII', 'MLMVIII', 'MLMIX', 'MLMX', 'MLMXI', 'MLMXII', 'MLMXIII', 'MLMXIV', 'MLMXV', 'MLMXVI', 'MLMXVII', 'MLMXVIII', 'MLMXIX', 'MLMXX', 'MLMXXI', 'MLMXXII', 'MLMXXIII', 'MLMXXIV', 'MLMXXV', 'MLMXXVI', 'MLMXXVII', 'MLMXXVIII', 'MLMXXIX', 'MLMXXX', 'MLMXXXI', 'MLMXXXII', 'MLMXXXIII', 'MLMXXXIV', 'MLMXXXV', 'MLMXXXVI', 'MLMXXXVII', 'MLMXXXVIII', 'MLMXXXIX', 'MLMXL', 'MLMXLI', 'MLMXLII', 'MLMXLIII', 'MLMXLIV', 'MLMVL', 'MLMVLI', 'MLMVLII', 'MLMVLIII', 'MLMVLIV', 'MM', 'MMI', 'MMII', 'MMIII', 'MMIV', 'MMV', 'MMVI', 'MMVII', 'MMVIII', 'MMIX', 'MMX', 'MMXI', 'MMXII', 'MMXIII', 'MMXIV', 'MMXV', 'MMXVI', 'MMXVII', 'MMXVIII', 'MMXIX', 'MMXX', 'MMXXI', 'MMXXII', 'MMXXIII', 'MMXXIV', 'MMXXV', 'MMXXVI', 'MMXXVII', 'MMXXVIII', 'MMXXIX', 'MMXXX', 'MMXXXI', 'MMXXXII', 'MMXXXIII', 'MMXXXIV', 'MMXXXV', 'MMXXXVI', 'MMXXXVII', 'MMXXXVIII', 'MMXXXIX', 'MMXL', 'MMXLI', 'MMXLII', 'MMXLIII', 'MMXLIV', 'MMVL', 'MMVLI', 'MMVLII', 'MMVLIII', 'MMVLIV', 'MML', 'MMLI', 'MMLII', 'MMLIII', 'MMLIV', 'MMLV', 'MMLVI', 'MMLVII', 'MMLVIII', 'MMLIX', 'MMLX', 'MMLXI', 'MMLXII', 'MMLXIII', 'MMLXIV', 'MMLXV', 'MMLXVI', 'MMLXVII', 'MMLXVIII', 'MMLXIX', 'MMLXX', 'MMLXXI', 'MMLXXII', 'MMLXXIII', 'MMLXXIV', 'MMLXXV', 'MMLXXVI', 'MMLXXVII', 'MMLXXVIII', 'MMLXXIX', 'MMLXXX', 'MMLXXXI', 'MMLXXXII', 'MMLXXXIII', 'MMLXXXIV', 'MMLXXXV', 'MMLXXXVI', 'MMLXXXVII', 'MMLXXXVIII', 'MMLXXXIX', 'MMXC', 'MMXCI', 'MMXCII', 'MMXCIII', 'MMXCIV', 'MMVC', 'MMVCI', 'MMVCII', 'MMVCIII', 'MMVCIV', 'MMC', 'MMCI', 'MMCII', 'MMCIII', 'MMCIV', 'MMCV', 'MMCVI', 'MMCVII', 'MMCVIII', 'MMCIX', 'MMCX', 'MMCXI', 'MMCXII', 'MMCXIII', 'MMCXIV', 'MMCXV', 'MMCXVI', 'MMCXVII', 'MMCXVIII', 'MMCXIX', 'MMCXX', 'MMCXXI', 'MMCXXII', 'MMCXXIII', 'MMCXXIV', 'MMCXXV', 'MMCXXVI', 'MMCXXVII', 'MMCXXVIII', 'MMCXXIX', 'MMCXXX', 'MMCXXXI', 'MMCXXXII', 'MMCXXXIII', 'MMCXXXIV', 'MMCXXXV', 'MMCXXXVI', 'MMCXXXVII', 'MMCXXXVIII', 'MMCXXXIX', 'MMCXL', 'MMCXLI', 'MMCXLII', 'MMCXLIII', 'MMCXLIV', 'MMCVL', 'MMCVLI', 'MMCVLII', 'MMCVLIII', 'MMCVLIV', 'MMCL', 'MMCLI', 'MMCLII', 'MMCLIII', 'MMCLIV', 'MMCLV', 'MMCLVI', 'MMCLVII', 'MMCLVIII', 'MMCLIX', 'MMCLX', 'MMCLXI', 'MMCLXII', 'MMCLXIII', 'MMCLXIV', 'MMCLXV', 'MMCLXVI', 'MMCLXVII', 'MMCLXVIII', 'MMCLXIX', 'MMCLXX', 'MMCLXXI', 'MMCLXXII', 'MMCLXXIII', 'MMCLXXIV', 'MMCLXXV', 'MMCLXXVI', 'MMCLXXVII', 'MMCLXXVIII', 'MMCLXXIX', 'MMCLXXX', 'MMCLXXXI', 'MMCLXXXII', 'MMCLXXXIII', 'MMCLXXXIV', 'MMCLXXXV', 'MMCLXXXVI', 'MMCLXXXVII', 'MMCLXXXVIII', 'MMCLXXXIX', 'MMCXC', 'MMCXCI', 'MMCXCII', 'MMCXCIII', 'MMCXCIV', 'MMCVC', 'MMCVCI', 'MMCVCII', 'MMCVCIII', 'MMCVCIV', 'MMCC', 'MMCCI', 'MMCCII', 'MMCCIII', 'MMCCIV', 'MMCCV', 'MMCCVI', 'MMCCVII', 'MMCCVIII', 'MMCCIX', 'MMCCX', 'MMCCXI', 'MMCCXII', 'MMCCXIII', 'MMCCXIV', 'MMCCXV', 'MMCCXVI', 'MMCCXVII', 'MMCCXVIII', 'MMCCXIX', 'MMCCXX', 'MMCCXXI', 'MMCCXXII', 'MMCCXXIII', 'MMCCXXIV', 'MMCCXXV', 'MMCCXXVI', 'MMCCXXVII', 'MMCCXXVIII', 'MMCCXXIX', 'MMCCXXX', 'MMCCXXXI', 'MMCCXXXII', 'MMCCXXXIII', 'MMCCXXXIV', 'MMCCXXXV', 'MMCCXXXVI', 'MMCCXXXVII', 'MMCCXXXVIII', 'MMCCXXXIX', 'MMCCXL', 'MMCCXLI', 'MMCCXLII', 'MMCCXLIII', 'MMCCXLIV', 'MMCCVL', 'MMCCVLI', 'MMCCVLII', 'MMCCVLIII', 'MMCCVLIV', 'MMCCL', 'MMCCLI', 'MMCCLII', 'MMCCLIII', 'MMCCLIV', 'MMCCLV', 'MMCCLVI', 'MMCCLVII', 'MMCCLVIII', 'MMCCLIX', 'MMCCLX', 'MMCCLXI', 'MMCCLXII', 'MMCCLXIII', 'MMCCLXIV', 'MMCCLXV', 'MMCCLXVI', 'MMCCLXVII', 'MMCCLXVIII', 'MMCCLXIX', 'MMCCLXX', 'MMCCLXXI', 'MMCCLXXII', 'MMCCLXXIII', 'MMCCLXXIV', 'MMCCLXXV', 'MMCCLXXVI', 'MMCCLXXVII', 'MMCCLXXVIII', 'MMCCLXXIX', 'MMCCLXXX', 'MMCCLXXXI', 'MMCCLXXXII', 'MMCCLXXXIII', 'MMCCLXXXIV', 'MMCCLXXXV', 'MMCCLXXXVI', 'MMCCLXXXVII', 'MMCCLXXXVIII', 'MMCCLXXXIX', 'MMCCXC', 'MMCCXCI', 'MMCCXCII', 'MMCCXCIII', 'MMCCXCIV', 'MMCCVC', 'MMCCVCI', 'MMCCVCII', 'MMCCVCIII', 'MMCCVCIV', 'MMCCC', 'MMCCCI', 'MMCCCII', 'MMCCCIII', 'MMCCCIV', 'MMCCCV', 'MMCCCVI', 'MMCCCVII', 'MMCCCVIII', 'MMCCCIX', 'MMCCCX', 'MMCCCXI', 'MMCCCXII', 'MMCCCXIII', 'MMCCCXIV', 'MMCCCXV', 'MMCCCXVI', 'MMCCCXVII', 'MMCCCXVIII', 'MMCCCXIX', 'MMCCCXX', 'MMCCCXXI', 'MMCCCXXII', 'MMCCCXXIII', 'MMCCCXXIV', 'MMCCCXXV', 'MMCCCXXVI', 'MMCCCXXVII', 'MMCCCXXVIII', 'MMCCCXXIX', 'MMCCCXXX', 'MMCCCXXXI', 'MMCCCXXXII', 'MMCCCXXXIII', 'MMCCCXXXIV', 'MMCCCXXXV', 'MMCCCXXXVI', 'MMCCCXXXVII', 'MMCCCXXXVIII', 'MMCCCXXXIX', 'MMCCCXL', 'MMCCCXLI', 'MMCCCXLII', 'MMCCCXLIII', 'MMCCCXLIV', 'MMCCCVL', 'MMCCCVLI', 'MMCCCVLII', 'MMCCCVLIII', 'MMCCCVLIV', 'MMCCCL', 'MMCCCLI', 'MMCCCLII', 'MMCCCLIII', 'MMCCCLIV', 'MMCCCLV', 'MMCCCLVI', 'MMCCCLVII', 'MMCCCLVIII', 'MMCCCLIX', 'MMCCCLX', 'MMCCCLXI', 'MMCCCLXII', 'MMCCCLXIII', 'MMCCCLXIV', 'MMCCCLXV', 'MMCCCLXVI', 'MMCCCLXVII', 'MMCCCLXVIII', 'MMCCCLXIX', 'MMCCCLXX', 'MMCCCLXXI', 'MMCCCLXXII', 'MMCCCLXXIII', 'MMCCCLXXIV', 'MMCCCLXXV', 'MMCCCLXXVI', 'MMCCCLXXVII', 'MMCCCLXXVIII', 'MMCCCLXXIX', 'MMCCCLXXX', 'MMCCCLXXXI', 'MMCCCLXXXII', 'MMCCCLXXXIII', 'MMCCCLXXXIV', 'MMCCCLXXXV', 'MMCCCLXXXVI', 'MMCCCLXXXVII', 'MMCCCLXXXVIII', 'MMCCCLXXXIX', 'MMCCCXC', 'MMCCCXCI', 'MMCCCXCII', 'MMCCCXCIII', 'MMCCCXCIV', 'MMCCCVC', 'MMCCCVCI', 'MMCCCVCII', 'MMCCCVCIII', 'MMCCCVCIV', 'MMCD', 'MMCDI', 'MMCDII', 'MMCDIII', 'MMCDIV', 'MMCDV', 'MMCDVI', 'MMCDVII', 'MMCDVIII', 'MMCDIX', 'MMCDX', 'MMCDXI', 'MMCDXII', 'MMCDXIII', 'MMCDXIV', 'MMCDXV', 'MMCDXVI', 'MMCDXVII', 'MMCDXVIII', 'MMCDXIX', 'MMCDXX', 'MMCDXXI', 'MMCDXXII', 'MMCDXXIII', 'MMCDXXIV', 'MMCDXXV', 'MMCDXXVI', 'MMCDXXVII', 'MMCDXXVIII', 'MMCDXXIX', 'MMCDXXX', 'MMCDXXXI', 'MMCDXXXII', 'MMCDXXXIII', 'MMCDXXXIV', 'MMCDXXXV', 'MMCDXXXVI', 'MMCDXXXVII', 'MMCDXXXVIII', 'MMCDXXXIX', 'MMCDXL', 'MMCDXLI', 'MMCDXLII', 'MMCDXLIII', 'MMCDXLIV', 'MMCDVL', 'MMCDVLI', 'MMCDVLII', 'MMCDVLIII', 'MMCDVLIV', 'MMLD', 'MMLDI', 'MMLDII', 'MMLDIII', 'MMLDIV', 'MMLDV', 'MMLDVI', 'MMLDVII', 'MMLDVIII', 'MMLDIX', 'MMLDX', 'MMLDXI', 'MMLDXII', 'MMLDXIII', 'MMLDXIV', 'MMLDXV', 'MMLDXVI', 'MMLDXVII', 'MMLDXVIII', 'MMLDXIX', 'MMLDXX', 'MMLDXXI', 'MMLDXXII', 'MMLDXXIII', 'MMLDXXIV', 'MMLDXXV', 'MMLDXXVI', 'MMLDXXVII', 'MMLDXXVIII', 'MMLDXXIX', 'MMLDXXX', 'MMLDXXXI', 'MMLDXXXII', 'MMLDXXXIII', 'MMLDXXXIV', 'MMLDXXXV', 'MMLDXXXVI', 'MMLDXXXVII', 'MMLDXXXVIII', 'MMLDXXXIX', 'MMLDXL', 'MMLDXLI', 'MMLDXLII', 'MMLDXLIII', 'MMLDXLIV', 'MMLDVL', 'MMLDVLI', 'MMLDVLII', 'MMLDVLIII', 'MMLDVLIV', 'MMD', 'MMDI', 'MMDII', 'MMDIII', 'MMDIV', 'MMDV', 'MMDVI', 'MMDVII', 'MMDVIII', 'MMDIX', 'MMDX', 'MMDXI', 'MMDXII', 'MMDXIII', 'MMDXIV', 'MMDXV', 'MMDXVI', 'MMDXVII', 'MMDXVIII', 'MMDXIX', 'MMDXX', 'MMDXXI', 'MMDXXII', 'MMDXXIII', 'MMDXXIV', 'MMDXXV', 'MMDXXVI', 'MMDXXVII', 'MMDXXVIII', 'MMDXXIX', 'MMDXXX', 'MMDXXXI', 'MMDXXXII', 'MMDXXXIII', 'MMDXXXIV', 'MMDXXXV', 'MMDXXXVI', 'MMDXXXVII', 'MMDXXXVIII', 'MMDXXXIX', 'MMDXL', 'MMDXLI', 'MMDXLII', 'MMDXLIII', 'MMDXLIV', 'MMDVL', 'MMDVLI', 'MMDVLII', 'MMDVLIII', 'MMDVLIV', 'MMDL', 'MMDLI', 'MMDLII', 'MMDLIII', 'MMDLIV', 'MMDLV', 'MMDLVI', 'MMDLVII', 'MMDLVIII', 'MMDLIX', 'MMDLX', 'MMDLXI', 'MMDLXII', 'MMDLXIII', 'MMDLXIV', 'MMDLXV', 'MMDLXVI', 'MMDLXVII', 'MMDLXVIII', 'MMDLXIX', 'MMDLXX', 'MMDLXXI', 'MMDLXXII', 'MMDLXXIII', 'MMDLXXIV', 'MMDLXXV', 'MMDLXXVI', 'MMDLXXVII', 'MMDLXXVIII', 'MMDLXXIX', 'MMDLXXX', 'MMDLXXXI', 'MMDLXXXII', 'MMDLXXXIII', 'MMDLXXXIV', 'MMDLXXXV', 'MMDLXXXVI', 'MMDLXXXVII', 'MMDLXXXVIII', 'MMDLXXXIX', 'MMDXC', 'MMDXCI', 'MMDXCII', 'MMDXCIII', 'MMDXCIV', 'MMDVC', 'MMDVCI', 'MMDVCII', 'MMDVCIII', 'MMDVCIV', 'MMDC', 'MMDCI', 'MMDCII', 'MMDCIII', 'MMDCIV', 'MMDCV', 'MMDCVI', 'MMDCVII', 'MMDCVIII', 'MMDCIX', 'MMDCX', 'MMDCXI', 'MMDCXII', 'MMDCXIII', 'MMDCXIV', 'MMDCXV', 'MMDCXVI', 'MMDCXVII', 'MMDCXVIII', 'MMDCXIX', 'MMDCXX', 'MMDCXXI', 'MMDCXXII', 'MMDCXXIII', 'MMDCXXIV', 'MMDCXXV', 'MMDCXXVI', 'MMDCXXVII', 'MMDCXXVIII', 'MMDCXXIX', 'MMDCXXX', 'MMDCXXXI', 'MMDCXXXII', 'MMDCXXXIII', 'MMDCXXXIV', 'MMDCXXXV', 'MMDCXXXVI', 'MMDCXXXVII', 'MMDCXXXVIII', 'MMDCXXXIX', 'MMDCXL', 'MMDCXLI', 'MMDCXLII', 'MMDCXLIII', 'MMDCXLIV', 'MMDCVL', 'MMDCVLI', 'MMDCVLII', 'MMDCVLIII', 'MMDCVLIV', 'MMDCL', 'MMDCLI', 'MMDCLII', 'MMDCLIII', 'MMDCLIV', 'MMDCLV', 'MMDCLVI', 'MMDCLVII', 'MMDCLVIII', 'MMDCLIX', 'MMDCLX', 'MMDCLXI', 'MMDCLXII', 'MMDCLXIII', 'MMDCLXIV', 'MMDCLXV', 'MMDCLXVI', 'MMDCLXVII', 'MMDCLXVIII', 'MMDCLXIX', 'MMDCLXX', 'MMDCLXXI', 'MMDCLXXII', 'MMDCLXXIII', 'MMDCLXXIV', 'MMDCLXXV', 'MMDCLXXVI', 'MMDCLXXVII', 'MMDCLXXVIII', 'MMDCLXXIX', 'MMDCLXXX', 'MMDCLXXXI', 'MMDCLXXXII', 'MMDCLXXXIII', 'MMDCLXXXIV', 'MMDCLXXXV', 'MMDCLXXXVI', 'MMDCLXXXVII', 'MMDCLXXXVIII', 'MMDCLXXXIX', 'MMDCXC', 'MMDCXCI', 'MMDCXCII', 'MMDCXCIII', 'MMDCXCIV', 'MMDCVC', 'MMDCVCI', 'MMDCVCII', 'MMDCVCIII', 'MMDCVCIV', 'MMDCC', 'MMDCCI', 'MMDCCII', 'MMDCCIII', 'MMDCCIV', 'MMDCCV', 'MMDCCVI', 'MMDCCVII', 'MMDCCVIII', 'MMDCCIX', 'MMDCCX', 'MMDCCXI', 'MMDCCXII', 'MMDCCXIII', 'MMDCCXIV', 'MMDCCXV', 'MMDCCXVI', 'MMDCCXVII', 'MMDCCXVIII', 'MMDCCXIX', 'MMDCCXX', 'MMDCCXXI', 'MMDCCXXII', 'MMDCCXXIII', 'MMDCCXXIV', 'MMDCCXXV', 'MMDCCXXVI', 'MMDCCXXVII', 'MMDCCXXVIII', 'MMDCCXXIX', 'MMDCCXXX', 'MMDCCXXXI', 'MMDCCXXXII', 'MMDCCXXXIII', 'MMDCCXXXIV', 'MMDCCXXXV', 'MMDCCXXXVI', 'MMDCCXXXVII', 'MMDCCXXXVIII', 'MMDCCXXXIX', 'MMDCCXL', 'MMDCCXLI', 'MMDCCXLII', 'MMDCCXLIII', 'MMDCCXLIV', 'MMDCCVL', 'MMDCCVLI', 'MMDCCVLII', 'MMDCCVLIII', 'MMDCCVLIV', 'MMDCCL', 'MMDCCLI', 'MMDCCLII', 'MMDCCLIII', 'MMDCCLIV', 'MMDCCLV', 'MMDCCLVI', 'MMDCCLVII', 'MMDCCLVIII', 'MMDCCLIX', 'MMDCCLX', 'MMDCCLXI', 'MMDCCLXII', 'MMDCCLXIII', 'MMDCCLXIV', 'MMDCCLXV', 'MMDCCLXVI', 'MMDCCLXVII', 'MMDCCLXVIII', 'MMDCCLXIX', 'MMDCCLXX', 'MMDCCLXXI', 'MMDCCLXXII', 'MMDCCLXXIII', 'MMDCCLXXIV', 'MMDCCLXXV', 'MMDCCLXXVI', 'MMDCCLXXVII', 'MMDCCLXXVIII', 'MMDCCLXXIX', 'MMDCCLXXX', 'MMDCCLXXXI', 'MMDCCLXXXII', 'MMDCCLXXXIII', 'MMDCCLXXXIV', 'MMDCCLXXXV', 'MMDCCLXXXVI', 'MMDCCLXXXVII', 'MMDCCLXXXVIII', 'MMDCCLXXXIX', 'MMDCCXC', 'MMDCCXCI', 'MMDCCXCII', 'MMDCCXCIII', 'MMDCCXCIV', 'MMDCCVC', 'MMDCCVCI', 'MMDCCVCII', 'MMDCCVCIII', 'MMDCCVCIV', 'MMDCCC', 'MMDCCCI', 'MMDCCCII', 'MMDCCCIII', 'MMDCCCIV', 'MMDCCCV', 'MMDCCCVI', 'MMDCCCVII', 'MMDCCCVIII', 'MMDCCCIX', 'MMDCCCX', 'MMDCCCXI', 'MMDCCCXII', 'MMDCCCXIII', 'MMDCCCXIV', 'MMDCCCXV', 'MMDCCCXVI', 'MMDCCCXVII', 'MMDCCCXVIII', 'MMDCCCXIX', 'MMDCCCXX', 'MMDCCCXXI', 'MMDCCCXXII', 'MMDCCCXXIII', 'MMDCCCXXIV', 'MMDCCCXXV', 'MMDCCCXXVI', 'MMDCCCXXVII', 'MMDCCCXXVIII', 'MMDCCCXXIX', 'MMDCCCXXX', 'MMDCCCXXXI', 'MMDCCCXXXII', 'MMDCCCXXXIII', 'MMDCCCXXXIV', 'MMDCCCXXXV', 'MMDCCCXXXVI', 'MMDCCCXXXVII', 'MMDCCCXXXVIII', 'MMDCCCXXXIX', 'MMDCCCXL', 'MMDCCCXLI', 'MMDCCCXLII', 'MMDCCCXLIII', 'MMDCCCXLIV', 'MMDCCCVL', 'MMDCCCVLI', 'MMDCCCVLII', 'MMDCCCVLIII', 'MMDCCCVLIV', 'MMDCCCL', 'MMDCCCLI', 'MMDCCCLII', 'MMDCCCLIII', 'MMDCCCLIV', 'MMDCCCLV', 'MMDCCCLVI', 'MMDCCCLVII', 'MMDCCCLVIII', 'MMDCCCLIX', 'MMDCCCLX', 'MMDCCCLXI', 'MMDCCCLXII', 'MMDCCCLXIII', 'MMDCCCLXIV', 'MMDCCCLXV', 'MMDCCCLXVI', 'MMDCCCLXVII', 'MMDCCCLXVIII', 'MMDCCCLXIX', 'MMDCCCLXX', 'MMDCCCLXXI', 'MMDCCCLXXII', 'MMDCCCLXXIII', 'MMDCCCLXXIV', 'MMDCCCLXXV', 'MMDCCCLXXVI', 'MMDCCCLXXVII', 'MMDCCCLXXVIII', 'MMDCCCLXXIX', 'MMDCCCLXXX', 'MMDCCCLXXXI', 'MMDCCCLXXXII', 'MMDCCCLXXXIII', 'MMDCCCLXXXIV', 'MMDCCCLXXXV', 'MMDCCCLXXXVI', 'MMDCCCLXXXVII', 'MMDCCCLXXXVIII', 'MMDCCCLXXXIX', 'MMDCCCXC', 'MMDCCCXCI', 'MMDCCCXCII', 'MMDCCCXCIII', 'MMDCCCXCIV', 'MMDCCCVC', 'MMDCCCVCI', 'MMDCCCVCII', 'MMDCCCVCIII', 'MMDCCCVCIV', 'MMCM', 'MMCMI', 'MMCMII', 'MMCMIII', 'MMCMIV', 'MMCMV', 'MMCMVI', 'MMCMVII', 'MMCMVIII', 'MMCMIX', 'MMCMX', 'MMCMXI', 'MMCMXII', 'MMCMXIII', 'MMCMXIV', 'MMCMXV', 'MMCMXVI', 'MMCMXVII', 'MMCMXVIII', 'MMCMXIX', 'MMCMXX', 'MMCMXXI', 'MMCMXXII', 'MMCMXXIII', 'MMCMXXIV', 'MMCMXXV', 'MMCMXXVI', 'MMCMXXVII', 'MMCMXXVIII', 'MMCMXXIX', 'MMCMXXX', 'MMCMXXXI', 'MMCMXXXII', 'MMCMXXXIII', 'MMCMXXXIV', 'MMCMXXXV', 'MMCMXXXVI', 'MMCMXXXVII', 'MMCMXXXVIII', 'MMCMXXXIX', 'MMCMXL', 'MMCMXLI', 'MMCMXLII', 'MMCMXLIII', 'MMCMXLIV', 'MMCMVL', 'MMCMVLI', 'MMCMVLII', 'MMCMVLIII', 'MMCMVLIV', 'MMLM', 'MMLMI', 'MMLMII', 'MMLMIII', 'MMLMIV', 'MMLMV', 'MMLMVI', 'MMLMVII', 'MMLMVIII', 'MMLMIX', 'MMLMX', 'MMLMXI', 'MMLMXII', 'MMLMXIII', 'MMLMXIV', 'MMLMXV', 'MMLMXVI', 'MMLMXVII', 'MMLMXVIII', 'MMLMXIX', 'MMLMXX', 'MMLMXXI', 'MMLMXXII', 'MMLMXXIII', 'MMLMXXIV', 'MMLMXXV', 'MMLMXXVI', 'MMLMXXVII', 'MMLMXXVIII', 'MMLMXXIX', 'MMLMXXX', 'MMLMXXXI', 'MMLMXXXII', 'MMLMXXXIII', 'MMLMXXXIV', 'MMLMXXXV', 'MMLMXXXVI', 'MMLMXXXVII', 'MMLMXXXVIII', 'MMLMXXXIX', 'MMLMXL', 'MMLMXLI', 'MMLMXLII', 'MMLMXLIII', 'MMLMXLIV', 'MMLMVL', 'MMLMVLI', 'MMLMVLII', 'MMLMVLIII', 'MMLMVLIV', 'MMM', 'MMMI', 'MMMII', 'MMMIII', 'MMMIV', 'MMMV', 'MMMVI', 'MMMVII', 'MMMVIII', 'MMMIX', 'MMMX', 'MMMXI', 'MMMXII', 'MMMXIII', 'MMMXIV', 'MMMXV', 'MMMXVI', 'MMMXVII', 'MMMXVIII', 'MMMXIX', 'MMMXX', 'MMMXXI', 'MMMXXII', 'MMMXXIII', 'MMMXXIV', 'MMMXXV', 'MMMXXVI', 'MMMXXVII', 'MMMXXVIII', 'MMMXXIX', 'MMMXXX', 'MMMXXXI', 'MMMXXXII', 'MMMXXXIII', 'MMMXXXIV', 'MMMXXXV', 'MMMXXXVI', 'MMMXXXVII', 'MMMXXXVIII', 'MMMXXXIX', 'MMMXL', 'MMMXLI', 'MMMXLII', 'MMMXLIII', 'MMMXLIV', 'MMMVL', 'MMMVLI', 'MMMVLII', 'MMMVLIII', 'MMMVLIV', 'MMML', 'MMMLI', 'MMMLII', 'MMMLIII', 'MMMLIV', 'MMMLV', 'MMMLVI', 'MMMLVII', 'MMMLVIII', 'MMMLIX', 'MMMLX', 'MMMLXI', 'MMMLXII', 'MMMLXIII', 'MMMLXIV', 'MMMLXV', 'MMMLXVI', 'MMMLXVII', 'MMMLXVIII', 'MMMLXIX', 'MMMLXX', 'MMMLXXI', 'MMMLXXII', 'MMMLXXIII', 'MMMLXXIV', 'MMMLXXV', 'MMMLXXVI', 'MMMLXXVII', 'MMMLXXVIII', 'MMMLXXIX', 'MMMLXXX', 'MMMLXXXI', 'MMMLXXXII', 'MMMLXXXIII', 'MMMLXXXIV', 'MMMLXXXV', 'MMMLXXXVI', 'MMMLXXXVII', 'MMMLXXXVIII', 'MMMLXXXIX', 'MMMXC', 'MMMXCI', 'MMMXCII', 'MMMXCIII', 'MMMXCIV', 'MMMVC', 'MMMVCI', 'MMMVCII', 'MMMVCIII', 'MMMVCIV', 'MMMC', 'MMMCI', 'MMMCII', 'MMMCIII', 'MMMCIV', 'MMMCV', 'MMMCVI', 'MMMCVII', 'MMMCVIII', 'MMMCIX', 'MMMCX', 'MMMCXI', 'MMMCXII', 'MMMCXIII', 'MMMCXIV', 'MMMCXV', 'MMMCXVI', 'MMMCXVII', 'MMMCXVIII', 'MMMCXIX', 'MMMCXX', 'MMMCXXI', 'MMMCXXII', 'MMMCXXIII', 'MMMCXXIV', 'MMMCXXV', 'MMMCXXVI', 'MMMCXXVII', 'MMMCXXVIII', 'MMMCXXIX', 'MMMCXXX', 'MMMCXXXI', 'MMMCXXXII', 'MMMCXXXIII', 'MMMCXXXIV', 'MMMCXXXV', 'MMMCXXXVI', 'MMMCXXXVII', 'MMMCXXXVIII', 'MMMCXXXIX', 'MMMCXL', 'MMMCXLI', 'MMMCXLII', 'MMMCXLIII', 'MMMCXLIV', 'MMMCVL', 'MMMCVLI', 'MMMCVLII', 'MMMCVLIII', 'MMMCVLIV', 'MMMCL', 'MMMCLI', 'MMMCLII', 'MMMCLIII', 'MMMCLIV', 'MMMCLV', 'MMMCLVI', 'MMMCLVII', 'MMMCLVIII', 'MMMCLIX', 'MMMCLX', 'MMMCLXI', 'MMMCLXII', 'MMMCLXIII', 'MMMCLXIV', 'MMMCLXV', 'MMMCLXVI', 'MMMCLXVII', 'MMMCLXVIII', 'MMMCLXIX', 'MMMCLXX', 'MMMCLXXI', 'MMMCLXXII', 'MMMCLXXIII', 'MMMCLXXIV', 'MMMCLXXV', 'MMMCLXXVI', 'MMMCLXXVII', 'MMMCLXXVIII', 'MMMCLXXIX', 'MMMCLXXX', 'MMMCLXXXI', 'MMMCLXXXII', 'MMMCLXXXIII', 'MMMCLXXXIV', 'MMMCLXXXV', 'MMMCLXXXVI', 'MMMCLXXXVII', 'MMMCLXXXVIII', 'MMMCLXXXIX', 'MMMCXC', 'MMMCXCI', 'MMMCXCII', 'MMMCXCIII', 'MMMCXCIV', 'MMMCVC', 'MMMCVCI', 'MMMCVCII', 'MMMCVCIII', 'MMMCVCIV', 'MMMCC', 'MMMCCI', 'MMMCCII', 'MMMCCIII', 'MMMCCIV', 'MMMCCV', 'MMMCCVI', 'MMMCCVII', 'MMMCCVIII', 'MMMCCIX', 'MMMCCX', 'MMMCCXI', 'MMMCCXII', 'MMMCCXIII', 'MMMCCXIV', 'MMMCCXV', 'MMMCCXVI', 'MMMCCXVII', 'MMMCCXVIII', 'MMMCCXIX', 'MMMCCXX', 'MMMCCXXI', 'MMMCCXXII', 'MMMCCXXIII', 'MMMCCXXIV', 'MMMCCXXV', 'MMMCCXXVI', 'MMMCCXXVII', 'MMMCCXXVIII', 'MMMCCXXIX', 'MMMCCXXX', 'MMMCCXXXI', 'MMMCCXXXII', 'MMMCCXXXIII', 'MMMCCXXXIV', 'MMMCCXXXV', 'MMMCCXXXVI', 'MMMCCXXXVII', 'MMMCCXXXVIII', 'MMMCCXXXIX', 'MMMCCXL', 'MMMCCXLI', 'MMMCCXLII', 'MMMCCXLIII', 'MMMCCXLIV', 'MMMCCVL', 'MMMCCVLI', 'MMMCCVLII', 'MMMCCVLIII', 'MMMCCVLIV', 'MMMCCL', 'MMMCCLI', 'MMMCCLII', 'MMMCCLIII', 'MMMCCLIV', 'MMMCCLV', 'MMMCCLVI', 'MMMCCLVII', 'MMMCCLVIII', 'MMMCCLIX', 'MMMCCLX', 'MMMCCLXI', 'MMMCCLXII', 'MMMCCLXIII', 'MMMCCLXIV', 'MMMCCLXV', 'MMMCCLXVI', 'MMMCCLXVII', 'MMMCCLXVIII', 'MMMCCLXIX', 'MMMCCLXX', 'MMMCCLXXI', 'MMMCCLXXII', 'MMMCCLXXIII', 'MMMCCLXXIV', 'MMMCCLXXV', 'MMMCCLXXVI', 'MMMCCLXXVII', 'MMMCCLXXVIII', 'MMMCCLXXIX', 'MMMCCLXXX', 'MMMCCLXXXI', 'MMMCCLXXXII', 'MMMCCLXXXIII', 'MMMCCLXXXIV', 'MMMCCLXXXV', 'MMMCCLXXXVI', 'MMMCCLXXXVII', 'MMMCCLXXXVIII', 'MMMCCLXXXIX', 'MMMCCXC', 'MMMCCXCI', 'MMMCCXCII', 'MMMCCXCIII', 'MMMCCXCIV', 'MMMCCVC', 'MMMCCVCI', 'MMMCCVCII', 'MMMCCVCIII', 'MMMCCVCIV', 'MMMCCC', 'MMMCCCI', 'MMMCCCII', 'MMMCCCIII', 'MMMCCCIV', 'MMMCCCV', 'MMMCCCVI', 'MMMCCCVII', 'MMMCCCVIII', 'MMMCCCIX', 'MMMCCCX', 'MMMCCCXI', 'MMMCCCXII', 'MMMCCCXIII', 'MMMCCCXIV', 'MMMCCCXV', 'MMMCCCXVI', 'MMMCCCXVII', 'MMMCCCXVIII', 'MMMCCCXIX', 'MMMCCCXX', 'MMMCCCXXI', 'MMMCCCXXII', 'MMMCCCXXIII', 'MMMCCCXXIV', 'MMMCCCXXV', 'MMMCCCXXVI', 'MMMCCCXXVII', 'MMMCCCXXVIII', 'MMMCCCXXIX', 'MMMCCCXXX', 'MMMCCCXXXI', 'MMMCCCXXXII', 'MMMCCCXXXIII', 'MMMCCCXXXIV', 'MMMCCCXXXV', 'MMMCCCXXXVI', 'MMMCCCXXXVII', 'MMMCCCXXXVIII', 'MMMCCCXXXIX', 'MMMCCCXL', 'MMMCCCXLI', 'MMMCCCXLII', 'MMMCCCXLIII', 'MMMCCCXLIV', 'MMMCCCVL', 'MMMCCCVLI', 'MMMCCCVLII', 'MMMCCCVLIII', 'MMMCCCVLIV', 'MMMCCCL', 'MMMCCCLI', 'MMMCCCLII', 'MMMCCCLIII', 'MMMCCCLIV', 'MMMCCCLV', 'MMMCCCLVI', 'MMMCCCLVII', 'MMMCCCLVIII', 'MMMCCCLIX', 'MMMCCCLX', 'MMMCCCLXI', 'MMMCCCLXII', 'MMMCCCLXIII', 'MMMCCCLXIV', 'MMMCCCLXV', 'MMMCCCLXVI', 'MMMCCCLXVII', 'MMMCCCLXVIII', 'MMMCCCLXIX', 'MMMCCCLXX', 'MMMCCCLXXI', 'MMMCCCLXXII', 'MMMCCCLXXIII', 'MMMCCCLXXIV', 'MMMCCCLXXV', 'MMMCCCLXXVI', 'MMMCCCLXXVII', 'MMMCCCLXXVIII', 'MMMCCCLXXIX', 'MMMCCCLXXX', 'MMMCCCLXXXI', 'MMMCCCLXXXII', 'MMMCCCLXXXIII', 'MMMCCCLXXXIV', 'MMMCCCLXXXV', 'MMMCCCLXXXVI', 'MMMCCCLXXXVII', 'MMMCCCLXXXVIII', 'MMMCCCLXXXIX', 'MMMCCCXC', 'MMMCCCXCI', 'MMMCCCXCII', 'MMMCCCXCIII', 'MMMCCCXCIV', 'MMMCCCVC', 'MMMCCCVCI', 'MMMCCCVCII', 'MMMCCCVCIII', 'MMMCCCVCIV', 'MMMCD', 'MMMCDI', 'MMMCDII', 'MMMCDIII', 'MMMCDIV', 'MMMCDV', 'MMMCDVI', 'MMMCDVII', 'MMMCDVIII', 'MMMCDIX', 'MMMCDX', 'MMMCDXI', 'MMMCDXII', 'MMMCDXIII', 'MMMCDXIV', 'MMMCDXV', 'MMMCDXVI', 'MMMCDXVII', 'MMMCDXVIII', 'MMMCDXIX', 'MMMCDXX', 'MMMCDXXI', 'MMMCDXXII', 'MMMCDXXIII', 'MMMCDXXIV', 'MMMCDXXV', 'MMMCDXXVI', 'MMMCDXXVII', 'MMMCDXXVIII', 'MMMCDXXIX', 'MMMCDXXX', 'MMMCDXXXI', 'MMMCDXXXII', 'MMMCDXXXIII', 'MMMCDXXXIV', 'MMMCDXXXV', 'MMMCDXXXVI', 'MMMCDXXXVII', 'MMMCDXXXVIII', 'MMMCDXXXIX', 'MMMCDXL', 'MMMCDXLI', 'MMMCDXLII', 'MMMCDXLIII', 'MMMCDXLIV', 'MMMCDVL', 'MMMCDVLI', 'MMMCDVLII', 'MMMCDVLIII', 'MMMCDVLIV', 'MMMLD', 'MMMLDI', 'MMMLDII', 'MMMLDIII', 'MMMLDIV', 'MMMLDV', 'MMMLDVI', 'MMMLDVII', 'MMMLDVIII', 'MMMLDIX', 'MMMLDX', 'MMMLDXI', 'MMMLDXII', 'MMMLDXIII', 'MMMLDXIV', 'MMMLDXV', 'MMMLDXVI', 'MMMLDXVII', 'MMMLDXVIII', 'MMMLDXIX', 'MMMLDXX', 'MMMLDXXI', 'MMMLDXXII', 'MMMLDXXIII', 'MMMLDXXIV', 'MMMLDXXV', 'MMMLDXXVI', 'MMMLDXXVII', 'MMMLDXXVIII', 'MMMLDXXIX', 'MMMLDXXX', 'MMMLDXXXI', 'MMMLDXXXII', 'MMMLDXXXIII', 'MMMLDXXXIV', 'MMMLDXXXV', 'MMMLDXXXVI', 'MMMLDXXXVII', 'MMMLDXXXVIII', 'MMMLDXXXIX', 'MMMLDXL', 'MMMLDXLI', 'MMMLDXLII', 'MMMLDXLIII', 'MMMLDXLIV', 'MMMLDVL', 'MMMLDVLI', 'MMMLDVLII', 'MMMLDVLIII', 'MMMLDVLIV', 'MMMD', 'MMMDI', 'MMMDII', 'MMMDIII', 'MMMDIV', 'MMMDV', 'MMMDVI', 'MMMDVII', 'MMMDVIII', 'MMMDIX', 'MMMDX', 'MMMDXI', 'MMMDXII', 'MMMDXIII', 'MMMDXIV', 'MMMDXV', 'MMMDXVI', 'MMMDXVII', 'MMMDXVIII', 'MMMDXIX', 'MMMDXX', 'MMMDXXI', 'MMMDXXII', 'MMMDXXIII', 'MMMDXXIV', 'MMMDXXV', 'MMMDXXVI', 'MMMDXXVII', 'MMMDXXVIII', 'MMMDXXIX', 'MMMDXXX', 'MMMDXXXI', 'MMMDXXXII', 'MMMDXXXIII', 'MMMDXXXIV', 'MMMDXXXV', 'MMMDXXXVI', 'MMMDXXXVII', 'MMMDXXXVIII', 'MMMDXXXIX', 'MMMDXL', 'MMMDXLI', 'MMMDXLII', 'MMMDXLIII', 'MMMDXLIV', 'MMMDVL', 'MMMDVLI', 'MMMDVLII', 'MMMDVLIII', 'MMMDVLIV', 'MMMDL', 'MMMDLI', 'MMMDLII', 'MMMDLIII', 'MMMDLIV', 'MMMDLV', 'MMMDLVI', 'MMMDLVII', 'MMMDLVIII', 'MMMDLIX', 'MMMDLX', 'MMMDLXI', 'MMMDLXII', 'MMMDLXIII', 'MMMDLXIV', 'MMMDLXV', 'MMMDLXVI', 'MMMDLXVII', 'MMMDLXVIII', 'MMMDLXIX', 'MMMDLXX', 'MMMDLXXI', 'MMMDLXXII', 'MMMDLXXIII', 'MMMDLXXIV', 'MMMDLXXV', 'MMMDLXXVI', 'MMMDLXXVII', 'MMMDLXXVIII', 'MMMDLXXIX', 'MMMDLXXX', 'MMMDLXXXI', 'MMMDLXXXII', 'MMMDLXXXIII', 'MMMDLXXXIV', 'MMMDLXXXV', 'MMMDLXXXVI', 'MMMDLXXXVII', 'MMMDLXXXVIII', 'MMMDLXXXIX', 'MMMDXC', 'MMMDXCI', 'MMMDXCII', 'MMMDXCIII', 'MMMDXCIV', 'MMMDVC', 'MMMDVCI', 'MMMDVCII', 'MMMDVCIII', 'MMMDVCIV', 'MMMDC', 'MMMDCI', 'MMMDCII', 'MMMDCIII', 'MMMDCIV', 'MMMDCV', 'MMMDCVI', 'MMMDCVII', 'MMMDCVIII', 'MMMDCIX', 'MMMDCX', 'MMMDCXI', 'MMMDCXII', 'MMMDCXIII', 'MMMDCXIV', 'MMMDCXV', 'MMMDCXVI', 'MMMDCXVII', 'MMMDCXVIII', 'MMMDCXIX', 'MMMDCXX', 'MMMDCXXI', 'MMMDCXXII', 'MMMDCXXIII', 'MMMDCXXIV', 'MMMDCXXV', 'MMMDCXXVI', 'MMMDCXXVII', 'MMMDCXXVIII', 'MMMDCXXIX', 'MMMDCXXX', 'MMMDCXXXI', 'MMMDCXXXII', 'MMMDCXXXIII', 'MMMDCXXXIV', 'MMMDCXXXV', 'MMMDCXXXVI', 'MMMDCXXXVII', 'MMMDCXXXVIII', 'MMMDCXXXIX', 'MMMDCXL', 'MMMDCXLI', 'MMMDCXLII', 'MMMDCXLIII', 'MMMDCXLIV', 'MMMDCVL', 'MMMDCVLI', 'MMMDCVLII', 'MMMDCVLIII', 'MMMDCVLIV', 'MMMDCL', 'MMMDCLI', 'MMMDCLII', 'MMMDCLIII', 'MMMDCLIV', 'MMMDCLV', 'MMMDCLVI', 'MMMDCLVII', 'MMMDCLVIII', 'MMMDCLIX', 'MMMDCLX', 'MMMDCLXI', 'MMMDCLXII', 'MMMDCLXIII', 'MMMDCLXIV', 'MMMDCLXV', 'MMMDCLXVI', 'MMMDCLXVII', 'MMMDCLXVIII', 'MMMDCLXIX', 'MMMDCLXX', 'MMMDCLXXI', 'MMMDCLXXII', 'MMMDCLXXIII', 'MMMDCLXXIV', 'MMMDCLXXV', 'MMMDCLXXVI', 'MMMDCLXXVII', 'MMMDCLXXVIII', 'MMMDCLXXIX', 'MMMDCLXXX', 'MMMDCLXXXI', 'MMMDCLXXXII', 'MMMDCLXXXIII', 'MMMDCLXXXIV', 'MMMDCLXXXV', 'MMMDCLXXXVI', 'MMMDCLXXXVII', 'MMMDCLXXXVIII', 'MMMDCLXXXIX', 'MMMDCXC', 'MMMDCXCI', 'MMMDCXCII', 'MMMDCXCIII', 'MMMDCXCIV', 'MMMDCVC', 'MMMDCVCI', 'MMMDCVCII', 'MMMDCVCIII', 'MMMDCVCIV', 'MMMDCC', 'MMMDCCI', 'MMMDCCII', 'MMMDCCIII', 'MMMDCCIV', 'MMMDCCV', 'MMMDCCVI', 'MMMDCCVII', 'MMMDCCVIII', 'MMMDCCIX', 'MMMDCCX', 'MMMDCCXI', 'MMMDCCXII', 'MMMDCCXIII', 'MMMDCCXIV', 'MMMDCCXV', 'MMMDCCXVI', 'MMMDCCXVII', 'MMMDCCXVIII', 'MMMDCCXIX', 'MMMDCCXX', 'MMMDCCXXI', 'MMMDCCXXII', 'MMMDCCXXIII', 'MMMDCCXXIV', 'MMMDCCXXV', 'MMMDCCXXVI', 'MMMDCCXXVII', 'MMMDCCXXVIII', 'MMMDCCXXIX', 'MMMDCCXXX', 'MMMDCCXXXI', 'MMMDCCXXXII', 'MMMDCCXXXIII', 'MMMDCCXXXIV', 'MMMDCCXXXV', 'MMMDCCXXXVI', 'MMMDCCXXXVII', 'MMMDCCXXXVIII', 'MMMDCCXXXIX', 'MMMDCCXL', 'MMMDCCXLI', 'MMMDCCXLII', 'MMMDCCXLIII', 'MMMDCCXLIV', 'MMMDCCVL', 'MMMDCCVLI', 'MMMDCCVLII', 'MMMDCCVLIII', 'MMMDCCVLIV', 'MMMDCCL', 'MMMDCCLI', 'MMMDCCLII', 'MMMDCCLIII', 'MMMDCCLIV', 'MMMDCCLV', 'MMMDCCLVI', 'MMMDCCLVII', 'MMMDCCLVIII', 'MMMDCCLIX', 'MMMDCCLX', 'MMMDCCLXI', 'MMMDCCLXII', 'MMMDCCLXIII', 'MMMDCCLXIV', 'MMMDCCLXV', 'MMMDCCLXVI', 'MMMDCCLXVII', 'MMMDCCLXVIII', 'MMMDCCLXIX', 'MMMDCCLXX', 'MMMDCCLXXI', 'MMMDCCLXXII', 'MMMDCCLXXIII', 'MMMDCCLXXIV', 'MMMDCCLXXV', 'MMMDCCLXXVI', 'MMMDCCLXXVII', 'MMMDCCLXXVIII', 'MMMDCCLXXIX', 'MMMDCCLXXX', 'MMMDCCLXXXI', 'MMMDCCLXXXII', 'MMMDCCLXXXIII', 'MMMDCCLXXXIV', 'MMMDCCLXXXV', 'MMMDCCLXXXVI', 'MMMDCCLXXXVII', 'MMMDCCLXXXVIII', 'MMMDCCLXXXIX', 'MMMDCCXC', 'MMMDCCXCI', 'MMMDCCXCII', 'MMMDCCXCIII', 'MMMDCCXCIV', 'MMMDCCVC', 'MMMDCCVCI', 'MMMDCCVCII', 'MMMDCCVCIII', 'MMMDCCVCIV', 'MMMDCCC', 'MMMDCCCI', 'MMMDCCCII', 'MMMDCCCIII', 'MMMDCCCIV', 'MMMDCCCV', 'MMMDCCCVI', 'MMMDCCCVII', 'MMMDCCCVIII', 'MMMDCCCIX', 'MMMDCCCX', 'MMMDCCCXI', 'MMMDCCCXII', 'MMMDCCCXIII', 'MMMDCCCXIV', 'MMMDCCCXV', 'MMMDCCCXVI', 'MMMDCCCXVII', 'MMMDCCCXVIII', 'MMMDCCCXIX', 'MMMDCCCXX', 'MMMDCCCXXI', 'MMMDCCCXXII', 'MMMDCCCXXIII', 'MMMDCCCXXIV', 'MMMDCCCXXV', 'MMMDCCCXXVI', 'MMMDCCCXXVII', 'MMMDCCCXXVIII', 'MMMDCCCXXIX', 'MMMDCCCXXX', 'MMMDCCCXXXI', 'MMMDCCCXXXII', 'MMMDCCCXXXIII', 'MMMDCCCXXXIV', 'MMMDCCCXXXV', 'MMMDCCCXXXVI', 'MMMDCCCXXXVII', 'MMMDCCCXXXVIII', 'MMMDCCCXXXIX', 'MMMDCCCXL', 'MMMDCCCXLI', 'MMMDCCCXLII', 'MMMDCCCXLIII', 'MMMDCCCXLIV', 'MMMDCCCVL', 'MMMDCCCVLI', 'MMMDCCCVLII', 'MMMDCCCVLIII', 'MMMDCCCVLIV', 'MMMDCCCL', 'MMMDCCCLI', 'MMMDCCCLII', 'MMMDCCCLIII', 'MMMDCCCLIV', 'MMMDCCCLV', 'MMMDCCCLVI', 'MMMDCCCLVII', 'MMMDCCCLVIII', 'MMMDCCCLIX', 'MMMDCCCLX', 'MMMDCCCLXI', 'MMMDCCCLXII', 'MMMDCCCLXIII', 'MMMDCCCLXIV', 'MMMDCCCLXV', 'MMMDCCCLXVI', 'MMMDCCCLXVII', 'MMMDCCCLXVIII', 'MMMDCCCLXIX', 'MMMDCCCLXX', 'MMMDCCCLXXI', 'MMMDCCCLXXII', 'MMMDCCCLXXIII', 'MMMDCCCLXXIV', 'MMMDCCCLXXV', 'MMMDCCCLXXVI', 'MMMDCCCLXXVII', 'MMMDCCCLXXVIII', 'MMMDCCCLXXIX', 'MMMDCCCLXXX', 'MMMDCCCLXXXI', 'MMMDCCCLXXXII', 'MMMDCCCLXXXIII', 'MMMDCCCLXXXIV', 'MMMDCCCLXXXV', 'MMMDCCCLXXXVI', 'MMMDCCCLXXXVII', 'MMMDCCCLXXXVIII', 'MMMDCCCLXXXIX', 'MMMDCCCXC', 'MMMDCCCXCI', 'MMMDCCCXCII', 'MMMDCCCXCIII', 'MMMDCCCXCIV', 'MMMDCCCVC', 'MMMDCCCVCI', 'MMMDCCCVCII', 'MMMDCCCVCIII', 'MMMDCCCVCIV', 'MMMCM', 'MMMCMI', 'MMMCMII', 'MMMCMIII', 'MMMCMIV', 'MMMCMV', 'MMMCMVI', 'MMMCMVII', 'MMMCMVIII', 'MMMCMIX', 'MMMCMX', 'MMMCMXI', 'MMMCMXII', 'MMMCMXIII', 'MMMCMXIV', 'MMMCMXV', 'MMMCMXVI', 'MMMCMXVII', 'MMMCMXVIII', 'MMMCMXIX', 'MMMCMXX', 'MMMCMXXI', 'MMMCMXXII', 'MMMCMXXIII', 'MMMCMXXIV', 'MMMCMXXV', 'MMMCMXXVI', 'MMMCMXXVII', 'MMMCMXXVIII', 'MMMCMXXIX', 'MMMCMXXX', 'MMMCMXXXI', 'MMMCMXXXII', 'MMMCMXXXIII', 'MMMCMXXXIV', 'MMMCMXXXV', 'MMMCMXXXVI', 'MMMCMXXXVII', 'MMMCMXXXVIII', 'MMMCMXXXIX', 'MMMCMXL', 'MMMCMXLI', 'MMMCMXLII', 'MMMCMXLIII', 'MMMCMXLIV', 'MMMCMVL', 'MMMCMVLI', 'MMMCMVLII', 'MMMCMVLIII', 'MMMCMVLIV', 'MMMLM', 'MMMLMI', 'MMMLMII', 'MMMLMIII', 'MMMLMIV', 'MMMLMV', 'MMMLMVI', 'MMMLMVII', 'MMMLMVIII', 'MMMLMIX', 'MMMLMX', 'MMMLMXI', 'MMMLMXII', 'MMMLMXIII', 'MMMLMXIV', 'MMMLMXV', 'MMMLMXVI', 'MMMLMXVII', 'MMMLMXVIII', 'MMMLMXIX', 'MMMLMXX', 'MMMLMXXI', 'MMMLMXXII', 'MMMLMXXIII', 'MMMLMXXIV', 'MMMLMXXV', 'MMMLMXXVI', 'MMMLMXXVII', 'MMMLMXXVIII', 'MMMLMXXIX', 'MMMLMXXX', 'MMMLMXXXI', 'MMMLMXXXII', 'MMMLMXXXIII', 'MMMLMXXXIV', 'MMMLMXXXV', 'MMMLMXXXVI', 'MMMLMXXXVII', 'MMMLMXXXVIII', 'MMMLMXXXIX', 'MMMLMXL', 'MMMLMXLI', 'MMMLMXLII', 'MMMLMXLIII', 'MMMLMXLIV', 'MMMLMVL', 'MMMLMVLI', 'MMMLMVLII', 'MMMLMVLIII', 'MMMLMVLIV', ] -const mode2 = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX', 'XXI', 'XXII', 'XXIII', 'XXIV', 'XXV', 'XXVI', 'XXVII', 'XXVIII', 'XXIX', 'XXX', 'XXXI', 'XXXII', 'XXXIII', 'XXXIV', 'XXXV', 'XXXVI', 'XXXVII', 'XXXVIII', 'XXXIX', 'XL', 'XLI', 'XLII', 'XLIII', 'XLIV', 'VL', 'VLI', 'VLII', 'VLIII', 'IL', 'L', 'LI', 'LII', 'LIII', 'LIV', 'LV', 'LVI', 'LVII', 'LVIII', 'LIX', 'LX', 'LXI', 'LXII', 'LXIII', 'LXIV', 'LXV', 'LXVI', 'LXVII', 'LXVIII', 'LXIX', 'LXX', 'LXXI', 'LXXII', 'LXXIII', 'LXXIV', 'LXXV', 'LXXVI', 'LXXVII', 'LXXVIII', 'LXXIX', 'LXXX', 'LXXXI', 'LXXXII', 'LXXXIII', 'LXXXIV', 'LXXXV', 'LXXXVI', 'LXXXVII', 'LXXXVIII', 'LXXXIX', 'XC', 'XCI', 'XCII', 'XCIII', 'XCIV', 'VC', 'VCI', 'VCII', 'VCIII', 'IC', 'C', 'CI', 'CII', 'CIII', 'CIV', 'CV', 'CVI', 'CVII', 'CVIII', 'CIX', 'CX', 'CXI', 'CXII', 'CXIII', 'CXIV', 'CXV', 'CXVI', 'CXVII', 'CXVIII', 'CXIX', 'CXX', 'CXXI', 'CXXII', 'CXXIII', 'CXXIV', 'CXXV', 'CXXVI', 'CXXVII', 'CXXVIII', 'CXXIX', 'CXXX', 'CXXXI', 'CXXXII', 'CXXXIII', 'CXXXIV', 'CXXXV', 'CXXXVI', 'CXXXVII', 'CXXXVIII', 'CXXXIX', 'CXL', 'CXLI', 'CXLII', 'CXLIII', 'CXLIV', 'CVL', 'CVLI', 'CVLII', 'CVLIII', 'CIL', 'CL', 'CLI', 'CLII', 'CLIII', 'CLIV', 'CLV', 'CLVI', 'CLVII', 'CLVIII', 'CLIX', 'CLX', 'CLXI', 'CLXII', 'CLXIII', 'CLXIV', 'CLXV', 'CLXVI', 'CLXVII', 'CLXVIII', 'CLXIX', 'CLXX', 'CLXXI', 'CLXXII', 'CLXXIII', 'CLXXIV', 'CLXXV', 'CLXXVI', 'CLXXVII', 'CLXXVIII', 'CLXXIX', 'CLXXX', 'CLXXXI', 'CLXXXII', 'CLXXXIII', 'CLXXXIV', 'CLXXXV', 'CLXXXVI', 'CLXXXVII', 'CLXXXVIII', 'CLXXXIX', 'CXC', 'CXCI', 'CXCII', 'CXCIII', 'CXCIV', 'CVC', 'CVCI', 'CVCII', 'CVCIII', 'CIC', 'CC', 'CCI', 'CCII', 'CCIII', 'CCIV', 'CCV', 'CCVI', 'CCVII', 'CCVIII', 'CCIX', 'CCX', 'CCXI', 'CCXII', 'CCXIII', 'CCXIV', 'CCXV', 'CCXVI', 'CCXVII', 'CCXVIII', 'CCXIX', 'CCXX', 'CCXXI', 'CCXXII', 'CCXXIII', 'CCXXIV', 'CCXXV', 'CCXXVI', 'CCXXVII', 'CCXXVIII', 'CCXXIX', 'CCXXX', 'CCXXXI', 'CCXXXII', 'CCXXXIII', 'CCXXXIV', 'CCXXXV', 'CCXXXVI', 'CCXXXVII', 'CCXXXVIII', 'CCXXXIX', 'CCXL', 'CCXLI', 'CCXLII', 'CCXLIII', 'CCXLIV', 'CCVL', 'CCVLI', 'CCVLII', 'CCVLIII', 'CCIL', 'CCL', 'CCLI', 'CCLII', 'CCLIII', 'CCLIV', 'CCLV', 'CCLVI', 'CCLVII', 'CCLVIII', 'CCLIX', 'CCLX', 'CCLXI', 'CCLXII', 'CCLXIII', 'CCLXIV', 'CCLXV', 'CCLXVI', 'CCLXVII', 'CCLXVIII', 'CCLXIX', 'CCLXX', 'CCLXXI', 'CCLXXII', 'CCLXXIII', 'CCLXXIV', 'CCLXXV', 'CCLXXVI', 'CCLXXVII', 'CCLXXVIII', 'CCLXXIX', 'CCLXXX', 'CCLXXXI', 'CCLXXXII', 'CCLXXXIII', 'CCLXXXIV', 'CCLXXXV', 'CCLXXXVI', 'CCLXXXVII', 'CCLXXXVIII', 'CCLXXXIX', 'CCXC', 'CCXCI', 'CCXCII', 'CCXCIII', 'CCXCIV', 'CCVC', 'CCVCI', 'CCVCII', 'CCVCIII', 'CCIC', 'CCC', 'CCCI', 'CCCII', 'CCCIII', 'CCCIV', 'CCCV', 'CCCVI', 'CCCVII', 'CCCVIII', 'CCCIX', 'CCCX', 'CCCXI', 'CCCXII', 'CCCXIII', 'CCCXIV', 'CCCXV', 'CCCXVI', 'CCCXVII', 'CCCXVIII', 'CCCXIX', 'CCCXX', 'CCCXXI', 'CCCXXII', 'CCCXXIII', 'CCCXXIV', 'CCCXXV', 'CCCXXVI', 'CCCXXVII', 'CCCXXVIII', 'CCCXXIX', 'CCCXXX', 'CCCXXXI', 'CCCXXXII', 'CCCXXXIII', 'CCCXXXIV', 'CCCXXXV', 'CCCXXXVI', 'CCCXXXVII', 'CCCXXXVIII', 'CCCXXXIX', 'CCCXL', 'CCCXLI', 'CCCXLII', 'CCCXLIII', 'CCCXLIV', 'CCCVL', 'CCCVLI', 'CCCVLII', 'CCCVLIII', 'CCCIL', 'CCCL', 'CCCLI', 'CCCLII', 'CCCLIII', 'CCCLIV', 'CCCLV', 'CCCLVI', 'CCCLVII', 'CCCLVIII', 'CCCLIX', 'CCCLX', 'CCCLXI', 'CCCLXII', 'CCCLXIII', 'CCCLXIV', 'CCCLXV', 'CCCLXVI', 'CCCLXVII', 'CCCLXVIII', 'CCCLXIX', 'CCCLXX', 'CCCLXXI', 'CCCLXXII', 'CCCLXXIII', 'CCCLXXIV', 'CCCLXXV', 'CCCLXXVI', 'CCCLXXVII', 'CCCLXXVIII', 'CCCLXXIX', 'CCCLXXX', 'CCCLXXXI', 'CCCLXXXII', 'CCCLXXXIII', 'CCCLXXXIV', 'CCCLXXXV', 'CCCLXXXVI', 'CCCLXXXVII', 'CCCLXXXVIII', 'CCCLXXXIX', 'CCCXC', 'CCCXCI', 'CCCXCII', 'CCCXCIII', 'CCCXCIV', 'CCCVC', 'CCCVCI', 'CCCVCII', 'CCCVCIII', 'CCCIC', 'CD', 'CDI', 'CDII', 'CDIII', 'CDIV', 'CDV', 'CDVI', 'CDVII', 'CDVIII', 'CDIX', 'CDX', 'CDXI', 'CDXII', 'CDXIII', 'CDXIV', 'CDXV', 'CDXVI', 'CDXVII', 'CDXVIII', 'CDXIX', 'CDXX', 'CDXXI', 'CDXXII', 'CDXXIII', 'CDXXIV', 'CDXXV', 'CDXXVI', 'CDXXVII', 'CDXXVIII', 'CDXXIX', 'CDXXX', 'CDXXXI', 'CDXXXII', 'CDXXXIII', 'CDXXXIV', 'CDXXXV', 'CDXXXVI', 'CDXXXVII', 'CDXXXVIII', 'CDXXXIX', 'CDXL', 'CDXLI', 'CDXLII', 'CDXLIII', 'CDXLIV', 'CDVL', 'CDVLI', 'CDVLII', 'CDVLIII', 'CDIL', 'LD', 'LDI', 'LDII', 'LDIII', 'LDIV', 'LDV', 'LDVI', 'LDVII', 'LDVIII', 'LDIX', 'LDX', 'LDXI', 'LDXII', 'LDXIII', 'LDXIV', 'LDXV', 'LDXVI', 'LDXVII', 'LDXVIII', 'LDXIX', 'LDXX', 'LDXXI', 'LDXXII', 'LDXXIII', 'LDXXIV', 'LDXXV', 'LDXXVI', 'LDXXVII', 'LDXXVIII', 'LDXXIX', 'LDXXX', 'LDXXXI', 'LDXXXII', 'LDXXXIII', 'LDXXXIV', 'LDXXXV', 'LDXXXVI', 'LDXXXVII', 'LDXXXVIII', 'LDXXXIX', 'XD', 'XDI', 'XDII', 'XDIII', 'XDIV', 'XDV', 'XDVI', 'XDVII', 'XDVIII', 'XDIX', 'D', 'DI', 'DII', 'DIII', 'DIV', 'DV', 'DVI', 'DVII', 'DVIII', 'DIX', 'DX', 'DXI', 'DXII', 'DXIII', 'DXIV', 'DXV', 'DXVI', 'DXVII', 'DXVIII', 'DXIX', 'DXX', 'DXXI', 'DXXII', 'DXXIII', 'DXXIV', 'DXXV', 'DXXVI', 'DXXVII', 'DXXVIII', 'DXXIX', 'DXXX', 'DXXXI', 'DXXXII', 'DXXXIII', 'DXXXIV', 'DXXXV', 'DXXXVI', 'DXXXVII', 'DXXXVIII', 'DXXXIX', 'DXL', 'DXLI', 'DXLII', 'DXLIII', 'DXLIV', 'DVL', 'DVLI', 'DVLII', 'DVLIII', 'DIL', 'DL', 'DLI', 'DLII', 'DLIII', 'DLIV', 'DLV', 'DLVI', 'DLVII', 'DLVIII', 'DLIX', 'DLX', 'DLXI', 'DLXII', 'DLXIII', 'DLXIV', 'DLXV', 'DLXVI', 'DLXVII', 'DLXVIII', 'DLXIX', 'DLXX', 'DLXXI', 'DLXXII', 'DLXXIII', 'DLXXIV', 'DLXXV', 'DLXXVI', 'DLXXVII', 'DLXXVIII', 'DLXXIX', 'DLXXX', 'DLXXXI', 'DLXXXII', 'DLXXXIII', 'DLXXXIV', 'DLXXXV', 'DLXXXVI', 'DLXXXVII', 'DLXXXVIII', 'DLXXXIX', 'DXC', 'DXCI', 'DXCII', 'DXCIII', 'DXCIV', 'DVC', 'DVCI', 'DVCII', 'DVCIII', 'DIC', 'DC', 'DCI', 'DCII', 'DCIII', 'DCIV', 'DCV', 'DCVI', 'DCVII', 'DCVIII', 'DCIX', 'DCX', 'DCXI', 'DCXII', 'DCXIII', 'DCXIV', 'DCXV', 'DCXVI', 'DCXVII', 'DCXVIII', 'DCXIX', 'DCXX', 'DCXXI', 'DCXXII', 'DCXXIII', 'DCXXIV', 'DCXXV', 'DCXXVI', 'DCXXVII', 'DCXXVIII', 'DCXXIX', 'DCXXX', 'DCXXXI', 'DCXXXII', 'DCXXXIII', 'DCXXXIV', 'DCXXXV', 'DCXXXVI', 'DCXXXVII', 'DCXXXVIII', 'DCXXXIX', 'DCXL', 'DCXLI', 'DCXLII', 'DCXLIII', 'DCXLIV', 'DCVL', 'DCVLI', 'DCVLII', 'DCVLIII', 'DCIL', 'DCL', 'DCLI', 'DCLII', 'DCLIII', 'DCLIV', 'DCLV', 'DCLVI', 'DCLVII', 'DCLVIII', 'DCLIX', 'DCLX', 'DCLXI', 'DCLXII', 'DCLXIII', 'DCLXIV', 'DCLXV', 'DCLXVI', 'DCLXVII', 'DCLXVIII', 'DCLXIX', 'DCLXX', 'DCLXXI', 'DCLXXII', 'DCLXXIII', 'DCLXXIV', 'DCLXXV', 'DCLXXVI', 'DCLXXVII', 'DCLXXVIII', 'DCLXXIX', 'DCLXXX', 'DCLXXXI', 'DCLXXXII', 'DCLXXXIII', 'DCLXXXIV', 'DCLXXXV', 'DCLXXXVI', 'DCLXXXVII', 'DCLXXXVIII', 'DCLXXXIX', 'DCXC', 'DCXCI', 'DCXCII', 'DCXCIII', 'DCXCIV', 'DCVC', 'DCVCI', 'DCVCII', 'DCVCIII', 'DCIC', 'DCC', 'DCCI', 'DCCII', 'DCCIII', 'DCCIV', 'DCCV', 'DCCVI', 'DCCVII', 'DCCVIII', 'DCCIX', 'DCCX', 'DCCXI', 'DCCXII', 'DCCXIII', 'DCCXIV', 'DCCXV', 'DCCXVI', 'DCCXVII', 'DCCXVIII', 'DCCXIX', 'DCCXX', 'DCCXXI', 'DCCXXII', 'DCCXXIII', 'DCCXXIV', 'DCCXXV', 'DCCXXVI', 'DCCXXVII', 'DCCXXVIII', 'DCCXXIX', 'DCCXXX', 'DCCXXXI', 'DCCXXXII', 'DCCXXXIII', 'DCCXXXIV', 'DCCXXXV', 'DCCXXXVI', 'DCCXXXVII', 'DCCXXXVIII', 'DCCXXXIX', 'DCCXL', 'DCCXLI', 'DCCXLII', 'DCCXLIII', 'DCCXLIV', 'DCCVL', 'DCCVLI', 'DCCVLII', 'DCCVLIII', 'DCCIL', 'DCCL', 'DCCLI', 'DCCLII', 'DCCLIII', 'DCCLIV', 'DCCLV', 'DCCLVI', 'DCCLVII', 'DCCLVIII', 'DCCLIX', 'DCCLX', 'DCCLXI', 'DCCLXII', 'DCCLXIII', 'DCCLXIV', 'DCCLXV', 'DCCLXVI', 'DCCLXVII', 'DCCLXVIII', 'DCCLXIX', 'DCCLXX', 'DCCLXXI', 'DCCLXXII', 'DCCLXXIII', 'DCCLXXIV', 'DCCLXXV', 'DCCLXXVI', 'DCCLXXVII', 'DCCLXXVIII', 'DCCLXXIX', 'DCCLXXX', 'DCCLXXXI', 'DCCLXXXII', 'DCCLXXXIII', 'DCCLXXXIV', 'DCCLXXXV', 'DCCLXXXVI', 'DCCLXXXVII', 'DCCLXXXVIII', 'DCCLXXXIX', 'DCCXC', 'DCCXCI', 'DCCXCII', 'DCCXCIII', 'DCCXCIV', 'DCCVC', 'DCCVCI', 'DCCVCII', 'DCCVCIII', 'DCCIC', 'DCCC', 'DCCCI', 'DCCCII', 'DCCCIII', 'DCCCIV', 'DCCCV', 'DCCCVI', 'DCCCVII', 'DCCCVIII', 'DCCCIX', 'DCCCX', 'DCCCXI', 'DCCCXII', 'DCCCXIII', 'DCCCXIV', 'DCCCXV', 'DCCCXVI', 'DCCCXVII', 'DCCCXVIII', 'DCCCXIX', 'DCCCXX', 'DCCCXXI', 'DCCCXXII', 'DCCCXXIII', 'DCCCXXIV', 'DCCCXXV', 'DCCCXXVI', 'DCCCXXVII', 'DCCCXXVIII', 'DCCCXXIX', 'DCCCXXX', 'DCCCXXXI', 'DCCCXXXII', 'DCCCXXXIII', 'DCCCXXXIV', 'DCCCXXXV', 'DCCCXXXVI', 'DCCCXXXVII', 'DCCCXXXVIII', 'DCCCXXXIX', 'DCCCXL', 'DCCCXLI', 'DCCCXLII', 'DCCCXLIII', 'DCCCXLIV', 'DCCCVL', 'DCCCVLI', 'DCCCVLII', 'DCCCVLIII', 'DCCCIL', 'DCCCL', 'DCCCLI', 'DCCCLII', 'DCCCLIII', 'DCCCLIV', 'DCCCLV', 'DCCCLVI', 'DCCCLVII', 'DCCCLVIII', 'DCCCLIX', 'DCCCLX', 'DCCCLXI', 'DCCCLXII', 'DCCCLXIII', 'DCCCLXIV', 'DCCCLXV', 'DCCCLXVI', 'DCCCLXVII', 'DCCCLXVIII', 'DCCCLXIX', 'DCCCLXX', 'DCCCLXXI', 'DCCCLXXII', 'DCCCLXXIII', 'DCCCLXXIV', 'DCCCLXXV', 'DCCCLXXVI', 'DCCCLXXVII', 'DCCCLXXVIII', 'DCCCLXXIX', 'DCCCLXXX', 'DCCCLXXXI', 'DCCCLXXXII', 'DCCCLXXXIII', 'DCCCLXXXIV', 'DCCCLXXXV', 'DCCCLXXXVI', 'DCCCLXXXVII', 'DCCCLXXXVIII', 'DCCCLXXXIX', 'DCCCXC', 'DCCCXCI', 'DCCCXCII', 'DCCCXCIII', 'DCCCXCIV', 'DCCCVC', 'DCCCVCI', 'DCCCVCII', 'DCCCVCIII', 'DCCCIC', 'CM', 'CMI', 'CMII', 'CMIII', 'CMIV', 'CMV', 'CMVI', 'CMVII', 'CMVIII', 'CMIX', 'CMX', 'CMXI', 'CMXII', 'CMXIII', 'CMXIV', 'CMXV', 'CMXVI', 'CMXVII', 'CMXVIII', 'CMXIX', 'CMXX', 'CMXXI', 'CMXXII', 'CMXXIII', 'CMXXIV', 'CMXXV', 'CMXXVI', 'CMXXVII', 'CMXXVIII', 'CMXXIX', 'CMXXX', 'CMXXXI', 'CMXXXII', 'CMXXXIII', 'CMXXXIV', 'CMXXXV', 'CMXXXVI', 'CMXXXVII', 'CMXXXVIII', 'CMXXXIX', 'CMXL', 'CMXLI', 'CMXLII', 'CMXLIII', 'CMXLIV', 'CMVL', 'CMVLI', 'CMVLII', 'CMVLIII', 'CMIL', 'LM', 'LMI', 'LMII', 'LMIII', 'LMIV', 'LMV', 'LMVI', 'LMVII', 'LMVIII', 'LMIX', 'LMX', 'LMXI', 'LMXII', 'LMXIII', 'LMXIV', 'LMXV', 'LMXVI', 'LMXVII', 'LMXVIII', 'LMXIX', 'LMXX', 'LMXXI', 'LMXXII', 'LMXXIII', 'LMXXIV', 'LMXXV', 'LMXXVI', 'LMXXVII', 'LMXXVIII', 'LMXXIX', 'LMXXX', 'LMXXXI', 'LMXXXII', 'LMXXXIII', 'LMXXXIV', 'LMXXXV', 'LMXXXVI', 'LMXXXVII', 'LMXXXVIII', 'LMXXXIX', 'XM', 'XMI', 'XMII', 'XMIII', 'XMIV', 'XMV', 'XMVI', 'XMVII', 'XMVIII', 'XMIX', 'M', 'MI', 'MII', 'MIII', 'MIV', 'MV', 'MVI', 'MVII', 'MVIII', 'MIX', 'MX', 'MXI', 'MXII', 'MXIII', 'MXIV', 'MXV', 'MXVI', 'MXVII', 'MXVIII', 'MXIX', 'MXX', 'MXXI', 'MXXII', 'MXXIII', 'MXXIV', 'MXXV', 'MXXVI', 'MXXVII', 'MXXVIII', 'MXXIX', 'MXXX', 'MXXXI', 'MXXXII', 'MXXXIII', 'MXXXIV', 'MXXXV', 'MXXXVI', 'MXXXVII', 'MXXXVIII', 'MXXXIX', 'MXL', 'MXLI', 'MXLII', 'MXLIII', 'MXLIV', 'MVL', 'MVLI', 'MVLII', 'MVLIII', 'MIL', 'ML', 'MLI', 'MLII', 'MLIII', 'MLIV', 'MLV', 'MLVI', 'MLVII', 'MLVIII', 'MLIX', 'MLX', 'MLXI', 'MLXII', 'MLXIII', 'MLXIV', 'MLXV', 'MLXVI', 'MLXVII', 'MLXVIII', 'MLXIX', 'MLXX', 'MLXXI', 'MLXXII', 'MLXXIII', 'MLXXIV', 'MLXXV', 'MLXXVI', 'MLXXVII', 'MLXXVIII', 'MLXXIX', 'MLXXX', 'MLXXXI', 'MLXXXII', 'MLXXXIII', 'MLXXXIV', 'MLXXXV', 'MLXXXVI', 'MLXXXVII', 'MLXXXVIII', 'MLXXXIX', 'MXC', 'MXCI', 'MXCII', 'MXCIII', 'MXCIV', 'MVC', 'MVCI', 'MVCII', 'MVCIII', 'MIC', 'MC', 'MCI', 'MCII', 'MCIII', 'MCIV', 'MCV', 'MCVI', 'MCVII', 'MCVIII', 'MCIX', 'MCX', 'MCXI', 'MCXII', 'MCXIII', 'MCXIV', 'MCXV', 'MCXVI', 'MCXVII', 'MCXVIII', 'MCXIX', 'MCXX', 'MCXXI', 'MCXXII', 'MCXXIII', 'MCXXIV', 'MCXXV', 'MCXXVI', 'MCXXVII', 'MCXXVIII', 'MCXXIX', 'MCXXX', 'MCXXXI', 'MCXXXII', 'MCXXXIII', 'MCXXXIV', 'MCXXXV', 'MCXXXVI', 'MCXXXVII', 'MCXXXVIII', 'MCXXXIX', 'MCXL', 'MCXLI', 'MCXLII', 'MCXLIII', 'MCXLIV', 'MCVL', 'MCVLI', 'MCVLII', 'MCVLIII', 'MCIL', 'MCL', 'MCLI', 'MCLII', 'MCLIII', 'MCLIV', 'MCLV', 'MCLVI', 'MCLVII', 'MCLVIII', 'MCLIX', 'MCLX', 'MCLXI', 'MCLXII', 'MCLXIII', 'MCLXIV', 'MCLXV', 'MCLXVI', 'MCLXVII', 'MCLXVIII', 'MCLXIX', 'MCLXX', 'MCLXXI', 'MCLXXII', 'MCLXXIII', 'MCLXXIV', 'MCLXXV', 'MCLXXVI', 'MCLXXVII', 'MCLXXVIII', 'MCLXXIX', 'MCLXXX', 'MCLXXXI', 'MCLXXXII', 'MCLXXXIII', 'MCLXXXIV', 'MCLXXXV', 'MCLXXXVI', 'MCLXXXVII', 'MCLXXXVIII', 'MCLXXXIX', 'MCXC', 'MCXCI', 'MCXCII', 'MCXCIII', 'MCXCIV', 'MCVC', 'MCVCI', 'MCVCII', 'MCVCIII', 'MCIC', 'MCC', 'MCCI', 'MCCII', 'MCCIII', 'MCCIV', 'MCCV', 'MCCVI', 'MCCVII', 'MCCVIII', 'MCCIX', 'MCCX', 'MCCXI', 'MCCXII', 'MCCXIII', 'MCCXIV', 'MCCXV', 'MCCXVI', 'MCCXVII', 'MCCXVIII', 'MCCXIX', 'MCCXX', 'MCCXXI', 'MCCXXII', 'MCCXXIII', 'MCCXXIV', 'MCCXXV', 'MCCXXVI', 'MCCXXVII', 'MCCXXVIII', 'MCCXXIX', 'MCCXXX', 'MCCXXXI', 'MCCXXXII', 'MCCXXXIII', 'MCCXXXIV', 'MCCXXXV', 'MCCXXXVI', 'MCCXXXVII', 'MCCXXXVIII', 'MCCXXXIX', 'MCCXL', 'MCCXLI', 'MCCXLII', 'MCCXLIII', 'MCCXLIV', 'MCCVL', 'MCCVLI', 'MCCVLII', 'MCCVLIII', 'MCCIL', 'MCCL', 'MCCLI', 'MCCLII', 'MCCLIII', 'MCCLIV', 'MCCLV', 'MCCLVI', 'MCCLVII', 'MCCLVIII', 'MCCLIX', 'MCCLX', 'MCCLXI', 'MCCLXII', 'MCCLXIII', 'MCCLXIV', 'MCCLXV', 'MCCLXVI', 'MCCLXVII', 'MCCLXVIII', 'MCCLXIX', 'MCCLXX', 'MCCLXXI', 'MCCLXXII', 'MCCLXXIII', 'MCCLXXIV', 'MCCLXXV', 'MCCLXXVI', 'MCCLXXVII', 'MCCLXXVIII', 'MCCLXXIX', 'MCCLXXX', 'MCCLXXXI', 'MCCLXXXII', 'MCCLXXXIII', 'MCCLXXXIV', 'MCCLXXXV', 'MCCLXXXVI', 'MCCLXXXVII', 'MCCLXXXVIII', 'MCCLXXXIX', 'MCCXC', 'MCCXCI', 'MCCXCII', 'MCCXCIII', 'MCCXCIV', 'MCCVC', 'MCCVCI', 'MCCVCII', 'MCCVCIII', 'MCCIC', 'MCCC', 'MCCCI', 'MCCCII', 'MCCCIII', 'MCCCIV', 'MCCCV', 'MCCCVI', 'MCCCVII', 'MCCCVIII', 'MCCCIX', 'MCCCX', 'MCCCXI', 'MCCCXII', 'MCCCXIII', 'MCCCXIV', 'MCCCXV', 'MCCCXVI', 'MCCCXVII', 'MCCCXVIII', 'MCCCXIX', 'MCCCXX', 'MCCCXXI', 'MCCCXXII', 'MCCCXXIII', 'MCCCXXIV', 'MCCCXXV', 'MCCCXXVI', 'MCCCXXVII', 'MCCCXXVIII', 'MCCCXXIX', 'MCCCXXX', 'MCCCXXXI', 'MCCCXXXII', 'MCCCXXXIII', 'MCCCXXXIV', 'MCCCXXXV', 'MCCCXXXVI', 'MCCCXXXVII', 'MCCCXXXVIII', 'MCCCXXXIX', 'MCCCXL', 'MCCCXLI', 'MCCCXLII', 'MCCCXLIII', 'MCCCXLIV', 'MCCCVL', 'MCCCVLI', 'MCCCVLII', 'MCCCVLIII', 'MCCCIL', 'MCCCL', 'MCCCLI', 'MCCCLII', 'MCCCLIII', 'MCCCLIV', 'MCCCLV', 'MCCCLVI', 'MCCCLVII', 'MCCCLVIII', 'MCCCLIX', 'MCCCLX', 'MCCCLXI', 'MCCCLXII', 'MCCCLXIII', 'MCCCLXIV', 'MCCCLXV', 'MCCCLXVI', 'MCCCLXVII', 'MCCCLXVIII', 'MCCCLXIX', 'MCCCLXX', 'MCCCLXXI', 'MCCCLXXII', 'MCCCLXXIII', 'MCCCLXXIV', 'MCCCLXXV', 'MCCCLXXVI', 'MCCCLXXVII', 'MCCCLXXVIII', 'MCCCLXXIX', 'MCCCLXXX', 'MCCCLXXXI', 'MCCCLXXXII', 'MCCCLXXXIII', 'MCCCLXXXIV', 'MCCCLXXXV', 'MCCCLXXXVI', 'MCCCLXXXVII', 'MCCCLXXXVIII', 'MCCCLXXXIX', 'MCCCXC', 'MCCCXCI', 'MCCCXCII', 'MCCCXCIII', 'MCCCXCIV', 'MCCCVC', 'MCCCVCI', 'MCCCVCII', 'MCCCVCIII', 'MCCCIC', 'MCD', 'MCDI', 'MCDII', 'MCDIII', 'MCDIV', 'MCDV', 'MCDVI', 'MCDVII', 'MCDVIII', 'MCDIX', 'MCDX', 'MCDXI', 'MCDXII', 'MCDXIII', 'MCDXIV', 'MCDXV', 'MCDXVI', 'MCDXVII', 'MCDXVIII', 'MCDXIX', 'MCDXX', 'MCDXXI', 'MCDXXII', 'MCDXXIII', 'MCDXXIV', 'MCDXXV', 'MCDXXVI', 'MCDXXVII', 'MCDXXVIII', 'MCDXXIX', 'MCDXXX', 'MCDXXXI', 'MCDXXXII', 'MCDXXXIII', 'MCDXXXIV', 'MCDXXXV', 'MCDXXXVI', 'MCDXXXVII', 'MCDXXXVIII', 'MCDXXXIX', 'MCDXL', 'MCDXLI', 'MCDXLII', 'MCDXLIII', 'MCDXLIV', 'MCDVL', 'MCDVLI', 'MCDVLII', 'MCDVLIII', 'MCDIL', 'MLD', 'MLDI', 'MLDII', 'MLDIII', 'MLDIV', 'MLDV', 'MLDVI', 'MLDVII', 'MLDVIII', 'MLDIX', 'MLDX', 'MLDXI', 'MLDXII', 'MLDXIII', 'MLDXIV', 'MLDXV', 'MLDXVI', 'MLDXVII', 'MLDXVIII', 'MLDXIX', 'MLDXX', 'MLDXXI', 'MLDXXII', 'MLDXXIII', 'MLDXXIV', 'MLDXXV', 'MLDXXVI', 'MLDXXVII', 'MLDXXVIII', 'MLDXXIX', 'MLDXXX', 'MLDXXXI', 'MLDXXXII', 'MLDXXXIII', 'MLDXXXIV', 'MLDXXXV', 'MLDXXXVI', 'MLDXXXVII', 'MLDXXXVIII', 'MLDXXXIX', 'MXD', 'MXDI', 'MXDII', 'MXDIII', 'MXDIV', 'MXDV', 'MXDVI', 'MXDVII', 'MXDVIII', 'MXDIX', 'MD', 'MDI', 'MDII', 'MDIII', 'MDIV', 'MDV', 'MDVI', 'MDVII', 'MDVIII', 'MDIX', 'MDX', 'MDXI', 'MDXII', 'MDXIII', 'MDXIV', 'MDXV', 'MDXVI', 'MDXVII', 'MDXVIII', 'MDXIX', 'MDXX', 'MDXXI', 'MDXXII', 'MDXXIII', 'MDXXIV', 'MDXXV', 'MDXXVI', 'MDXXVII', 'MDXXVIII', 'MDXXIX', 'MDXXX', 'MDXXXI', 'MDXXXII', 'MDXXXIII', 'MDXXXIV', 'MDXXXV', 'MDXXXVI', 'MDXXXVII', 'MDXXXVIII', 'MDXXXIX', 'MDXL', 'MDXLI', 'MDXLII', 'MDXLIII', 'MDXLIV', 'MDVL', 'MDVLI', 'MDVLII', 'MDVLIII', 'MDIL', 'MDL', 'MDLI', 'MDLII', 'MDLIII', 'MDLIV', 'MDLV', 'MDLVI', 'MDLVII', 'MDLVIII', 'MDLIX', 'MDLX', 'MDLXI', 'MDLXII', 'MDLXIII', 'MDLXIV', 'MDLXV', 'MDLXVI', 'MDLXVII', 'MDLXVIII', 'MDLXIX', 'MDLXX', 'MDLXXI', 'MDLXXII', 'MDLXXIII', 'MDLXXIV', 'MDLXXV', 'MDLXXVI', 'MDLXXVII', 'MDLXXVIII', 'MDLXXIX', 'MDLXXX', 'MDLXXXI', 'MDLXXXII', 'MDLXXXIII', 'MDLXXXIV', 'MDLXXXV', 'MDLXXXVI', 'MDLXXXVII', 'MDLXXXVIII', 'MDLXXXIX', 'MDXC', 'MDXCI', 'MDXCII', 'MDXCIII', 'MDXCIV', 'MDVC', 'MDVCI', 'MDVCII', 'MDVCIII', 'MDIC', 'MDC', 'MDCI', 'MDCII', 'MDCIII', 'MDCIV', 'MDCV', 'MDCVI', 'MDCVII', 'MDCVIII', 'MDCIX', 'MDCX', 'MDCXI', 'MDCXII', 'MDCXIII', 'MDCXIV', 'MDCXV', 'MDCXVI', 'MDCXVII', 'MDCXVIII', 'MDCXIX', 'MDCXX', 'MDCXXI', 'MDCXXII', 'MDCXXIII', 'MDCXXIV', 'MDCXXV', 'MDCXXVI', 'MDCXXVII', 'MDCXXVIII', 'MDCXXIX', 'MDCXXX', 'MDCXXXI', 'MDCXXXII', 'MDCXXXIII', 'MDCXXXIV', 'MDCXXXV', 'MDCXXXVI', 'MDCXXXVII', 'MDCXXXVIII', 'MDCXXXIX', 'MDCXL', 'MDCXLI', 'MDCXLII', 'MDCXLIII', 'MDCXLIV', 'MDCVL', 'MDCVLI', 'MDCVLII', 'MDCVLIII', 'MDCIL', 'MDCL', 'MDCLI', 'MDCLII', 'MDCLIII', 'MDCLIV', 'MDCLV', 'MDCLVI', 'MDCLVII', 'MDCLVIII', 'MDCLIX', 'MDCLX', 'MDCLXI', 'MDCLXII', 'MDCLXIII', 'MDCLXIV', 'MDCLXV', 'MDCLXVI', 'MDCLXVII', 'MDCLXVIII', 'MDCLXIX', 'MDCLXX', 'MDCLXXI', 'MDCLXXII', 'MDCLXXIII', 'MDCLXXIV', 'MDCLXXV', 'MDCLXXVI', 'MDCLXXVII', 'MDCLXXVIII', 'MDCLXXIX', 'MDCLXXX', 'MDCLXXXI', 'MDCLXXXII', 'MDCLXXXIII', 'MDCLXXXIV', 'MDCLXXXV', 'MDCLXXXVI', 'MDCLXXXVII', 'MDCLXXXVIII', 'MDCLXXXIX', 'MDCXC', 'MDCXCI', 'MDCXCII', 'MDCXCIII', 'MDCXCIV', 'MDCVC', 'MDCVCI', 'MDCVCII', 'MDCVCIII', 'MDCIC', 'MDCC', 'MDCCI', 'MDCCII', 'MDCCIII', 'MDCCIV', 'MDCCV', 'MDCCVI', 'MDCCVII', 'MDCCVIII', 'MDCCIX', 'MDCCX', 'MDCCXI', 'MDCCXII', 'MDCCXIII', 'MDCCXIV', 'MDCCXV', 'MDCCXVI', 'MDCCXVII', 'MDCCXVIII', 'MDCCXIX', 'MDCCXX', 'MDCCXXI', 'MDCCXXII', 'MDCCXXIII', 'MDCCXXIV', 'MDCCXXV', 'MDCCXXVI', 'MDCCXXVII', 'MDCCXXVIII', 'MDCCXXIX', 'MDCCXXX', 'MDCCXXXI', 'MDCCXXXII', 'MDCCXXXIII', 'MDCCXXXIV', 'MDCCXXXV', 'MDCCXXXVI', 'MDCCXXXVII', 'MDCCXXXVIII', 'MDCCXXXIX', 'MDCCXL', 'MDCCXLI', 'MDCCXLII', 'MDCCXLIII', 'MDCCXLIV', 'MDCCVL', 'MDCCVLI', 'MDCCVLII', 'MDCCVLIII', 'MDCCIL', 'MDCCL', 'MDCCLI', 'MDCCLII', 'MDCCLIII', 'MDCCLIV', 'MDCCLV', 'MDCCLVI', 'MDCCLVII', 'MDCCLVIII', 'MDCCLIX', 'MDCCLX', 'MDCCLXI', 'MDCCLXII', 'MDCCLXIII', 'MDCCLXIV', 'MDCCLXV', 'MDCCLXVI', 'MDCCLXVII', 'MDCCLXVIII', 'MDCCLXIX', 'MDCCLXX', 'MDCCLXXI', 'MDCCLXXII', 'MDCCLXXIII', 'MDCCLXXIV', 'MDCCLXXV', 'MDCCLXXVI', 'MDCCLXXVII', 'MDCCLXXVIII', 'MDCCLXXIX', 'MDCCLXXX', 'MDCCLXXXI', 'MDCCLXXXII', 'MDCCLXXXIII', 'MDCCLXXXIV', 'MDCCLXXXV', 'MDCCLXXXVI', 'MDCCLXXXVII', 'MDCCLXXXVIII', 'MDCCLXXXIX', 'MDCCXC', 'MDCCXCI', 'MDCCXCII', 'MDCCXCIII', 'MDCCXCIV', 'MDCCVC', 'MDCCVCI', 'MDCCVCII', 'MDCCVCIII', 'MDCCIC', 'MDCCC', 'MDCCCI', 'MDCCCII', 'MDCCCIII', 'MDCCCIV', 'MDCCCV', 'MDCCCVI', 'MDCCCVII', 'MDCCCVIII', 'MDCCCIX', 'MDCCCX', 'MDCCCXI', 'MDCCCXII', 'MDCCCXIII', 'MDCCCXIV', 'MDCCCXV', 'MDCCCXVI', 'MDCCCXVII', 'MDCCCXVIII', 'MDCCCXIX', 'MDCCCXX', 'MDCCCXXI', 'MDCCCXXII', 'MDCCCXXIII', 'MDCCCXXIV', 'MDCCCXXV', 'MDCCCXXVI', 'MDCCCXXVII', 'MDCCCXXVIII', 'MDCCCXXIX', 'MDCCCXXX', 'MDCCCXXXI', 'MDCCCXXXII', 'MDCCCXXXIII', 'MDCCCXXXIV', 'MDCCCXXXV', 'MDCCCXXXVI', 'MDCCCXXXVII', 'MDCCCXXXVIII', 'MDCCCXXXIX', 'MDCCCXL', 'MDCCCXLI', 'MDCCCXLII', 'MDCCCXLIII', 'MDCCCXLIV', 'MDCCCVL', 'MDCCCVLI', 'MDCCCVLII', 'MDCCCVLIII', 'MDCCCIL', 'MDCCCL', 'MDCCCLI', 'MDCCCLII', 'MDCCCLIII', 'MDCCCLIV', 'MDCCCLV', 'MDCCCLVI', 'MDCCCLVII', 'MDCCCLVIII', 'MDCCCLIX', 'MDCCCLX', 'MDCCCLXI', 'MDCCCLXII', 'MDCCCLXIII', 'MDCCCLXIV', 'MDCCCLXV', 'MDCCCLXVI', 'MDCCCLXVII', 'MDCCCLXVIII', 'MDCCCLXIX', 'MDCCCLXX', 'MDCCCLXXI', 'MDCCCLXXII', 'MDCCCLXXIII', 'MDCCCLXXIV', 'MDCCCLXXV', 'MDCCCLXXVI', 'MDCCCLXXVII', 'MDCCCLXXVIII', 'MDCCCLXXIX', 'MDCCCLXXX', 'MDCCCLXXXI', 'MDCCCLXXXII', 'MDCCCLXXXIII', 'MDCCCLXXXIV', 'MDCCCLXXXV', 'MDCCCLXXXVI', 'MDCCCLXXXVII', 'MDCCCLXXXVIII', 'MDCCCLXXXIX', 'MDCCCXC', 'MDCCCXCI', 'MDCCCXCII', 'MDCCCXCIII', 'MDCCCXCIV', 'MDCCCVC', 'MDCCCVCI', 'MDCCCVCII', 'MDCCCVCIII', 'MDCCCIC', 'MCM', 'MCMI', 'MCMII', 'MCMIII', 'MCMIV', 'MCMV', 'MCMVI', 'MCMVII', 'MCMVIII', 'MCMIX', 'MCMX', 'MCMXI', 'MCMXII', 'MCMXIII', 'MCMXIV', 'MCMXV', 'MCMXVI', 'MCMXVII', 'MCMXVIII', 'MCMXIX', 'MCMXX', 'MCMXXI', 'MCMXXII', 'MCMXXIII', 'MCMXXIV', 'MCMXXV', 'MCMXXVI', 'MCMXXVII', 'MCMXXVIII', 'MCMXXIX', 'MCMXXX', 'MCMXXXI', 'MCMXXXII', 'MCMXXXIII', 'MCMXXXIV', 'MCMXXXV', 'MCMXXXVI', 'MCMXXXVII', 'MCMXXXVIII', 'MCMXXXIX', 'MCMXL', 'MCMXLI', 'MCMXLII', 'MCMXLIII', 'MCMXLIV', 'MCMVL', 'MCMVLI', 'MCMVLII', 'MCMVLIII', 'MCMIL', 'MLM', 'MLMI', 'MLMII', 'MLMIII', 'MLMIV', 'MLMV', 'MLMVI', 'MLMVII', 'MLMVIII', 'MLMIX', 'MLMX', 'MLMXI', 'MLMXII', 'MLMXIII', 'MLMXIV', 'MLMXV', 'MLMXVI', 'MLMXVII', 'MLMXVIII', 'MLMXIX', 'MLMXX', 'MLMXXI', 'MLMXXII', 'MLMXXIII', 'MLMXXIV', 'MLMXXV', 'MLMXXVI', 'MLMXXVII', 'MLMXXVIII', 'MLMXXIX', 'MLMXXX', 'MLMXXXI', 'MLMXXXII', 'MLMXXXIII', 'MLMXXXIV', 'MLMXXXV', 'MLMXXXVI', 'MLMXXXVII', 'MLMXXXVIII', 'MLMXXXIX', 'MXM', 'MXMI', 'MXMII', 'MXMIII', 'MXMIV', 'MXMV', 'MXMVI', 'MXMVII', 'MXMVIII', 'MXMIX', 'MM', 'MMI', 'MMII', 'MMIII', 'MMIV', 'MMV', 'MMVI', 'MMVII', 'MMVIII', 'MMIX', 'MMX', 'MMXI', 'MMXII', 'MMXIII', 'MMXIV', 'MMXV', 'MMXVI', 'MMXVII', 'MMXVIII', 'MMXIX', 'MMXX', 'MMXXI', 'MMXXII', 'MMXXIII', 'MMXXIV', 'MMXXV', 'MMXXVI', 'MMXXVII', 'MMXXVIII', 'MMXXIX', 'MMXXX', 'MMXXXI', 'MMXXXII', 'MMXXXIII', 'MMXXXIV', 'MMXXXV', 'MMXXXVI', 'MMXXXVII', 'MMXXXVIII', 'MMXXXIX', 'MMXL', 'MMXLI', 'MMXLII', 'MMXLIII', 'MMXLIV', 'MMVL', 'MMVLI', 'MMVLII', 'MMVLIII', 'MMIL', 'MML', 'MMLI', 'MMLII', 'MMLIII', 'MMLIV', 'MMLV', 'MMLVI', 'MMLVII', 'MMLVIII', 'MMLIX', 'MMLX', 'MMLXI', 'MMLXII', 'MMLXIII', 'MMLXIV', 'MMLXV', 'MMLXVI', 'MMLXVII', 'MMLXVIII', 'MMLXIX', 'MMLXX', 'MMLXXI', 'MMLXXII', 'MMLXXIII', 'MMLXXIV', 'MMLXXV', 'MMLXXVI', 'MMLXXVII', 'MMLXXVIII', 'MMLXXIX', 'MMLXXX', 'MMLXXXI', 'MMLXXXII', 'MMLXXXIII', 'MMLXXXIV', 'MMLXXXV', 'MMLXXXVI', 'MMLXXXVII', 'MMLXXXVIII', 'MMLXXXIX', 'MMXC', 'MMXCI', 'MMXCII', 'MMXCIII', 'MMXCIV', 'MMVC', 'MMVCI', 'MMVCII', 'MMVCIII', 'MMIC', 'MMC', 'MMCI', 'MMCII', 'MMCIII', 'MMCIV', 'MMCV', 'MMCVI', 'MMCVII', 'MMCVIII', 'MMCIX', 'MMCX', 'MMCXI', 'MMCXII', 'MMCXIII', 'MMCXIV', 'MMCXV', 'MMCXVI', 'MMCXVII', 'MMCXVIII', 'MMCXIX', 'MMCXX', 'MMCXXI', 'MMCXXII', 'MMCXXIII', 'MMCXXIV', 'MMCXXV', 'MMCXXVI', 'MMCXXVII', 'MMCXXVIII', 'MMCXXIX', 'MMCXXX', 'MMCXXXI', 'MMCXXXII', 'MMCXXXIII', 'MMCXXXIV', 'MMCXXXV', 'MMCXXXVI', 'MMCXXXVII', 'MMCXXXVIII', 'MMCXXXIX', 'MMCXL', 'MMCXLI', 'MMCXLII', 'MMCXLIII', 'MMCXLIV', 'MMCVL', 'MMCVLI', 'MMCVLII', 'MMCVLIII', 'MMCIL', 'MMCL', 'MMCLI', 'MMCLII', 'MMCLIII', 'MMCLIV', 'MMCLV', 'MMCLVI', 'MMCLVII', 'MMCLVIII', 'MMCLIX', 'MMCLX', 'MMCLXI', 'MMCLXII', 'MMCLXIII', 'MMCLXIV', 'MMCLXV', 'MMCLXVI', 'MMCLXVII', 'MMCLXVIII', 'MMCLXIX', 'MMCLXX', 'MMCLXXI', 'MMCLXXII', 'MMCLXXIII', 'MMCLXXIV', 'MMCLXXV', 'MMCLXXVI', 'MMCLXXVII', 'MMCLXXVIII', 'MMCLXXIX', 'MMCLXXX', 'MMCLXXXI', 'MMCLXXXII', 'MMCLXXXIII', 'MMCLXXXIV', 'MMCLXXXV', 'MMCLXXXVI', 'MMCLXXXVII', 'MMCLXXXVIII', 'MMCLXXXIX', 'MMCXC', 'MMCXCI', 'MMCXCII', 'MMCXCIII', 'MMCXCIV', 'MMCVC', 'MMCVCI', 'MMCVCII', 'MMCVCIII', 'MMCIC', 'MMCC', 'MMCCI', 'MMCCII', 'MMCCIII', 'MMCCIV', 'MMCCV', 'MMCCVI', 'MMCCVII', 'MMCCVIII', 'MMCCIX', 'MMCCX', 'MMCCXI', 'MMCCXII', 'MMCCXIII', 'MMCCXIV', 'MMCCXV', 'MMCCXVI', 'MMCCXVII', 'MMCCXVIII', 'MMCCXIX', 'MMCCXX', 'MMCCXXI', 'MMCCXXII', 'MMCCXXIII', 'MMCCXXIV', 'MMCCXXV', 'MMCCXXVI', 'MMCCXXVII', 'MMCCXXVIII', 'MMCCXXIX', 'MMCCXXX', 'MMCCXXXI', 'MMCCXXXII', 'MMCCXXXIII', 'MMCCXXXIV', 'MMCCXXXV', 'MMCCXXXVI', 'MMCCXXXVII', 'MMCCXXXVIII', 'MMCCXXXIX', 'MMCCXL', 'MMCCXLI', 'MMCCXLII', 'MMCCXLIII', 'MMCCXLIV', 'MMCCVL', 'MMCCVLI', 'MMCCVLII', 'MMCCVLIII', 'MMCCIL', 'MMCCL', 'MMCCLI', 'MMCCLII', 'MMCCLIII', 'MMCCLIV', 'MMCCLV', 'MMCCLVI', 'MMCCLVII', 'MMCCLVIII', 'MMCCLIX', 'MMCCLX', 'MMCCLXI', 'MMCCLXII', 'MMCCLXIII', 'MMCCLXIV', 'MMCCLXV', 'MMCCLXVI', 'MMCCLXVII', 'MMCCLXVIII', 'MMCCLXIX', 'MMCCLXX', 'MMCCLXXI', 'MMCCLXXII', 'MMCCLXXIII', 'MMCCLXXIV', 'MMCCLXXV', 'MMCCLXXVI', 'MMCCLXXVII', 'MMCCLXXVIII', 'MMCCLXXIX', 'MMCCLXXX', 'MMCCLXXXI', 'MMCCLXXXII', 'MMCCLXXXIII', 'MMCCLXXXIV', 'MMCCLXXXV', 'MMCCLXXXVI', 'MMCCLXXXVII', 'MMCCLXXXVIII', 'MMCCLXXXIX', 'MMCCXC', 'MMCCXCI', 'MMCCXCII', 'MMCCXCIII', 'MMCCXCIV', 'MMCCVC', 'MMCCVCI', 'MMCCVCII', 'MMCCVCIII', 'MMCCIC', 'MMCCC', 'MMCCCI', 'MMCCCII', 'MMCCCIII', 'MMCCCIV', 'MMCCCV', 'MMCCCVI', 'MMCCCVII', 'MMCCCVIII', 'MMCCCIX', 'MMCCCX', 'MMCCCXI', 'MMCCCXII', 'MMCCCXIII', 'MMCCCXIV', 'MMCCCXV', 'MMCCCXVI', 'MMCCCXVII', 'MMCCCXVIII', 'MMCCCXIX', 'MMCCCXX', 'MMCCCXXI', 'MMCCCXXII', 'MMCCCXXIII', 'MMCCCXXIV', 'MMCCCXXV', 'MMCCCXXVI', 'MMCCCXXVII', 'MMCCCXXVIII', 'MMCCCXXIX', 'MMCCCXXX', 'MMCCCXXXI', 'MMCCCXXXII', 'MMCCCXXXIII', 'MMCCCXXXIV', 'MMCCCXXXV', 'MMCCCXXXVI', 'MMCCCXXXVII', 'MMCCCXXXVIII', 'MMCCCXXXIX', 'MMCCCXL', 'MMCCCXLI', 'MMCCCXLII', 'MMCCCXLIII', 'MMCCCXLIV', 'MMCCCVL', 'MMCCCVLI', 'MMCCCVLII', 'MMCCCVLIII', 'MMCCCIL', 'MMCCCL', 'MMCCCLI', 'MMCCCLII', 'MMCCCLIII', 'MMCCCLIV', 'MMCCCLV', 'MMCCCLVI', 'MMCCCLVII', 'MMCCCLVIII', 'MMCCCLIX', 'MMCCCLX', 'MMCCCLXI', 'MMCCCLXII', 'MMCCCLXIII', 'MMCCCLXIV', 'MMCCCLXV', 'MMCCCLXVI', 'MMCCCLXVII', 'MMCCCLXVIII', 'MMCCCLXIX', 'MMCCCLXX', 'MMCCCLXXI', 'MMCCCLXXII', 'MMCCCLXXIII', 'MMCCCLXXIV', 'MMCCCLXXV', 'MMCCCLXXVI', 'MMCCCLXXVII', 'MMCCCLXXVIII', 'MMCCCLXXIX', 'MMCCCLXXX', 'MMCCCLXXXI', 'MMCCCLXXXII', 'MMCCCLXXXIII', 'MMCCCLXXXIV', 'MMCCCLXXXV', 'MMCCCLXXXVI', 'MMCCCLXXXVII', 'MMCCCLXXXVIII', 'MMCCCLXXXIX', 'MMCCCXC', 'MMCCCXCI', 'MMCCCXCII', 'MMCCCXCIII', 'MMCCCXCIV', 'MMCCCVC', 'MMCCCVCI', 'MMCCCVCII', 'MMCCCVCIII', 'MMCCCIC', 'MMCD', 'MMCDI', 'MMCDII', 'MMCDIII', 'MMCDIV', 'MMCDV', 'MMCDVI', 'MMCDVII', 'MMCDVIII', 'MMCDIX', 'MMCDX', 'MMCDXI', 'MMCDXII', 'MMCDXIII', 'MMCDXIV', 'MMCDXV', 'MMCDXVI', 'MMCDXVII', 'MMCDXVIII', 'MMCDXIX', 'MMCDXX', 'MMCDXXI', 'MMCDXXII', 'MMCDXXIII', 'MMCDXXIV', 'MMCDXXV', 'MMCDXXVI', 'MMCDXXVII', 'MMCDXXVIII', 'MMCDXXIX', 'MMCDXXX', 'MMCDXXXI', 'MMCDXXXII', 'MMCDXXXIII', 'MMCDXXXIV', 'MMCDXXXV', 'MMCDXXXVI', 'MMCDXXXVII', 'MMCDXXXVIII', 'MMCDXXXIX', 'MMCDXL', 'MMCDXLI', 'MMCDXLII', 'MMCDXLIII', 'MMCDXLIV', 'MMCDVL', 'MMCDVLI', 'MMCDVLII', 'MMCDVLIII', 'MMCDIL', 'MMLD', 'MMLDI', 'MMLDII', 'MMLDIII', 'MMLDIV', 'MMLDV', 'MMLDVI', 'MMLDVII', 'MMLDVIII', 'MMLDIX', 'MMLDX', 'MMLDXI', 'MMLDXII', 'MMLDXIII', 'MMLDXIV', 'MMLDXV', 'MMLDXVI', 'MMLDXVII', 'MMLDXVIII', 'MMLDXIX', 'MMLDXX', 'MMLDXXI', 'MMLDXXII', 'MMLDXXIII', 'MMLDXXIV', 'MMLDXXV', 'MMLDXXVI', 'MMLDXXVII', 'MMLDXXVIII', 'MMLDXXIX', 'MMLDXXX', 'MMLDXXXI', 'MMLDXXXII', 'MMLDXXXIII', 'MMLDXXXIV', 'MMLDXXXV', 'MMLDXXXVI', 'MMLDXXXVII', 'MMLDXXXVIII', 'MMLDXXXIX', 'MMXD', 'MMXDI', 'MMXDII', 'MMXDIII', 'MMXDIV', 'MMXDV', 'MMXDVI', 'MMXDVII', 'MMXDVIII', 'MMXDIX', 'MMD', 'MMDI', 'MMDII', 'MMDIII', 'MMDIV', 'MMDV', 'MMDVI', 'MMDVII', 'MMDVIII', 'MMDIX', 'MMDX', 'MMDXI', 'MMDXII', 'MMDXIII', 'MMDXIV', 'MMDXV', 'MMDXVI', 'MMDXVII', 'MMDXVIII', 'MMDXIX', 'MMDXX', 'MMDXXI', 'MMDXXII', 'MMDXXIII', 'MMDXXIV', 'MMDXXV', 'MMDXXVI', 'MMDXXVII', 'MMDXXVIII', 'MMDXXIX', 'MMDXXX', 'MMDXXXI', 'MMDXXXII', 'MMDXXXIII', 'MMDXXXIV', 'MMDXXXV', 'MMDXXXVI', 'MMDXXXVII', 'MMDXXXVIII', 'MMDXXXIX', 'MMDXL', 'MMDXLI', 'MMDXLII', 'MMDXLIII', 'MMDXLIV', 'MMDVL', 'MMDVLI', 'MMDVLII', 'MMDVLIII', 'MMDIL', 'MMDL', 'MMDLI', 'MMDLII', 'MMDLIII', 'MMDLIV', 'MMDLV', 'MMDLVI', 'MMDLVII', 'MMDLVIII', 'MMDLIX', 'MMDLX', 'MMDLXI', 'MMDLXII', 'MMDLXIII', 'MMDLXIV', 'MMDLXV', 'MMDLXVI', 'MMDLXVII', 'MMDLXVIII', 'MMDLXIX', 'MMDLXX', 'MMDLXXI', 'MMDLXXII', 'MMDLXXIII', 'MMDLXXIV', 'MMDLXXV', 'MMDLXXVI', 'MMDLXXVII', 'MMDLXXVIII', 'MMDLXXIX', 'MMDLXXX', 'MMDLXXXI', 'MMDLXXXII', 'MMDLXXXIII', 'MMDLXXXIV', 'MMDLXXXV', 'MMDLXXXVI', 'MMDLXXXVII', 'MMDLXXXVIII', 'MMDLXXXIX', 'MMDXC', 'MMDXCI', 'MMDXCII', 'MMDXCIII', 'MMDXCIV', 'MMDVC', 'MMDVCI', 'MMDVCII', 'MMDVCIII', 'MMDIC', 'MMDC', 'MMDCI', 'MMDCII', 'MMDCIII', 'MMDCIV', 'MMDCV', 'MMDCVI', 'MMDCVII', 'MMDCVIII', 'MMDCIX', 'MMDCX', 'MMDCXI', 'MMDCXII', 'MMDCXIII', 'MMDCXIV', 'MMDCXV', 'MMDCXVI', 'MMDCXVII', 'MMDCXVIII', 'MMDCXIX', 'MMDCXX', 'MMDCXXI', 'MMDCXXII', 'MMDCXXIII', 'MMDCXXIV', 'MMDCXXV', 'MMDCXXVI', 'MMDCXXVII', 'MMDCXXVIII', 'MMDCXXIX', 'MMDCXXX', 'MMDCXXXI', 'MMDCXXXII', 'MMDCXXXIII', 'MMDCXXXIV', 'MMDCXXXV', 'MMDCXXXVI', 'MMDCXXXVII', 'MMDCXXXVIII', 'MMDCXXXIX', 'MMDCXL', 'MMDCXLI', 'MMDCXLII', 'MMDCXLIII', 'MMDCXLIV', 'MMDCVL', 'MMDCVLI', 'MMDCVLII', 'MMDCVLIII', 'MMDCIL', 'MMDCL', 'MMDCLI', 'MMDCLII', 'MMDCLIII', 'MMDCLIV', 'MMDCLV', 'MMDCLVI', 'MMDCLVII', 'MMDCLVIII', 'MMDCLIX', 'MMDCLX', 'MMDCLXI', 'MMDCLXII', 'MMDCLXIII', 'MMDCLXIV', 'MMDCLXV', 'MMDCLXVI', 'MMDCLXVII', 'MMDCLXVIII', 'MMDCLXIX', 'MMDCLXX', 'MMDCLXXI', 'MMDCLXXII', 'MMDCLXXIII', 'MMDCLXXIV', 'MMDCLXXV', 'MMDCLXXVI', 'MMDCLXXVII', 'MMDCLXXVIII', 'MMDCLXXIX', 'MMDCLXXX', 'MMDCLXXXI', 'MMDCLXXXII', 'MMDCLXXXIII', 'MMDCLXXXIV', 'MMDCLXXXV', 'MMDCLXXXVI', 'MMDCLXXXVII', 'MMDCLXXXVIII', 'MMDCLXXXIX', 'MMDCXC', 'MMDCXCI', 'MMDCXCII', 'MMDCXCIII', 'MMDCXCIV', 'MMDCVC', 'MMDCVCI', 'MMDCVCII', 'MMDCVCIII', 'MMDCIC', 'MMDCC', 'MMDCCI', 'MMDCCII', 'MMDCCIII', 'MMDCCIV', 'MMDCCV', 'MMDCCVI', 'MMDCCVII', 'MMDCCVIII', 'MMDCCIX', 'MMDCCX', 'MMDCCXI', 'MMDCCXII', 'MMDCCXIII', 'MMDCCXIV', 'MMDCCXV', 'MMDCCXVI', 'MMDCCXVII', 'MMDCCXVIII', 'MMDCCXIX', 'MMDCCXX', 'MMDCCXXI', 'MMDCCXXII', 'MMDCCXXIII', 'MMDCCXXIV', 'MMDCCXXV', 'MMDCCXXVI', 'MMDCCXXVII', 'MMDCCXXVIII', 'MMDCCXXIX', 'MMDCCXXX', 'MMDCCXXXI', 'MMDCCXXXII', 'MMDCCXXXIII', 'MMDCCXXXIV', 'MMDCCXXXV', 'MMDCCXXXVI', 'MMDCCXXXVII', 'MMDCCXXXVIII', 'MMDCCXXXIX', 'MMDCCXL', 'MMDCCXLI', 'MMDCCXLII', 'MMDCCXLIII', 'MMDCCXLIV', 'MMDCCVL', 'MMDCCVLI', 'MMDCCVLII', 'MMDCCVLIII', 'MMDCCIL', 'MMDCCL', 'MMDCCLI', 'MMDCCLII', 'MMDCCLIII', 'MMDCCLIV', 'MMDCCLV', 'MMDCCLVI', 'MMDCCLVII', 'MMDCCLVIII', 'MMDCCLIX', 'MMDCCLX', 'MMDCCLXI', 'MMDCCLXII', 'MMDCCLXIII', 'MMDCCLXIV', 'MMDCCLXV', 'MMDCCLXVI', 'MMDCCLXVII', 'MMDCCLXVIII', 'MMDCCLXIX', 'MMDCCLXX', 'MMDCCLXXI', 'MMDCCLXXII', 'MMDCCLXXIII', 'MMDCCLXXIV', 'MMDCCLXXV', 'MMDCCLXXVI', 'MMDCCLXXVII', 'MMDCCLXXVIII', 'MMDCCLXXIX', 'MMDCCLXXX', 'MMDCCLXXXI', 'MMDCCLXXXII', 'MMDCCLXXXIII', 'MMDCCLXXXIV', 'MMDCCLXXXV', 'MMDCCLXXXVI', 'MMDCCLXXXVII', 'MMDCCLXXXVIII', 'MMDCCLXXXIX', 'MMDCCXC', 'MMDCCXCI', 'MMDCCXCII', 'MMDCCXCIII', 'MMDCCXCIV', 'MMDCCVC', 'MMDCCVCI', 'MMDCCVCII', 'MMDCCVCIII', 'MMDCCIC', 'MMDCCC', 'MMDCCCI', 'MMDCCCII', 'MMDCCCIII', 'MMDCCCIV', 'MMDCCCV', 'MMDCCCVI', 'MMDCCCVII', 'MMDCCCVIII', 'MMDCCCIX', 'MMDCCCX', 'MMDCCCXI', 'MMDCCCXII', 'MMDCCCXIII', 'MMDCCCXIV', 'MMDCCCXV', 'MMDCCCXVI', 'MMDCCCXVII', 'MMDCCCXVIII', 'MMDCCCXIX', 'MMDCCCXX', 'MMDCCCXXI', 'MMDCCCXXII', 'MMDCCCXXIII', 'MMDCCCXXIV', 'MMDCCCXXV', 'MMDCCCXXVI', 'MMDCCCXXVII', 'MMDCCCXXVIII', 'MMDCCCXXIX', 'MMDCCCXXX', 'MMDCCCXXXI', 'MMDCCCXXXII', 'MMDCCCXXXIII', 'MMDCCCXXXIV', 'MMDCCCXXXV', 'MMDCCCXXXVI', 'MMDCCCXXXVII', 'MMDCCCXXXVIII', 'MMDCCCXXXIX', 'MMDCCCXL', 'MMDCCCXLI', 'MMDCCCXLII', 'MMDCCCXLIII', 'MMDCCCXLIV', 'MMDCCCVL', 'MMDCCCVLI', 'MMDCCCVLII', 'MMDCCCVLIII', 'MMDCCCIL', 'MMDCCCL', 'MMDCCCLI', 'MMDCCCLII', 'MMDCCCLIII', 'MMDCCCLIV', 'MMDCCCLV', 'MMDCCCLVI', 'MMDCCCLVII', 'MMDCCCLVIII', 'MMDCCCLIX', 'MMDCCCLX', 'MMDCCCLXI', 'MMDCCCLXII', 'MMDCCCLXIII', 'MMDCCCLXIV', 'MMDCCCLXV', 'MMDCCCLXVI', 'MMDCCCLXVII', 'MMDCCCLXVIII', 'MMDCCCLXIX', 'MMDCCCLXX', 'MMDCCCLXXI', 'MMDCCCLXXII', 'MMDCCCLXXIII', 'MMDCCCLXXIV', 'MMDCCCLXXV', 'MMDCCCLXXVI', 'MMDCCCLXXVII', 'MMDCCCLXXVIII', 'MMDCCCLXXIX', 'MMDCCCLXXX', 'MMDCCCLXXXI', 'MMDCCCLXXXII', 'MMDCCCLXXXIII', 'MMDCCCLXXXIV', 'MMDCCCLXXXV', 'MMDCCCLXXXVI', 'MMDCCCLXXXVII', 'MMDCCCLXXXVIII', 'MMDCCCLXXXIX', 'MMDCCCXC', 'MMDCCCXCI', 'MMDCCCXCII', 'MMDCCCXCIII', 'MMDCCCXCIV', 'MMDCCCVC', 'MMDCCCVCI', 'MMDCCCVCII', 'MMDCCCVCIII', 'MMDCCCIC', 'MMCM', 'MMCMI', 'MMCMII', 'MMCMIII', 'MMCMIV', 'MMCMV', 'MMCMVI', 'MMCMVII', 'MMCMVIII', 'MMCMIX', 'MMCMX', 'MMCMXI', 'MMCMXII', 'MMCMXIII', 'MMCMXIV', 'MMCMXV', 'MMCMXVI', 'MMCMXVII', 'MMCMXVIII', 'MMCMXIX', 'MMCMXX', 'MMCMXXI', 'MMCMXXII', 'MMCMXXIII', 'MMCMXXIV', 'MMCMXXV', 'MMCMXXVI', 'MMCMXXVII', 'MMCMXXVIII', 'MMCMXXIX', 'MMCMXXX', 'MMCMXXXI', 'MMCMXXXII', 'MMCMXXXIII', 'MMCMXXXIV', 'MMCMXXXV', 'MMCMXXXVI', 'MMCMXXXVII', 'MMCMXXXVIII', 'MMCMXXXIX', 'MMCMXL', 'MMCMXLI', 'MMCMXLII', 'MMCMXLIII', 'MMCMXLIV', 'MMCMVL', 'MMCMVLI', 'MMCMVLII', 'MMCMVLIII', 'MMCMIL', 'MMLM', 'MMLMI', 'MMLMII', 'MMLMIII', 'MMLMIV', 'MMLMV', 'MMLMVI', 'MMLMVII', 'MMLMVIII', 'MMLMIX', 'MMLMX', 'MMLMXI', 'MMLMXII', 'MMLMXIII', 'MMLMXIV', 'MMLMXV', 'MMLMXVI', 'MMLMXVII', 'MMLMXVIII', 'MMLMXIX', 'MMLMXX', 'MMLMXXI', 'MMLMXXII', 'MMLMXXIII', 'MMLMXXIV', 'MMLMXXV', 'MMLMXXVI', 'MMLMXXVII', 'MMLMXXVIII', 'MMLMXXIX', 'MMLMXXX', 'MMLMXXXI', 'MMLMXXXII', 'MMLMXXXIII', 'MMLMXXXIV', 'MMLMXXXV', 'MMLMXXXVI', 'MMLMXXXVII', 'MMLMXXXVIII', 'MMLMXXXIX', 'MMXM', 'MMXMI', 'MMXMII', 'MMXMIII', 'MMXMIV', 'MMXMV', 'MMXMVI', 'MMXMVII', 'MMXMVIII', 'MMXMIX', 'MMM', 'MMMI', 'MMMII', 'MMMIII', 'MMMIV', 'MMMV', 'MMMVI', 'MMMVII', 'MMMVIII', 'MMMIX', 'MMMX', 'MMMXI', 'MMMXII', 'MMMXIII', 'MMMXIV', 'MMMXV', 'MMMXVI', 'MMMXVII', 'MMMXVIII', 'MMMXIX', 'MMMXX', 'MMMXXI', 'MMMXXII', 'MMMXXIII', 'MMMXXIV', 'MMMXXV', 'MMMXXVI', 'MMMXXVII', 'MMMXXVIII', 'MMMXXIX', 'MMMXXX', 'MMMXXXI', 'MMMXXXII', 'MMMXXXIII', 'MMMXXXIV', 'MMMXXXV', 'MMMXXXVI', 'MMMXXXVII', 'MMMXXXVIII', 'MMMXXXIX', 'MMMXL', 'MMMXLI', 'MMMXLII', 'MMMXLIII', 'MMMXLIV', 'MMMVL', 'MMMVLI', 'MMMVLII', 'MMMVLIII', 'MMMIL', 'MMML', 'MMMLI', 'MMMLII', 'MMMLIII', 'MMMLIV', 'MMMLV', 'MMMLVI', 'MMMLVII', 'MMMLVIII', 'MMMLIX', 'MMMLX', 'MMMLXI', 'MMMLXII', 'MMMLXIII', 'MMMLXIV', 'MMMLXV', 'MMMLXVI', 'MMMLXVII', 'MMMLXVIII', 'MMMLXIX', 'MMMLXX', 'MMMLXXI', 'MMMLXXII', 'MMMLXXIII', 'MMMLXXIV', 'MMMLXXV', 'MMMLXXVI', 'MMMLXXVII', 'MMMLXXVIII', 'MMMLXXIX', 'MMMLXXX', 'MMMLXXXI', 'MMMLXXXII', 'MMMLXXXIII', 'MMMLXXXIV', 'MMMLXXXV', 'MMMLXXXVI', 'MMMLXXXVII', 'MMMLXXXVIII', 'MMMLXXXIX', 'MMMXC', 'MMMXCI', 'MMMXCII', 'MMMXCIII', 'MMMXCIV', 'MMMVC', 'MMMVCI', 'MMMVCII', 'MMMVCIII', 'MMMIC', 'MMMC', 'MMMCI', 'MMMCII', 'MMMCIII', 'MMMCIV', 'MMMCV', 'MMMCVI', 'MMMCVII', 'MMMCVIII', 'MMMCIX', 'MMMCX', 'MMMCXI', 'MMMCXII', 'MMMCXIII', 'MMMCXIV', 'MMMCXV', 'MMMCXVI', 'MMMCXVII', 'MMMCXVIII', 'MMMCXIX', 'MMMCXX', 'MMMCXXI', 'MMMCXXII', 'MMMCXXIII', 'MMMCXXIV', 'MMMCXXV', 'MMMCXXVI', 'MMMCXXVII', 'MMMCXXVIII', 'MMMCXXIX', 'MMMCXXX', 'MMMCXXXI', 'MMMCXXXII', 'MMMCXXXIII', 'MMMCXXXIV', 'MMMCXXXV', 'MMMCXXXVI', 'MMMCXXXVII', 'MMMCXXXVIII', 'MMMCXXXIX', 'MMMCXL', 'MMMCXLI', 'MMMCXLII', 'MMMCXLIII', 'MMMCXLIV', 'MMMCVL', 'MMMCVLI', 'MMMCVLII', 'MMMCVLIII', 'MMMCIL', 'MMMCL', 'MMMCLI', 'MMMCLII', 'MMMCLIII', 'MMMCLIV', 'MMMCLV', 'MMMCLVI', 'MMMCLVII', 'MMMCLVIII', 'MMMCLIX', 'MMMCLX', 'MMMCLXI', 'MMMCLXII', 'MMMCLXIII', 'MMMCLXIV', 'MMMCLXV', 'MMMCLXVI', 'MMMCLXVII', 'MMMCLXVIII', 'MMMCLXIX', 'MMMCLXX', 'MMMCLXXI', 'MMMCLXXII', 'MMMCLXXIII', 'MMMCLXXIV', 'MMMCLXXV', 'MMMCLXXVI', 'MMMCLXXVII', 'MMMCLXXVIII', 'MMMCLXXIX', 'MMMCLXXX', 'MMMCLXXXI', 'MMMCLXXXII', 'MMMCLXXXIII', 'MMMCLXXXIV', 'MMMCLXXXV', 'MMMCLXXXVI', 'MMMCLXXXVII', 'MMMCLXXXVIII', 'MMMCLXXXIX', 'MMMCXC', 'MMMCXCI', 'MMMCXCII', 'MMMCXCIII', 'MMMCXCIV', 'MMMCVC', 'MMMCVCI', 'MMMCVCII', 'MMMCVCIII', 'MMMCIC', 'MMMCC', 'MMMCCI', 'MMMCCII', 'MMMCCIII', 'MMMCCIV', 'MMMCCV', 'MMMCCVI', 'MMMCCVII', 'MMMCCVIII', 'MMMCCIX', 'MMMCCX', 'MMMCCXI', 'MMMCCXII', 'MMMCCXIII', 'MMMCCXIV', 'MMMCCXV', 'MMMCCXVI', 'MMMCCXVII', 'MMMCCXVIII', 'MMMCCXIX', 'MMMCCXX', 'MMMCCXXI', 'MMMCCXXII', 'MMMCCXXIII', 'MMMCCXXIV', 'MMMCCXXV', 'MMMCCXXVI', 'MMMCCXXVII', 'MMMCCXXVIII', 'MMMCCXXIX', 'MMMCCXXX', 'MMMCCXXXI', 'MMMCCXXXII', 'MMMCCXXXIII', 'MMMCCXXXIV', 'MMMCCXXXV', 'MMMCCXXXVI', 'MMMCCXXXVII', 'MMMCCXXXVIII', 'MMMCCXXXIX', 'MMMCCXL', 'MMMCCXLI', 'MMMCCXLII', 'MMMCCXLIII', 'MMMCCXLIV', 'MMMCCVL', 'MMMCCVLI', 'MMMCCVLII', 'MMMCCVLIII', 'MMMCCIL', 'MMMCCL', 'MMMCCLI', 'MMMCCLII', 'MMMCCLIII', 'MMMCCLIV', 'MMMCCLV', 'MMMCCLVI', 'MMMCCLVII', 'MMMCCLVIII', 'MMMCCLIX', 'MMMCCLX', 'MMMCCLXI', 'MMMCCLXII', 'MMMCCLXIII', 'MMMCCLXIV', 'MMMCCLXV', 'MMMCCLXVI', 'MMMCCLXVII', 'MMMCCLXVIII', 'MMMCCLXIX', 'MMMCCLXX', 'MMMCCLXXI', 'MMMCCLXXII', 'MMMCCLXXIII', 'MMMCCLXXIV', 'MMMCCLXXV', 'MMMCCLXXVI', 'MMMCCLXXVII', 'MMMCCLXXVIII', 'MMMCCLXXIX', 'MMMCCLXXX', 'MMMCCLXXXI', 'MMMCCLXXXII', 'MMMCCLXXXIII', 'MMMCCLXXXIV', 'MMMCCLXXXV', 'MMMCCLXXXVI', 'MMMCCLXXXVII', 'MMMCCLXXXVIII', 'MMMCCLXXXIX', 'MMMCCXC', 'MMMCCXCI', 'MMMCCXCII', 'MMMCCXCIII', 'MMMCCXCIV', 'MMMCCVC', 'MMMCCVCI', 'MMMCCVCII', 'MMMCCVCIII', 'MMMCCIC', 'MMMCCC', 'MMMCCCI', 'MMMCCCII', 'MMMCCCIII', 'MMMCCCIV', 'MMMCCCV', 'MMMCCCVI', 'MMMCCCVII', 'MMMCCCVIII', 'MMMCCCIX', 'MMMCCCX', 'MMMCCCXI', 'MMMCCCXII', 'MMMCCCXIII', 'MMMCCCXIV', 'MMMCCCXV', 'MMMCCCXVI', 'MMMCCCXVII', 'MMMCCCXVIII', 'MMMCCCXIX', 'MMMCCCXX', 'MMMCCCXXI', 'MMMCCCXXII', 'MMMCCCXXIII', 'MMMCCCXXIV', 'MMMCCCXXV', 'MMMCCCXXVI', 'MMMCCCXXVII', 'MMMCCCXXVIII', 'MMMCCCXXIX', 'MMMCCCXXX', 'MMMCCCXXXI', 'MMMCCCXXXII', 'MMMCCCXXXIII', 'MMMCCCXXXIV', 'MMMCCCXXXV', 'MMMCCCXXXVI', 'MMMCCCXXXVII', 'MMMCCCXXXVIII', 'MMMCCCXXXIX', 'MMMCCCXL', 'MMMCCCXLI', 'MMMCCCXLII', 'MMMCCCXLIII', 'MMMCCCXLIV', 'MMMCCCVL', 'MMMCCCVLI', 'MMMCCCVLII', 'MMMCCCVLIII', 'MMMCCCIL', 'MMMCCCL', 'MMMCCCLI', 'MMMCCCLII', 'MMMCCCLIII', 'MMMCCCLIV', 'MMMCCCLV', 'MMMCCCLVI', 'MMMCCCLVII', 'MMMCCCLVIII', 'MMMCCCLIX', 'MMMCCCLX', 'MMMCCCLXI', 'MMMCCCLXII', 'MMMCCCLXIII', 'MMMCCCLXIV', 'MMMCCCLXV', 'MMMCCCLXVI', 'MMMCCCLXVII', 'MMMCCCLXVIII', 'MMMCCCLXIX', 'MMMCCCLXX', 'MMMCCCLXXI', 'MMMCCCLXXII', 'MMMCCCLXXIII', 'MMMCCCLXXIV', 'MMMCCCLXXV', 'MMMCCCLXXVI', 'MMMCCCLXXVII', 'MMMCCCLXXVIII', 'MMMCCCLXXIX', 'MMMCCCLXXX', 'MMMCCCLXXXI', 'MMMCCCLXXXII', 'MMMCCCLXXXIII', 'MMMCCCLXXXIV', 'MMMCCCLXXXV', 'MMMCCCLXXXVI', 'MMMCCCLXXXVII', 'MMMCCCLXXXVIII', 'MMMCCCLXXXIX', 'MMMCCCXC', 'MMMCCCXCI', 'MMMCCCXCII', 'MMMCCCXCIII', 'MMMCCCXCIV', 'MMMCCCVC', 'MMMCCCVCI', 'MMMCCCVCII', 'MMMCCCVCIII', 'MMMCCCIC', 'MMMCD', 'MMMCDI', 'MMMCDII', 'MMMCDIII', 'MMMCDIV', 'MMMCDV', 'MMMCDVI', 'MMMCDVII', 'MMMCDVIII', 'MMMCDIX', 'MMMCDX', 'MMMCDXI', 'MMMCDXII', 'MMMCDXIII', 'MMMCDXIV', 'MMMCDXV', 'MMMCDXVI', 'MMMCDXVII', 'MMMCDXVIII', 'MMMCDXIX', 'MMMCDXX', 'MMMCDXXI', 'MMMCDXXII', 'MMMCDXXIII', 'MMMCDXXIV', 'MMMCDXXV', 'MMMCDXXVI', 'MMMCDXXVII', 'MMMCDXXVIII', 'MMMCDXXIX', 'MMMCDXXX', 'MMMCDXXXI', 'MMMCDXXXII', 'MMMCDXXXIII', 'MMMCDXXXIV', 'MMMCDXXXV', 'MMMCDXXXVI', 'MMMCDXXXVII', 'MMMCDXXXVIII', 'MMMCDXXXIX', 'MMMCDXL', 'MMMCDXLI', 'MMMCDXLII', 'MMMCDXLIII', 'MMMCDXLIV', 'MMMCDVL', 'MMMCDVLI', 'MMMCDVLII', 'MMMCDVLIII', 'MMMCDIL', 'MMMLD', 'MMMLDI', 'MMMLDII', 'MMMLDIII', 'MMMLDIV', 'MMMLDV', 'MMMLDVI', 'MMMLDVII', 'MMMLDVIII', 'MMMLDIX', 'MMMLDX', 'MMMLDXI', 'MMMLDXII', 'MMMLDXIII', 'MMMLDXIV', 'MMMLDXV', 'MMMLDXVI', 'MMMLDXVII', 'MMMLDXVIII', 'MMMLDXIX', 'MMMLDXX', 'MMMLDXXI', 'MMMLDXXII', 'MMMLDXXIII', 'MMMLDXXIV', 'MMMLDXXV', 'MMMLDXXVI', 'MMMLDXXVII', 'MMMLDXXVIII', 'MMMLDXXIX', 'MMMLDXXX', 'MMMLDXXXI', 'MMMLDXXXII', 'MMMLDXXXIII', 'MMMLDXXXIV', 'MMMLDXXXV', 'MMMLDXXXVI', 'MMMLDXXXVII', 'MMMLDXXXVIII', 'MMMLDXXXIX', 'MMMXD', 'MMMXDI', 'MMMXDII', 'MMMXDIII', 'MMMXDIV', 'MMMXDV', 'MMMXDVI', 'MMMXDVII', 'MMMXDVIII', 'MMMXDIX', 'MMMD', 'MMMDI', 'MMMDII', 'MMMDIII', 'MMMDIV', 'MMMDV', 'MMMDVI', 'MMMDVII', 'MMMDVIII', 'MMMDIX', 'MMMDX', 'MMMDXI', 'MMMDXII', 'MMMDXIII', 'MMMDXIV', 'MMMDXV', 'MMMDXVI', 'MMMDXVII', 'MMMDXVIII', 'MMMDXIX', 'MMMDXX', 'MMMDXXI', 'MMMDXXII', 'MMMDXXIII', 'MMMDXXIV', 'MMMDXXV', 'MMMDXXVI', 'MMMDXXVII', 'MMMDXXVIII', 'MMMDXXIX', 'MMMDXXX', 'MMMDXXXI', 'MMMDXXXII', 'MMMDXXXIII', 'MMMDXXXIV', 'MMMDXXXV', 'MMMDXXXVI', 'MMMDXXXVII', 'MMMDXXXVIII', 'MMMDXXXIX', 'MMMDXL', 'MMMDXLI', 'MMMDXLII', 'MMMDXLIII', 'MMMDXLIV', 'MMMDVL', 'MMMDVLI', 'MMMDVLII', 'MMMDVLIII', 'MMMDIL', 'MMMDL', 'MMMDLI', 'MMMDLII', 'MMMDLIII', 'MMMDLIV', 'MMMDLV', 'MMMDLVI', 'MMMDLVII', 'MMMDLVIII', 'MMMDLIX', 'MMMDLX', 'MMMDLXI', 'MMMDLXII', 'MMMDLXIII', 'MMMDLXIV', 'MMMDLXV', 'MMMDLXVI', 'MMMDLXVII', 'MMMDLXVIII', 'MMMDLXIX', 'MMMDLXX', 'MMMDLXXI', 'MMMDLXXII', 'MMMDLXXIII', 'MMMDLXXIV', 'MMMDLXXV', 'MMMDLXXVI', 'MMMDLXXVII', 'MMMDLXXVIII', 'MMMDLXXIX', 'MMMDLXXX', 'MMMDLXXXI', 'MMMDLXXXII', 'MMMDLXXXIII', 'MMMDLXXXIV', 'MMMDLXXXV', 'MMMDLXXXVI', 'MMMDLXXXVII', 'MMMDLXXXVIII', 'MMMDLXXXIX', 'MMMDXC', 'MMMDXCI', 'MMMDXCII', 'MMMDXCIII', 'MMMDXCIV', 'MMMDVC', 'MMMDVCI', 'MMMDVCII', 'MMMDVCIII', 'MMMDIC', 'MMMDC', 'MMMDCI', 'MMMDCII', 'MMMDCIII', 'MMMDCIV', 'MMMDCV', 'MMMDCVI', 'MMMDCVII', 'MMMDCVIII', 'MMMDCIX', 'MMMDCX', 'MMMDCXI', 'MMMDCXII', 'MMMDCXIII', 'MMMDCXIV', 'MMMDCXV', 'MMMDCXVI', 'MMMDCXVII', 'MMMDCXVIII', 'MMMDCXIX', 'MMMDCXX', 'MMMDCXXI', 'MMMDCXXII', 'MMMDCXXIII', 'MMMDCXXIV', 'MMMDCXXV', 'MMMDCXXVI', 'MMMDCXXVII', 'MMMDCXXVIII', 'MMMDCXXIX', 'MMMDCXXX', 'MMMDCXXXI', 'MMMDCXXXII', 'MMMDCXXXIII', 'MMMDCXXXIV', 'MMMDCXXXV', 'MMMDCXXXVI', 'MMMDCXXXVII', 'MMMDCXXXVIII', 'MMMDCXXXIX', 'MMMDCXL', 'MMMDCXLI', 'MMMDCXLII', 'MMMDCXLIII', 'MMMDCXLIV', 'MMMDCVL', 'MMMDCVLI', 'MMMDCVLII', 'MMMDCVLIII', 'MMMDCIL', 'MMMDCL', 'MMMDCLI', 'MMMDCLII', 'MMMDCLIII', 'MMMDCLIV', 'MMMDCLV', 'MMMDCLVI', 'MMMDCLVII', 'MMMDCLVIII', 'MMMDCLIX', 'MMMDCLX', 'MMMDCLXI', 'MMMDCLXII', 'MMMDCLXIII', 'MMMDCLXIV', 'MMMDCLXV', 'MMMDCLXVI', 'MMMDCLXVII', 'MMMDCLXVIII', 'MMMDCLXIX', 'MMMDCLXX', 'MMMDCLXXI', 'MMMDCLXXII', 'MMMDCLXXIII', 'MMMDCLXXIV', 'MMMDCLXXV', 'MMMDCLXXVI', 'MMMDCLXXVII', 'MMMDCLXXVIII', 'MMMDCLXXIX', 'MMMDCLXXX', 'MMMDCLXXXI', 'MMMDCLXXXII', 'MMMDCLXXXIII', 'MMMDCLXXXIV', 'MMMDCLXXXV', 'MMMDCLXXXVI', 'MMMDCLXXXVII', 'MMMDCLXXXVIII', 'MMMDCLXXXIX', 'MMMDCXC', 'MMMDCXCI', 'MMMDCXCII', 'MMMDCXCIII', 'MMMDCXCIV', 'MMMDCVC', 'MMMDCVCI', 'MMMDCVCII', 'MMMDCVCIII', 'MMMDCIC', 'MMMDCC', 'MMMDCCI', 'MMMDCCII', 'MMMDCCIII', 'MMMDCCIV', 'MMMDCCV', 'MMMDCCVI', 'MMMDCCVII', 'MMMDCCVIII', 'MMMDCCIX', 'MMMDCCX', 'MMMDCCXI', 'MMMDCCXII', 'MMMDCCXIII', 'MMMDCCXIV', 'MMMDCCXV', 'MMMDCCXVI', 'MMMDCCXVII', 'MMMDCCXVIII', 'MMMDCCXIX', 'MMMDCCXX', 'MMMDCCXXI', 'MMMDCCXXII', 'MMMDCCXXIII', 'MMMDCCXXIV', 'MMMDCCXXV', 'MMMDCCXXVI', 'MMMDCCXXVII', 'MMMDCCXXVIII', 'MMMDCCXXIX', 'MMMDCCXXX', 'MMMDCCXXXI', 'MMMDCCXXXII', 'MMMDCCXXXIII', 'MMMDCCXXXIV', 'MMMDCCXXXV', 'MMMDCCXXXVI', 'MMMDCCXXXVII', 'MMMDCCXXXVIII', 'MMMDCCXXXIX', 'MMMDCCXL', 'MMMDCCXLI', 'MMMDCCXLII', 'MMMDCCXLIII', 'MMMDCCXLIV', 'MMMDCCVL', 'MMMDCCVLI', 'MMMDCCVLII', 'MMMDCCVLIII', 'MMMDCCIL', 'MMMDCCL', 'MMMDCCLI', 'MMMDCCLII', 'MMMDCCLIII', 'MMMDCCLIV', 'MMMDCCLV', 'MMMDCCLVI', 'MMMDCCLVII', 'MMMDCCLVIII', 'MMMDCCLIX', 'MMMDCCLX', 'MMMDCCLXI', 'MMMDCCLXII', 'MMMDCCLXIII', 'MMMDCCLXIV', 'MMMDCCLXV', 'MMMDCCLXVI', 'MMMDCCLXVII', 'MMMDCCLXVIII', 'MMMDCCLXIX', 'MMMDCCLXX', 'MMMDCCLXXI', 'MMMDCCLXXII', 'MMMDCCLXXIII', 'MMMDCCLXXIV', 'MMMDCCLXXV', 'MMMDCCLXXVI', 'MMMDCCLXXVII', 'MMMDCCLXXVIII', 'MMMDCCLXXIX', 'MMMDCCLXXX', 'MMMDCCLXXXI', 'MMMDCCLXXXII', 'MMMDCCLXXXIII', 'MMMDCCLXXXIV', 'MMMDCCLXXXV', 'MMMDCCLXXXVI', 'MMMDCCLXXXVII', 'MMMDCCLXXXVIII', 'MMMDCCLXXXIX', 'MMMDCCXC', 'MMMDCCXCI', 'MMMDCCXCII', 'MMMDCCXCIII', 'MMMDCCXCIV', 'MMMDCCVC', 'MMMDCCVCI', 'MMMDCCVCII', 'MMMDCCVCIII', 'MMMDCCIC', 'MMMDCCC', 'MMMDCCCI', 'MMMDCCCII', 'MMMDCCCIII', 'MMMDCCCIV', 'MMMDCCCV', 'MMMDCCCVI', 'MMMDCCCVII', 'MMMDCCCVIII', 'MMMDCCCIX', 'MMMDCCCX', 'MMMDCCCXI', 'MMMDCCCXII', 'MMMDCCCXIII', 'MMMDCCCXIV', 'MMMDCCCXV', 'MMMDCCCXVI', 'MMMDCCCXVII', 'MMMDCCCXVIII', 'MMMDCCCXIX', 'MMMDCCCXX', 'MMMDCCCXXI', 'MMMDCCCXXII', 'MMMDCCCXXIII', 'MMMDCCCXXIV', 'MMMDCCCXXV', 'MMMDCCCXXVI', 'MMMDCCCXXVII', 'MMMDCCCXXVIII', 'MMMDCCCXXIX', 'MMMDCCCXXX', 'MMMDCCCXXXI', 'MMMDCCCXXXII', 'MMMDCCCXXXIII', 'MMMDCCCXXXIV', 'MMMDCCCXXXV', 'MMMDCCCXXXVI', 'MMMDCCCXXXVII', 'MMMDCCCXXXVIII', 'MMMDCCCXXXIX', 'MMMDCCCXL', 'MMMDCCCXLI', 'MMMDCCCXLII', 'MMMDCCCXLIII', 'MMMDCCCXLIV', 'MMMDCCCVL', 'MMMDCCCVLI', 'MMMDCCCVLII', 'MMMDCCCVLIII', 'MMMDCCCIL', 'MMMDCCCL', 'MMMDCCCLI', 'MMMDCCCLII', 'MMMDCCCLIII', 'MMMDCCCLIV', 'MMMDCCCLV', 'MMMDCCCLVI', 'MMMDCCCLVII', 'MMMDCCCLVIII', 'MMMDCCCLIX', 'MMMDCCCLX', 'MMMDCCCLXI', 'MMMDCCCLXII', 'MMMDCCCLXIII', 'MMMDCCCLXIV', 'MMMDCCCLXV', 'MMMDCCCLXVI', 'MMMDCCCLXVII', 'MMMDCCCLXVIII', 'MMMDCCCLXIX', 'MMMDCCCLXX', 'MMMDCCCLXXI', 'MMMDCCCLXXII', 'MMMDCCCLXXIII', 'MMMDCCCLXXIV', 'MMMDCCCLXXV', 'MMMDCCCLXXVI', 'MMMDCCCLXXVII', 'MMMDCCCLXXVIII', 'MMMDCCCLXXIX', 'MMMDCCCLXXX', 'MMMDCCCLXXXI', 'MMMDCCCLXXXII', 'MMMDCCCLXXXIII', 'MMMDCCCLXXXIV', 'MMMDCCCLXXXV', 'MMMDCCCLXXXVI', 'MMMDCCCLXXXVII', 'MMMDCCCLXXXVIII', 'MMMDCCCLXXXIX', 'MMMDCCCXC', 'MMMDCCCXCI', 'MMMDCCCXCII', 'MMMDCCCXCIII', 'MMMDCCCXCIV', 'MMMDCCCVC', 'MMMDCCCVCI', 'MMMDCCCVCII', 'MMMDCCCVCIII', 'MMMDCCCIC', 'MMMCM', 'MMMCMI', 'MMMCMII', 'MMMCMIII', 'MMMCMIV', 'MMMCMV', 'MMMCMVI', 'MMMCMVII', 'MMMCMVIII', 'MMMCMIX', 'MMMCMX', 'MMMCMXI', 'MMMCMXII', 'MMMCMXIII', 'MMMCMXIV', 'MMMCMXV', 'MMMCMXVI', 'MMMCMXVII', 'MMMCMXVIII', 'MMMCMXIX', 'MMMCMXX', 'MMMCMXXI', 'MMMCMXXII', 'MMMCMXXIII', 'MMMCMXXIV', 'MMMCMXXV', 'MMMCMXXVI', 'MMMCMXXVII', 'MMMCMXXVIII', 'MMMCMXXIX', 'MMMCMXXX', 'MMMCMXXXI', 'MMMCMXXXII', 'MMMCMXXXIII', 'MMMCMXXXIV', 'MMMCMXXXV', 'MMMCMXXXVI', 'MMMCMXXXVII', 'MMMCMXXXVIII', 'MMMCMXXXIX', 'MMMCMXL', 'MMMCMXLI', 'MMMCMXLII', 'MMMCMXLIII', 'MMMCMXLIV', 'MMMCMVL', 'MMMCMVLI', 'MMMCMVLII', 'MMMCMVLIII', 'MMMCMIL', 'MMMLM', 'MMMLMI', 'MMMLMII', 'MMMLMIII', 'MMMLMIV', 'MMMLMV', 'MMMLMVI', 'MMMLMVII', 'MMMLMVIII', 'MMMLMIX', 'MMMLMX', 'MMMLMXI', 'MMMLMXII', 'MMMLMXIII', 'MMMLMXIV', 'MMMLMXV', 'MMMLMXVI', 'MMMLMXVII', 'MMMLMXVIII', 'MMMLMXIX', 'MMMLMXX', 'MMMLMXXI', 'MMMLMXXII', 'MMMLMXXIII', 'MMMLMXXIV', 'MMMLMXXV', 'MMMLMXXVI', 'MMMLMXXVII', 'MMMLMXXVIII', 'MMMLMXXIX', 'MMMLMXXX', 'MMMLMXXXI', 'MMMLMXXXII', 'MMMLMXXXIII', 'MMMLMXXXIV', 'MMMLMXXXV', 'MMMLMXXXVI', 'MMMLMXXXVII', 'MMMLMXXXVIII', 'MMMLMXXXIX', 'MMMXM', 'MMMXMI', 'MMMXMII', 'MMMXMIII', 'MMMXMIV', 'MMMXMV', 'MMMXMVI', 'MMMXMVII', 'MMMXMVIII', 'MMMXMIX', ] -const mode3 = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX', 'XXI', 'XXII', 'XXIII', 'XXIV', 'XXV', 'XXVI', 'XXVII', 'XXVIII', 'XXIX', 'XXX', 'XXXI', 'XXXII', 'XXXIII', 'XXXIV', 'XXXV', 'XXXVI', 'XXXVII', 'XXXVIII', 'XXXIX', 'XL', 'XLI', 'XLII', 'XLIII', 'XLIV', 'VL', 'VLI', 'VLII', 'VLIII', 'IL', 'L', 'LI', 'LII', 'LIII', 'LIV', 'LV', 'LVI', 'LVII', 'LVIII', 'LIX', 'LX', 'LXI', 'LXII', 'LXIII', 'LXIV', 'LXV', 'LXVI', 'LXVII', 'LXVIII', 'LXIX', 'LXX', 'LXXI', 'LXXII', 'LXXIII', 'LXXIV', 'LXXV', 'LXXVI', 'LXXVII', 'LXXVIII', 'LXXIX', 'LXXX', 'LXXXI', 'LXXXII', 'LXXXIII', 'LXXXIV', 'LXXXV', 'LXXXVI', 'LXXXVII', 'LXXXVIII', 'LXXXIX', 'XC', 'XCI', 'XCII', 'XCIII', 'XCIV', 'VC', 'VCI', 'VCII', 'VCIII', 'IC', 'C', 'CI', 'CII', 'CIII', 'CIV', 'CV', 'CVI', 'CVII', 'CVIII', 'CIX', 'CX', 'CXI', 'CXII', 'CXIII', 'CXIV', 'CXV', 'CXVI', 'CXVII', 'CXVIII', 'CXIX', 'CXX', 'CXXI', 'CXXII', 'CXXIII', 'CXXIV', 'CXXV', 'CXXVI', 'CXXVII', 'CXXVIII', 'CXXIX', 'CXXX', 'CXXXI', 'CXXXII', 'CXXXIII', 'CXXXIV', 'CXXXV', 'CXXXVI', 'CXXXVII', 'CXXXVIII', 'CXXXIX', 'CXL', 'CXLI', 'CXLII', 'CXLIII', 'CXLIV', 'CVL', 'CVLI', 'CVLII', 'CVLIII', 'CIL', 'CL', 'CLI', 'CLII', 'CLIII', 'CLIV', 'CLV', 'CLVI', 'CLVII', 'CLVIII', 'CLIX', 'CLX', 'CLXI', 'CLXII', 'CLXIII', 'CLXIV', 'CLXV', 'CLXVI', 'CLXVII', 'CLXVIII', 'CLXIX', 'CLXX', 'CLXXI', 'CLXXII', 'CLXXIII', 'CLXXIV', 'CLXXV', 'CLXXVI', 'CLXXVII', 'CLXXVIII', 'CLXXIX', 'CLXXX', 'CLXXXI', 'CLXXXII', 'CLXXXIII', 'CLXXXIV', 'CLXXXV', 'CLXXXVI', 'CLXXXVII', 'CLXXXVIII', 'CLXXXIX', 'CXC', 'CXCI', 'CXCII', 'CXCIII', 'CXCIV', 'CVC', 'CVCI', 'CVCII', 'CVCIII', 'CIC', 'CC', 'CCI', 'CCII', 'CCIII', 'CCIV', 'CCV', 'CCVI', 'CCVII', 'CCVIII', 'CCIX', 'CCX', 'CCXI', 'CCXII', 'CCXIII', 'CCXIV', 'CCXV', 'CCXVI', 'CCXVII', 'CCXVIII', 'CCXIX', 'CCXX', 'CCXXI', 'CCXXII', 'CCXXIII', 'CCXXIV', 'CCXXV', 'CCXXVI', 'CCXXVII', 'CCXXVIII', 'CCXXIX', 'CCXXX', 'CCXXXI', 'CCXXXII', 'CCXXXIII', 'CCXXXIV', 'CCXXXV', 'CCXXXVI', 'CCXXXVII', 'CCXXXVIII', 'CCXXXIX', 'CCXL', 'CCXLI', 'CCXLII', 'CCXLIII', 'CCXLIV', 'CCVL', 'CCVLI', 'CCVLII', 'CCVLIII', 'CCIL', 'CCL', 'CCLI', 'CCLII', 'CCLIII', 'CCLIV', 'CCLV', 'CCLVI', 'CCLVII', 'CCLVIII', 'CCLIX', 'CCLX', 'CCLXI', 'CCLXII', 'CCLXIII', 'CCLXIV', 'CCLXV', 'CCLXVI', 'CCLXVII', 'CCLXVIII', 'CCLXIX', 'CCLXX', 'CCLXXI', 'CCLXXII', 'CCLXXIII', 'CCLXXIV', 'CCLXXV', 'CCLXXVI', 'CCLXXVII', 'CCLXXVIII', 'CCLXXIX', 'CCLXXX', 'CCLXXXI', 'CCLXXXII', 'CCLXXXIII', 'CCLXXXIV', 'CCLXXXV', 'CCLXXXVI', 'CCLXXXVII', 'CCLXXXVIII', 'CCLXXXIX', 'CCXC', 'CCXCI', 'CCXCII', 'CCXCIII', 'CCXCIV', 'CCVC', 'CCVCI', 'CCVCII', 'CCVCIII', 'CCIC', 'CCC', 'CCCI', 'CCCII', 'CCCIII', 'CCCIV', 'CCCV', 'CCCVI', 'CCCVII', 'CCCVIII', 'CCCIX', 'CCCX', 'CCCXI', 'CCCXII', 'CCCXIII', 'CCCXIV', 'CCCXV', 'CCCXVI', 'CCCXVII', 'CCCXVIII', 'CCCXIX', 'CCCXX', 'CCCXXI', 'CCCXXII', 'CCCXXIII', 'CCCXXIV', 'CCCXXV', 'CCCXXVI', 'CCCXXVII', 'CCCXXVIII', 'CCCXXIX', 'CCCXXX', 'CCCXXXI', 'CCCXXXII', 'CCCXXXIII', 'CCCXXXIV', 'CCCXXXV', 'CCCXXXVI', 'CCCXXXVII', 'CCCXXXVIII', 'CCCXXXIX', 'CCCXL', 'CCCXLI', 'CCCXLII', 'CCCXLIII', 'CCCXLIV', 'CCCVL', 'CCCVLI', 'CCCVLII', 'CCCVLIII', 'CCCIL', 'CCCL', 'CCCLI', 'CCCLII', 'CCCLIII', 'CCCLIV', 'CCCLV', 'CCCLVI', 'CCCLVII', 'CCCLVIII', 'CCCLIX', 'CCCLX', 'CCCLXI', 'CCCLXII', 'CCCLXIII', 'CCCLXIV', 'CCCLXV', 'CCCLXVI', 'CCCLXVII', 'CCCLXVIII', 'CCCLXIX', 'CCCLXX', 'CCCLXXI', 'CCCLXXII', 'CCCLXXIII', 'CCCLXXIV', 'CCCLXXV', 'CCCLXXVI', 'CCCLXXVII', 'CCCLXXVIII', 'CCCLXXIX', 'CCCLXXX', 'CCCLXXXI', 'CCCLXXXII', 'CCCLXXXIII', 'CCCLXXXIV', 'CCCLXXXV', 'CCCLXXXVI', 'CCCLXXXVII', 'CCCLXXXVIII', 'CCCLXXXIX', 'CCCXC', 'CCCXCI', 'CCCXCII', 'CCCXCIII', 'CCCXCIV', 'CCCVC', 'CCCVCI', 'CCCVCII', 'CCCVCIII', 'CCCIC', 'CD', 'CDI', 'CDII', 'CDIII', 'CDIV', 'CDV', 'CDVI', 'CDVII', 'CDVIII', 'CDIX', 'CDX', 'CDXI', 'CDXII', 'CDXIII', 'CDXIV', 'CDXV', 'CDXVI', 'CDXVII', 'CDXVIII', 'CDXIX', 'CDXX', 'CDXXI', 'CDXXII', 'CDXXIII', 'CDXXIV', 'CDXXV', 'CDXXVI', 'CDXXVII', 'CDXXVIII', 'CDXXIX', 'CDXXX', 'CDXXXI', 'CDXXXII', 'CDXXXIII', 'CDXXXIV', 'CDXXXV', 'CDXXXVI', 'CDXXXVII', 'CDXXXVIII', 'CDXXXIX', 'CDXL', 'CDXLI', 'CDXLII', 'CDXLIII', 'CDXLIV', 'CDVL', 'CDVLI', 'CDVLII', 'CDVLIII', 'CDIL', 'LD', 'LDI', 'LDII', 'LDIII', 'LDIV', 'LDV', 'LDVI', 'LDVII', 'LDVIII', 'LDIX', 'LDX', 'LDXI', 'LDXII', 'LDXIII', 'LDXIV', 'LDXV', 'LDXVI', 'LDXVII', 'LDXVIII', 'LDXIX', 'LDXX', 'LDXXI', 'LDXXII', 'LDXXIII', 'LDXXIV', 'LDXXV', 'LDXXVI', 'LDXXVII', 'LDXXVIII', 'LDXXIX', 'LDXXX', 'LDXXXI', 'LDXXXII', 'LDXXXIII', 'LDXXXIV', 'LDXXXV', 'LDXXXVI', 'LDXXXVII', 'LDXXXVIII', 'LDXXXIX', 'XD', 'XDI', 'XDII', 'XDIII', 'XDIV', 'VD', 'VDI', 'VDII', 'VDIII', 'VDIV', 'D', 'DI', 'DII', 'DIII', 'DIV', 'DV', 'DVI', 'DVII', 'DVIII', 'DIX', 'DX', 'DXI', 'DXII', 'DXIII', 'DXIV', 'DXV', 'DXVI', 'DXVII', 'DXVIII', 'DXIX', 'DXX', 'DXXI', 'DXXII', 'DXXIII', 'DXXIV', 'DXXV', 'DXXVI', 'DXXVII', 'DXXVIII', 'DXXIX', 'DXXX', 'DXXXI', 'DXXXII', 'DXXXIII', 'DXXXIV', 'DXXXV', 'DXXXVI', 'DXXXVII', 'DXXXVIII', 'DXXXIX', 'DXL', 'DXLI', 'DXLII', 'DXLIII', 'DXLIV', 'DVL', 'DVLI', 'DVLII', 'DVLIII', 'DIL', 'DL', 'DLI', 'DLII', 'DLIII', 'DLIV', 'DLV', 'DLVI', 'DLVII', 'DLVIII', 'DLIX', 'DLX', 'DLXI', 'DLXII', 'DLXIII', 'DLXIV', 'DLXV', 'DLXVI', 'DLXVII', 'DLXVIII', 'DLXIX', 'DLXX', 'DLXXI', 'DLXXII', 'DLXXIII', 'DLXXIV', 'DLXXV', 'DLXXVI', 'DLXXVII', 'DLXXVIII', 'DLXXIX', 'DLXXX', 'DLXXXI', 'DLXXXII', 'DLXXXIII', 'DLXXXIV', 'DLXXXV', 'DLXXXVI', 'DLXXXVII', 'DLXXXVIII', 'DLXXXIX', 'DXC', 'DXCI', 'DXCII', 'DXCIII', 'DXCIV', 'DVC', 'DVCI', 'DVCII', 'DVCIII', 'DIC', 'DC', 'DCI', 'DCII', 'DCIII', 'DCIV', 'DCV', 'DCVI', 'DCVII', 'DCVIII', 'DCIX', 'DCX', 'DCXI', 'DCXII', 'DCXIII', 'DCXIV', 'DCXV', 'DCXVI', 'DCXVII', 'DCXVIII', 'DCXIX', 'DCXX', 'DCXXI', 'DCXXII', 'DCXXIII', 'DCXXIV', 'DCXXV', 'DCXXVI', 'DCXXVII', 'DCXXVIII', 'DCXXIX', 'DCXXX', 'DCXXXI', 'DCXXXII', 'DCXXXIII', 'DCXXXIV', 'DCXXXV', 'DCXXXVI', 'DCXXXVII', 'DCXXXVIII', 'DCXXXIX', 'DCXL', 'DCXLI', 'DCXLII', 'DCXLIII', 'DCXLIV', 'DCVL', 'DCVLI', 'DCVLII', 'DCVLIII', 'DCIL', 'DCL', 'DCLI', 'DCLII', 'DCLIII', 'DCLIV', 'DCLV', 'DCLVI', 'DCLVII', 'DCLVIII', 'DCLIX', 'DCLX', 'DCLXI', 'DCLXII', 'DCLXIII', 'DCLXIV', 'DCLXV', 'DCLXVI', 'DCLXVII', 'DCLXVIII', 'DCLXIX', 'DCLXX', 'DCLXXI', 'DCLXXII', 'DCLXXIII', 'DCLXXIV', 'DCLXXV', 'DCLXXVI', 'DCLXXVII', 'DCLXXVIII', 'DCLXXIX', 'DCLXXX', 'DCLXXXI', 'DCLXXXII', 'DCLXXXIII', 'DCLXXXIV', 'DCLXXXV', 'DCLXXXVI', 'DCLXXXVII', 'DCLXXXVIII', 'DCLXXXIX', 'DCXC', 'DCXCI', 'DCXCII', 'DCXCIII', 'DCXCIV', 'DCVC', 'DCVCI', 'DCVCII', 'DCVCIII', 'DCIC', 'DCC', 'DCCI', 'DCCII', 'DCCIII', 'DCCIV', 'DCCV', 'DCCVI', 'DCCVII', 'DCCVIII', 'DCCIX', 'DCCX', 'DCCXI', 'DCCXII', 'DCCXIII', 'DCCXIV', 'DCCXV', 'DCCXVI', 'DCCXVII', 'DCCXVIII', 'DCCXIX', 'DCCXX', 'DCCXXI', 'DCCXXII', 'DCCXXIII', 'DCCXXIV', 'DCCXXV', 'DCCXXVI', 'DCCXXVII', 'DCCXXVIII', 'DCCXXIX', 'DCCXXX', 'DCCXXXI', 'DCCXXXII', 'DCCXXXIII', 'DCCXXXIV', 'DCCXXXV', 'DCCXXXVI', 'DCCXXXVII', 'DCCXXXVIII', 'DCCXXXIX', 'DCCXL', 'DCCXLI', 'DCCXLII', 'DCCXLIII', 'DCCXLIV', 'DCCVL', 'DCCVLI', 'DCCVLII', 'DCCVLIII', 'DCCIL', 'DCCL', 'DCCLI', 'DCCLII', 'DCCLIII', 'DCCLIV', 'DCCLV', 'DCCLVI', 'DCCLVII', 'DCCLVIII', 'DCCLIX', 'DCCLX', 'DCCLXI', 'DCCLXII', 'DCCLXIII', 'DCCLXIV', 'DCCLXV', 'DCCLXVI', 'DCCLXVII', 'DCCLXVIII', 'DCCLXIX', 'DCCLXX', 'DCCLXXI', 'DCCLXXII', 'DCCLXXIII', 'DCCLXXIV', 'DCCLXXV', 'DCCLXXVI', 'DCCLXXVII', 'DCCLXXVIII', 'DCCLXXIX', 'DCCLXXX', 'DCCLXXXI', 'DCCLXXXII', 'DCCLXXXIII', 'DCCLXXXIV', 'DCCLXXXV', 'DCCLXXXVI', 'DCCLXXXVII', 'DCCLXXXVIII', 'DCCLXXXIX', 'DCCXC', 'DCCXCI', 'DCCXCII', 'DCCXCIII', 'DCCXCIV', 'DCCVC', 'DCCVCI', 'DCCVCII', 'DCCVCIII', 'DCCIC', 'DCCC', 'DCCCI', 'DCCCII', 'DCCCIII', 'DCCCIV', 'DCCCV', 'DCCCVI', 'DCCCVII', 'DCCCVIII', 'DCCCIX', 'DCCCX', 'DCCCXI', 'DCCCXII', 'DCCCXIII', 'DCCCXIV', 'DCCCXV', 'DCCCXVI', 'DCCCXVII', 'DCCCXVIII', 'DCCCXIX', 'DCCCXX', 'DCCCXXI', 'DCCCXXII', 'DCCCXXIII', 'DCCCXXIV', 'DCCCXXV', 'DCCCXXVI', 'DCCCXXVII', 'DCCCXXVIII', 'DCCCXXIX', 'DCCCXXX', 'DCCCXXXI', 'DCCCXXXII', 'DCCCXXXIII', 'DCCCXXXIV', 'DCCCXXXV', 'DCCCXXXVI', 'DCCCXXXVII', 'DCCCXXXVIII', 'DCCCXXXIX', 'DCCCXL', 'DCCCXLI', 'DCCCXLII', 'DCCCXLIII', 'DCCCXLIV', 'DCCCVL', 'DCCCVLI', 'DCCCVLII', 'DCCCVLIII', 'DCCCIL', 'DCCCL', 'DCCCLI', 'DCCCLII', 'DCCCLIII', 'DCCCLIV', 'DCCCLV', 'DCCCLVI', 'DCCCLVII', 'DCCCLVIII', 'DCCCLIX', 'DCCCLX', 'DCCCLXI', 'DCCCLXII', 'DCCCLXIII', 'DCCCLXIV', 'DCCCLXV', 'DCCCLXVI', 'DCCCLXVII', 'DCCCLXVIII', 'DCCCLXIX', 'DCCCLXX', 'DCCCLXXI', 'DCCCLXXII', 'DCCCLXXIII', 'DCCCLXXIV', 'DCCCLXXV', 'DCCCLXXVI', 'DCCCLXXVII', 'DCCCLXXVIII', 'DCCCLXXIX', 'DCCCLXXX', 'DCCCLXXXI', 'DCCCLXXXII', 'DCCCLXXXIII', 'DCCCLXXXIV', 'DCCCLXXXV', 'DCCCLXXXVI', 'DCCCLXXXVII', 'DCCCLXXXVIII', 'DCCCLXXXIX', 'DCCCXC', 'DCCCXCI', 'DCCCXCII', 'DCCCXCIII', 'DCCCXCIV', 'DCCCVC', 'DCCCVCI', 'DCCCVCII', 'DCCCVCIII', 'DCCCIC', 'CM', 'CMI', 'CMII', 'CMIII', 'CMIV', 'CMV', 'CMVI', 'CMVII', 'CMVIII', 'CMIX', 'CMX', 'CMXI', 'CMXII', 'CMXIII', 'CMXIV', 'CMXV', 'CMXVI', 'CMXVII', 'CMXVIII', 'CMXIX', 'CMXX', 'CMXXI', 'CMXXII', 'CMXXIII', 'CMXXIV', 'CMXXV', 'CMXXVI', 'CMXXVII', 'CMXXVIII', 'CMXXIX', 'CMXXX', 'CMXXXI', 'CMXXXII', 'CMXXXIII', 'CMXXXIV', 'CMXXXV', 'CMXXXVI', 'CMXXXVII', 'CMXXXVIII', 'CMXXXIX', 'CMXL', 'CMXLI', 'CMXLII', 'CMXLIII', 'CMXLIV', 'CMVL', 'CMVLI', 'CMVLII', 'CMVLIII', 'CMIL', 'LM', 'LMI', 'LMII', 'LMIII', 'LMIV', 'LMV', 'LMVI', 'LMVII', 'LMVIII', 'LMIX', 'LMX', 'LMXI', 'LMXII', 'LMXIII', 'LMXIV', 'LMXV', 'LMXVI', 'LMXVII', 'LMXVIII', 'LMXIX', 'LMXX', 'LMXXI', 'LMXXII', 'LMXXIII', 'LMXXIV', 'LMXXV', 'LMXXVI', 'LMXXVII', 'LMXXVIII', 'LMXXIX', 'LMXXX', 'LMXXXI', 'LMXXXII', 'LMXXXIII', 'LMXXXIV', 'LMXXXV', 'LMXXXVI', 'LMXXXVII', 'LMXXXVIII', 'LMXXXIX', 'XM', 'XMI', 'XMII', 'XMIII', 'XMIV', 'VM', 'VMI', 'VMII', 'VMIII', 'VMIV', 'M', 'MI', 'MII', 'MIII', 'MIV', 'MV', 'MVI', 'MVII', 'MVIII', 'MIX', 'MX', 'MXI', 'MXII', 'MXIII', 'MXIV', 'MXV', 'MXVI', 'MXVII', 'MXVIII', 'MXIX', 'MXX', 'MXXI', 'MXXII', 'MXXIII', 'MXXIV', 'MXXV', 'MXXVI', 'MXXVII', 'MXXVIII', 'MXXIX', 'MXXX', 'MXXXI', 'MXXXII', 'MXXXIII', 'MXXXIV', 'MXXXV', 'MXXXVI', 'MXXXVII', 'MXXXVIII', 'MXXXIX', 'MXL', 'MXLI', 'MXLII', 'MXLIII', 'MXLIV', 'MVL', 'MVLI', 'MVLII', 'MVLIII', 'MIL', 'ML', 'MLI', 'MLII', 'MLIII', 'MLIV', 'MLV', 'MLVI', 'MLVII', 'MLVIII', 'MLIX', 'MLX', 'MLXI', 'MLXII', 'MLXIII', 'MLXIV', 'MLXV', 'MLXVI', 'MLXVII', 'MLXVIII', 'MLXIX', 'MLXX', 'MLXXI', 'MLXXII', 'MLXXIII', 'MLXXIV', 'MLXXV', 'MLXXVI', 'MLXXVII', 'MLXXVIII', 'MLXXIX', 'MLXXX', 'MLXXXI', 'MLXXXII', 'MLXXXIII', 'MLXXXIV', 'MLXXXV', 'MLXXXVI', 'MLXXXVII', 'MLXXXVIII', 'MLXXXIX', 'MXC', 'MXCI', 'MXCII', 'MXCIII', 'MXCIV', 'MVC', 'MVCI', 'MVCII', 'MVCIII', 'MIC', 'MC', 'MCI', 'MCII', 'MCIII', 'MCIV', 'MCV', 'MCVI', 'MCVII', 'MCVIII', 'MCIX', 'MCX', 'MCXI', 'MCXII', 'MCXIII', 'MCXIV', 'MCXV', 'MCXVI', 'MCXVII', 'MCXVIII', 'MCXIX', 'MCXX', 'MCXXI', 'MCXXII', 'MCXXIII', 'MCXXIV', 'MCXXV', 'MCXXVI', 'MCXXVII', 'MCXXVIII', 'MCXXIX', 'MCXXX', 'MCXXXI', 'MCXXXII', 'MCXXXIII', 'MCXXXIV', 'MCXXXV', 'MCXXXVI', 'MCXXXVII', 'MCXXXVIII', 'MCXXXIX', 'MCXL', 'MCXLI', 'MCXLII', 'MCXLIII', 'MCXLIV', 'MCVL', 'MCVLI', 'MCVLII', 'MCVLIII', 'MCIL', 'MCL', 'MCLI', 'MCLII', 'MCLIII', 'MCLIV', 'MCLV', 'MCLVI', 'MCLVII', 'MCLVIII', 'MCLIX', 'MCLX', 'MCLXI', 'MCLXII', 'MCLXIII', 'MCLXIV', 'MCLXV', 'MCLXVI', 'MCLXVII', 'MCLXVIII', 'MCLXIX', 'MCLXX', 'MCLXXI', 'MCLXXII', 'MCLXXIII', 'MCLXXIV', 'MCLXXV', 'MCLXXVI', 'MCLXXVII', 'MCLXXVIII', 'MCLXXIX', 'MCLXXX', 'MCLXXXI', 'MCLXXXII', 'MCLXXXIII', 'MCLXXXIV', 'MCLXXXV', 'MCLXXXVI', 'MCLXXXVII', 'MCLXXXVIII', 'MCLXXXIX', 'MCXC', 'MCXCI', 'MCXCII', 'MCXCIII', 'MCXCIV', 'MCVC', 'MCVCI', 'MCVCII', 'MCVCIII', 'MCIC', 'MCC', 'MCCI', 'MCCII', 'MCCIII', 'MCCIV', 'MCCV', 'MCCVI', 'MCCVII', 'MCCVIII', 'MCCIX', 'MCCX', 'MCCXI', 'MCCXII', 'MCCXIII', 'MCCXIV', 'MCCXV', 'MCCXVI', 'MCCXVII', 'MCCXVIII', 'MCCXIX', 'MCCXX', 'MCCXXI', 'MCCXXII', 'MCCXXIII', 'MCCXXIV', 'MCCXXV', 'MCCXXVI', 'MCCXXVII', 'MCCXXVIII', 'MCCXXIX', 'MCCXXX', 'MCCXXXI', 'MCCXXXII', 'MCCXXXIII', 'MCCXXXIV', 'MCCXXXV', 'MCCXXXVI', 'MCCXXXVII', 'MCCXXXVIII', 'MCCXXXIX', 'MCCXL', 'MCCXLI', 'MCCXLII', 'MCCXLIII', 'MCCXLIV', 'MCCVL', 'MCCVLI', 'MCCVLII', 'MCCVLIII', 'MCCIL', 'MCCL', 'MCCLI', 'MCCLII', 'MCCLIII', 'MCCLIV', 'MCCLV', 'MCCLVI', 'MCCLVII', 'MCCLVIII', 'MCCLIX', 'MCCLX', 'MCCLXI', 'MCCLXII', 'MCCLXIII', 'MCCLXIV', 'MCCLXV', 'MCCLXVI', 'MCCLXVII', 'MCCLXVIII', 'MCCLXIX', 'MCCLXX', 'MCCLXXI', 'MCCLXXII', 'MCCLXXIII', 'MCCLXXIV', 'MCCLXXV', 'MCCLXXVI', 'MCCLXXVII', 'MCCLXXVIII', 'MCCLXXIX', 'MCCLXXX', 'MCCLXXXI', 'MCCLXXXII', 'MCCLXXXIII', 'MCCLXXXIV', 'MCCLXXXV', 'MCCLXXXVI', 'MCCLXXXVII', 'MCCLXXXVIII', 'MCCLXXXIX', 'MCCXC', 'MCCXCI', 'MCCXCII', 'MCCXCIII', 'MCCXCIV', 'MCCVC', 'MCCVCI', 'MCCVCII', 'MCCVCIII', 'MCCIC', 'MCCC', 'MCCCI', 'MCCCII', 'MCCCIII', 'MCCCIV', 'MCCCV', 'MCCCVI', 'MCCCVII', 'MCCCVIII', 'MCCCIX', 'MCCCX', 'MCCCXI', 'MCCCXII', 'MCCCXIII', 'MCCCXIV', 'MCCCXV', 'MCCCXVI', 'MCCCXVII', 'MCCCXVIII', 'MCCCXIX', 'MCCCXX', 'MCCCXXI', 'MCCCXXII', 'MCCCXXIII', 'MCCCXXIV', 'MCCCXXV', 'MCCCXXVI', 'MCCCXXVII', 'MCCCXXVIII', 'MCCCXXIX', 'MCCCXXX', 'MCCCXXXI', 'MCCCXXXII', 'MCCCXXXIII', 'MCCCXXXIV', 'MCCCXXXV', 'MCCCXXXVI', 'MCCCXXXVII', 'MCCCXXXVIII', 'MCCCXXXIX', 'MCCCXL', 'MCCCXLI', 'MCCCXLII', 'MCCCXLIII', 'MCCCXLIV', 'MCCCVL', 'MCCCVLI', 'MCCCVLII', 'MCCCVLIII', 'MCCCIL', 'MCCCL', 'MCCCLI', 'MCCCLII', 'MCCCLIII', 'MCCCLIV', 'MCCCLV', 'MCCCLVI', 'MCCCLVII', 'MCCCLVIII', 'MCCCLIX', 'MCCCLX', 'MCCCLXI', 'MCCCLXII', 'MCCCLXIII', 'MCCCLXIV', 'MCCCLXV', 'MCCCLXVI', 'MCCCLXVII', 'MCCCLXVIII', 'MCCCLXIX', 'MCCCLXX', 'MCCCLXXI', 'MCCCLXXII', 'MCCCLXXIII', 'MCCCLXXIV', 'MCCCLXXV', 'MCCCLXXVI', 'MCCCLXXVII', 'MCCCLXXVIII', 'MCCCLXXIX', 'MCCCLXXX', 'MCCCLXXXI', 'MCCCLXXXII', 'MCCCLXXXIII', 'MCCCLXXXIV', 'MCCCLXXXV', 'MCCCLXXXVI', 'MCCCLXXXVII', 'MCCCLXXXVIII', 'MCCCLXXXIX', 'MCCCXC', 'MCCCXCI', 'MCCCXCII', 'MCCCXCIII', 'MCCCXCIV', 'MCCCVC', 'MCCCVCI', 'MCCCVCII', 'MCCCVCIII', 'MCCCIC', 'MCD', 'MCDI', 'MCDII', 'MCDIII', 'MCDIV', 'MCDV', 'MCDVI', 'MCDVII', 'MCDVIII', 'MCDIX', 'MCDX', 'MCDXI', 'MCDXII', 'MCDXIII', 'MCDXIV', 'MCDXV', 'MCDXVI', 'MCDXVII', 'MCDXVIII', 'MCDXIX', 'MCDXX', 'MCDXXI', 'MCDXXII', 'MCDXXIII', 'MCDXXIV', 'MCDXXV', 'MCDXXVI', 'MCDXXVII', 'MCDXXVIII', 'MCDXXIX', 'MCDXXX', 'MCDXXXI', 'MCDXXXII', 'MCDXXXIII', 'MCDXXXIV', 'MCDXXXV', 'MCDXXXVI', 'MCDXXXVII', 'MCDXXXVIII', 'MCDXXXIX', 'MCDXL', 'MCDXLI', 'MCDXLII', 'MCDXLIII', 'MCDXLIV', 'MCDVL', 'MCDVLI', 'MCDVLII', 'MCDVLIII', 'MCDIL', 'MLD', 'MLDI', 'MLDII', 'MLDIII', 'MLDIV', 'MLDV', 'MLDVI', 'MLDVII', 'MLDVIII', 'MLDIX', 'MLDX', 'MLDXI', 'MLDXII', 'MLDXIII', 'MLDXIV', 'MLDXV', 'MLDXVI', 'MLDXVII', 'MLDXVIII', 'MLDXIX', 'MLDXX', 'MLDXXI', 'MLDXXII', 'MLDXXIII', 'MLDXXIV', 'MLDXXV', 'MLDXXVI', 'MLDXXVII', 'MLDXXVIII', 'MLDXXIX', 'MLDXXX', 'MLDXXXI', 'MLDXXXII', 'MLDXXXIII', 'MLDXXXIV', 'MLDXXXV', 'MLDXXXVI', 'MLDXXXVII', 'MLDXXXVIII', 'MLDXXXIX', 'MXD', 'MXDI', 'MXDII', 'MXDIII', 'MXDIV', 'MVD', 'MVDI', 'MVDII', 'MVDIII', 'MVDIV', 'MD', 'MDI', 'MDII', 'MDIII', 'MDIV', 'MDV', 'MDVI', 'MDVII', 'MDVIII', 'MDIX', 'MDX', 'MDXI', 'MDXII', 'MDXIII', 'MDXIV', 'MDXV', 'MDXVI', 'MDXVII', 'MDXVIII', 'MDXIX', 'MDXX', 'MDXXI', 'MDXXII', 'MDXXIII', 'MDXXIV', 'MDXXV', 'MDXXVI', 'MDXXVII', 'MDXXVIII', 'MDXXIX', 'MDXXX', 'MDXXXI', 'MDXXXII', 'MDXXXIII', 'MDXXXIV', 'MDXXXV', 'MDXXXVI', 'MDXXXVII', 'MDXXXVIII', 'MDXXXIX', 'MDXL', 'MDXLI', 'MDXLII', 'MDXLIII', 'MDXLIV', 'MDVL', 'MDVLI', 'MDVLII', 'MDVLIII', 'MDIL', 'MDL', 'MDLI', 'MDLII', 'MDLIII', 'MDLIV', 'MDLV', 'MDLVI', 'MDLVII', 'MDLVIII', 'MDLIX', 'MDLX', 'MDLXI', 'MDLXII', 'MDLXIII', 'MDLXIV', 'MDLXV', 'MDLXVI', 'MDLXVII', 'MDLXVIII', 'MDLXIX', 'MDLXX', 'MDLXXI', 'MDLXXII', 'MDLXXIII', 'MDLXXIV', 'MDLXXV', 'MDLXXVI', 'MDLXXVII', 'MDLXXVIII', 'MDLXXIX', 'MDLXXX', 'MDLXXXI', 'MDLXXXII', 'MDLXXXIII', 'MDLXXXIV', 'MDLXXXV', 'MDLXXXVI', 'MDLXXXVII', 'MDLXXXVIII', 'MDLXXXIX', 'MDXC', 'MDXCI', 'MDXCII', 'MDXCIII', 'MDXCIV', 'MDVC', 'MDVCI', 'MDVCII', 'MDVCIII', 'MDIC', 'MDC', 'MDCI', 'MDCII', 'MDCIII', 'MDCIV', 'MDCV', 'MDCVI', 'MDCVII', 'MDCVIII', 'MDCIX', 'MDCX', 'MDCXI', 'MDCXII', 'MDCXIII', 'MDCXIV', 'MDCXV', 'MDCXVI', 'MDCXVII', 'MDCXVIII', 'MDCXIX', 'MDCXX', 'MDCXXI', 'MDCXXII', 'MDCXXIII', 'MDCXXIV', 'MDCXXV', 'MDCXXVI', 'MDCXXVII', 'MDCXXVIII', 'MDCXXIX', 'MDCXXX', 'MDCXXXI', 'MDCXXXII', 'MDCXXXIII', 'MDCXXXIV', 'MDCXXXV', 'MDCXXXVI', 'MDCXXXVII', 'MDCXXXVIII', 'MDCXXXIX', 'MDCXL', 'MDCXLI', 'MDCXLII', 'MDCXLIII', 'MDCXLIV', 'MDCVL', 'MDCVLI', 'MDCVLII', 'MDCVLIII', 'MDCIL', 'MDCL', 'MDCLI', 'MDCLII', 'MDCLIII', 'MDCLIV', 'MDCLV', 'MDCLVI', 'MDCLVII', 'MDCLVIII', 'MDCLIX', 'MDCLX', 'MDCLXI', 'MDCLXII', 'MDCLXIII', 'MDCLXIV', 'MDCLXV', 'MDCLXVI', 'MDCLXVII', 'MDCLXVIII', 'MDCLXIX', 'MDCLXX', 'MDCLXXI', 'MDCLXXII', 'MDCLXXIII', 'MDCLXXIV', 'MDCLXXV', 'MDCLXXVI', 'MDCLXXVII', 'MDCLXXVIII', 'MDCLXXIX', 'MDCLXXX', 'MDCLXXXI', 'MDCLXXXII', 'MDCLXXXIII', 'MDCLXXXIV', 'MDCLXXXV', 'MDCLXXXVI', 'MDCLXXXVII', 'MDCLXXXVIII', 'MDCLXXXIX', 'MDCXC', 'MDCXCI', 'MDCXCII', 'MDCXCIII', 'MDCXCIV', 'MDCVC', 'MDCVCI', 'MDCVCII', 'MDCVCIII', 'MDCIC', 'MDCC', 'MDCCI', 'MDCCII', 'MDCCIII', 'MDCCIV', 'MDCCV', 'MDCCVI', 'MDCCVII', 'MDCCVIII', 'MDCCIX', 'MDCCX', 'MDCCXI', 'MDCCXII', 'MDCCXIII', 'MDCCXIV', 'MDCCXV', 'MDCCXVI', 'MDCCXVII', 'MDCCXVIII', 'MDCCXIX', 'MDCCXX', 'MDCCXXI', 'MDCCXXII', 'MDCCXXIII', 'MDCCXXIV', 'MDCCXXV', 'MDCCXXVI', 'MDCCXXVII', 'MDCCXXVIII', 'MDCCXXIX', 'MDCCXXX', 'MDCCXXXI', 'MDCCXXXII', 'MDCCXXXIII', 'MDCCXXXIV', 'MDCCXXXV', 'MDCCXXXVI', 'MDCCXXXVII', 'MDCCXXXVIII', 'MDCCXXXIX', 'MDCCXL', 'MDCCXLI', 'MDCCXLII', 'MDCCXLIII', 'MDCCXLIV', 'MDCCVL', 'MDCCVLI', 'MDCCVLII', 'MDCCVLIII', 'MDCCIL', 'MDCCL', 'MDCCLI', 'MDCCLII', 'MDCCLIII', 'MDCCLIV', 'MDCCLV', 'MDCCLVI', 'MDCCLVII', 'MDCCLVIII', 'MDCCLIX', 'MDCCLX', 'MDCCLXI', 'MDCCLXII', 'MDCCLXIII', 'MDCCLXIV', 'MDCCLXV', 'MDCCLXVI', 'MDCCLXVII', 'MDCCLXVIII', 'MDCCLXIX', 'MDCCLXX', 'MDCCLXXI', 'MDCCLXXII', 'MDCCLXXIII', 'MDCCLXXIV', 'MDCCLXXV', 'MDCCLXXVI', 'MDCCLXXVII', 'MDCCLXXVIII', 'MDCCLXXIX', 'MDCCLXXX', 'MDCCLXXXI', 'MDCCLXXXII', 'MDCCLXXXIII', 'MDCCLXXXIV', 'MDCCLXXXV', 'MDCCLXXXVI', 'MDCCLXXXVII', 'MDCCLXXXVIII', 'MDCCLXXXIX', 'MDCCXC', 'MDCCXCI', 'MDCCXCII', 'MDCCXCIII', 'MDCCXCIV', 'MDCCVC', 'MDCCVCI', 'MDCCVCII', 'MDCCVCIII', 'MDCCIC', 'MDCCC', 'MDCCCI', 'MDCCCII', 'MDCCCIII', 'MDCCCIV', 'MDCCCV', 'MDCCCVI', 'MDCCCVII', 'MDCCCVIII', 'MDCCCIX', 'MDCCCX', 'MDCCCXI', 'MDCCCXII', 'MDCCCXIII', 'MDCCCXIV', 'MDCCCXV', 'MDCCCXVI', 'MDCCCXVII', 'MDCCCXVIII', 'MDCCCXIX', 'MDCCCXX', 'MDCCCXXI', 'MDCCCXXII', 'MDCCCXXIII', 'MDCCCXXIV', 'MDCCCXXV', 'MDCCCXXVI', 'MDCCCXXVII', 'MDCCCXXVIII', 'MDCCCXXIX', 'MDCCCXXX', 'MDCCCXXXI', 'MDCCCXXXII', 'MDCCCXXXIII', 'MDCCCXXXIV', 'MDCCCXXXV', 'MDCCCXXXVI', 'MDCCCXXXVII', 'MDCCCXXXVIII', 'MDCCCXXXIX', 'MDCCCXL', 'MDCCCXLI', 'MDCCCXLII', 'MDCCCXLIII', 'MDCCCXLIV', 'MDCCCVL', 'MDCCCVLI', 'MDCCCVLII', 'MDCCCVLIII', 'MDCCCIL', 'MDCCCL', 'MDCCCLI', 'MDCCCLII', 'MDCCCLIII', 'MDCCCLIV', 'MDCCCLV', 'MDCCCLVI', 'MDCCCLVII', 'MDCCCLVIII', 'MDCCCLIX', 'MDCCCLX', 'MDCCCLXI', 'MDCCCLXII', 'MDCCCLXIII', 'MDCCCLXIV', 'MDCCCLXV', 'MDCCCLXVI', 'MDCCCLXVII', 'MDCCCLXVIII', 'MDCCCLXIX', 'MDCCCLXX', 'MDCCCLXXI', 'MDCCCLXXII', 'MDCCCLXXIII', 'MDCCCLXXIV', 'MDCCCLXXV', 'MDCCCLXXVI', 'MDCCCLXXVII', 'MDCCCLXXVIII', 'MDCCCLXXIX', 'MDCCCLXXX', 'MDCCCLXXXI', 'MDCCCLXXXII', 'MDCCCLXXXIII', 'MDCCCLXXXIV', 'MDCCCLXXXV', 'MDCCCLXXXVI', 'MDCCCLXXXVII', 'MDCCCLXXXVIII', 'MDCCCLXXXIX', 'MDCCCXC', 'MDCCCXCI', 'MDCCCXCII', 'MDCCCXCIII', 'MDCCCXCIV', 'MDCCCVC', 'MDCCCVCI', 'MDCCCVCII', 'MDCCCVCIII', 'MDCCCIC', 'MCM', 'MCMI', 'MCMII', 'MCMIII', 'MCMIV', 'MCMV', 'MCMVI', 'MCMVII', 'MCMVIII', 'MCMIX', 'MCMX', 'MCMXI', 'MCMXII', 'MCMXIII', 'MCMXIV', 'MCMXV', 'MCMXVI', 'MCMXVII', 'MCMXVIII', 'MCMXIX', 'MCMXX', 'MCMXXI', 'MCMXXII', 'MCMXXIII', 'MCMXXIV', 'MCMXXV', 'MCMXXVI', 'MCMXXVII', 'MCMXXVIII', 'MCMXXIX', 'MCMXXX', 'MCMXXXI', 'MCMXXXII', 'MCMXXXIII', 'MCMXXXIV', 'MCMXXXV', 'MCMXXXVI', 'MCMXXXVII', 'MCMXXXVIII', 'MCMXXXIX', 'MCMXL', 'MCMXLI', 'MCMXLII', 'MCMXLIII', 'MCMXLIV', 'MCMVL', 'MCMVLI', 'MCMVLII', 'MCMVLIII', 'MCMIL', 'MLM', 'MLMI', 'MLMII', 'MLMIII', 'MLMIV', 'MLMV', 'MLMVI', 'MLMVII', 'MLMVIII', 'MLMIX', 'MLMX', 'MLMXI', 'MLMXII', 'MLMXIII', 'MLMXIV', 'MLMXV', 'MLMXVI', 'MLMXVII', 'MLMXVIII', 'MLMXIX', 'MLMXX', 'MLMXXI', 'MLMXXII', 'MLMXXIII', 'MLMXXIV', 'MLMXXV', 'MLMXXVI', 'MLMXXVII', 'MLMXXVIII', 'MLMXXIX', 'MLMXXX', 'MLMXXXI', 'MLMXXXII', 'MLMXXXIII', 'MLMXXXIV', 'MLMXXXV', 'MLMXXXVI', 'MLMXXXVII', 'MLMXXXVIII', 'MLMXXXIX', 'MXM', 'MXMI', 'MXMII', 'MXMIII', 'MXMIV', 'MVM', 'MVMI', 'MVMII', 'MVMIII', 'MVMIV', 'MM', 'MMI', 'MMII', 'MMIII', 'MMIV', 'MMV', 'MMVI', 'MMVII', 'MMVIII', 'MMIX', 'MMX', 'MMXI', 'MMXII', 'MMXIII', 'MMXIV', 'MMXV', 'MMXVI', 'MMXVII', 'MMXVIII', 'MMXIX', 'MMXX', 'MMXXI', 'MMXXII', 'MMXXIII', 'MMXXIV', 'MMXXV', 'MMXXVI', 'MMXXVII', 'MMXXVIII', 'MMXXIX', 'MMXXX', 'MMXXXI', 'MMXXXII', 'MMXXXIII', 'MMXXXIV', 'MMXXXV', 'MMXXXVI', 'MMXXXVII', 'MMXXXVIII', 'MMXXXIX', 'MMXL', 'MMXLI', 'MMXLII', 'MMXLIII', 'MMXLIV', 'MMVL', 'MMVLI', 'MMVLII', 'MMVLIII', 'MMIL', 'MML', 'MMLI', 'MMLII', 'MMLIII', 'MMLIV', 'MMLV', 'MMLVI', 'MMLVII', 'MMLVIII', 'MMLIX', 'MMLX', 'MMLXI', 'MMLXII', 'MMLXIII', 'MMLXIV', 'MMLXV', 'MMLXVI', 'MMLXVII', 'MMLXVIII', 'MMLXIX', 'MMLXX', 'MMLXXI', 'MMLXXII', 'MMLXXIII', 'MMLXXIV', 'MMLXXV', 'MMLXXVI', 'MMLXXVII', 'MMLXXVIII', 'MMLXXIX', 'MMLXXX', 'MMLXXXI', 'MMLXXXII', 'MMLXXXIII', 'MMLXXXIV', 'MMLXXXV', 'MMLXXXVI', 'MMLXXXVII', 'MMLXXXVIII', 'MMLXXXIX', 'MMXC', 'MMXCI', 'MMXCII', 'MMXCIII', 'MMXCIV', 'MMVC', 'MMVCI', 'MMVCII', 'MMVCIII', 'MMIC', 'MMC', 'MMCI', 'MMCII', 'MMCIII', 'MMCIV', 'MMCV', 'MMCVI', 'MMCVII', 'MMCVIII', 'MMCIX', 'MMCX', 'MMCXI', 'MMCXII', 'MMCXIII', 'MMCXIV', 'MMCXV', 'MMCXVI', 'MMCXVII', 'MMCXVIII', 'MMCXIX', 'MMCXX', 'MMCXXI', 'MMCXXII', 'MMCXXIII', 'MMCXXIV', 'MMCXXV', 'MMCXXVI', 'MMCXXVII', 'MMCXXVIII', 'MMCXXIX', 'MMCXXX', 'MMCXXXI', 'MMCXXXII', 'MMCXXXIII', 'MMCXXXIV', 'MMCXXXV', 'MMCXXXVI', 'MMCXXXVII', 'MMCXXXVIII', 'MMCXXXIX', 'MMCXL', 'MMCXLI', 'MMCXLII', 'MMCXLIII', 'MMCXLIV', 'MMCVL', 'MMCVLI', 'MMCVLII', 'MMCVLIII', 'MMCIL', 'MMCL', 'MMCLI', 'MMCLII', 'MMCLIII', 'MMCLIV', 'MMCLV', 'MMCLVI', 'MMCLVII', 'MMCLVIII', 'MMCLIX', 'MMCLX', 'MMCLXI', 'MMCLXII', 'MMCLXIII', 'MMCLXIV', 'MMCLXV', 'MMCLXVI', 'MMCLXVII', 'MMCLXVIII', 'MMCLXIX', 'MMCLXX', 'MMCLXXI', 'MMCLXXII', 'MMCLXXIII', 'MMCLXXIV', 'MMCLXXV', 'MMCLXXVI', 'MMCLXXVII', 'MMCLXXVIII', 'MMCLXXIX', 'MMCLXXX', 'MMCLXXXI', 'MMCLXXXII', 'MMCLXXXIII', 'MMCLXXXIV', 'MMCLXXXV', 'MMCLXXXVI', 'MMCLXXXVII', 'MMCLXXXVIII', 'MMCLXXXIX', 'MMCXC', 'MMCXCI', 'MMCXCII', 'MMCXCIII', 'MMCXCIV', 'MMCVC', 'MMCVCI', 'MMCVCII', 'MMCVCIII', 'MMCIC', 'MMCC', 'MMCCI', 'MMCCII', 'MMCCIII', 'MMCCIV', 'MMCCV', 'MMCCVI', 'MMCCVII', 'MMCCVIII', 'MMCCIX', 'MMCCX', 'MMCCXI', 'MMCCXII', 'MMCCXIII', 'MMCCXIV', 'MMCCXV', 'MMCCXVI', 'MMCCXVII', 'MMCCXVIII', 'MMCCXIX', 'MMCCXX', 'MMCCXXI', 'MMCCXXII', 'MMCCXXIII', 'MMCCXXIV', 'MMCCXXV', 'MMCCXXVI', 'MMCCXXVII', 'MMCCXXVIII', 'MMCCXXIX', 'MMCCXXX', 'MMCCXXXI', 'MMCCXXXII', 'MMCCXXXIII', 'MMCCXXXIV', 'MMCCXXXV', 'MMCCXXXVI', 'MMCCXXXVII', 'MMCCXXXVIII', 'MMCCXXXIX', 'MMCCXL', 'MMCCXLI', 'MMCCXLII', 'MMCCXLIII', 'MMCCXLIV', 'MMCCVL', 'MMCCVLI', 'MMCCVLII', 'MMCCVLIII', 'MMCCIL', 'MMCCL', 'MMCCLI', 'MMCCLII', 'MMCCLIII', 'MMCCLIV', 'MMCCLV', 'MMCCLVI', 'MMCCLVII', 'MMCCLVIII', 'MMCCLIX', 'MMCCLX', 'MMCCLXI', 'MMCCLXII', 'MMCCLXIII', 'MMCCLXIV', 'MMCCLXV', 'MMCCLXVI', 'MMCCLXVII', 'MMCCLXVIII', 'MMCCLXIX', 'MMCCLXX', 'MMCCLXXI', 'MMCCLXXII', 'MMCCLXXIII', 'MMCCLXXIV', 'MMCCLXXV', 'MMCCLXXVI', 'MMCCLXXVII', 'MMCCLXXVIII', 'MMCCLXXIX', 'MMCCLXXX', 'MMCCLXXXI', 'MMCCLXXXII', 'MMCCLXXXIII', 'MMCCLXXXIV', 'MMCCLXXXV', 'MMCCLXXXVI', 'MMCCLXXXVII', 'MMCCLXXXVIII', 'MMCCLXXXIX', 'MMCCXC', 'MMCCXCI', 'MMCCXCII', 'MMCCXCIII', 'MMCCXCIV', 'MMCCVC', 'MMCCVCI', 'MMCCVCII', 'MMCCVCIII', 'MMCCIC', 'MMCCC', 'MMCCCI', 'MMCCCII', 'MMCCCIII', 'MMCCCIV', 'MMCCCV', 'MMCCCVI', 'MMCCCVII', 'MMCCCVIII', 'MMCCCIX', 'MMCCCX', 'MMCCCXI', 'MMCCCXII', 'MMCCCXIII', 'MMCCCXIV', 'MMCCCXV', 'MMCCCXVI', 'MMCCCXVII', 'MMCCCXVIII', 'MMCCCXIX', 'MMCCCXX', 'MMCCCXXI', 'MMCCCXXII', 'MMCCCXXIII', 'MMCCCXXIV', 'MMCCCXXV', 'MMCCCXXVI', 'MMCCCXXVII', 'MMCCCXXVIII', 'MMCCCXXIX', 'MMCCCXXX', 'MMCCCXXXI', 'MMCCCXXXII', 'MMCCCXXXIII', 'MMCCCXXXIV', 'MMCCCXXXV', 'MMCCCXXXVI', 'MMCCCXXXVII', 'MMCCCXXXVIII', 'MMCCCXXXIX', 'MMCCCXL', 'MMCCCXLI', 'MMCCCXLII', 'MMCCCXLIII', 'MMCCCXLIV', 'MMCCCVL', 'MMCCCVLI', 'MMCCCVLII', 'MMCCCVLIII', 'MMCCCIL', 'MMCCCL', 'MMCCCLI', 'MMCCCLII', 'MMCCCLIII', 'MMCCCLIV', 'MMCCCLV', 'MMCCCLVI', 'MMCCCLVII', 'MMCCCLVIII', 'MMCCCLIX', 'MMCCCLX', 'MMCCCLXI', 'MMCCCLXII', 'MMCCCLXIII', 'MMCCCLXIV', 'MMCCCLXV', 'MMCCCLXVI', 'MMCCCLXVII', 'MMCCCLXVIII', 'MMCCCLXIX', 'MMCCCLXX', 'MMCCCLXXI', 'MMCCCLXXII', 'MMCCCLXXIII', 'MMCCCLXXIV', 'MMCCCLXXV', 'MMCCCLXXVI', 'MMCCCLXXVII', 'MMCCCLXXVIII', 'MMCCCLXXIX', 'MMCCCLXXX', 'MMCCCLXXXI', 'MMCCCLXXXII', 'MMCCCLXXXIII', 'MMCCCLXXXIV', 'MMCCCLXXXV', 'MMCCCLXXXVI', 'MMCCCLXXXVII', 'MMCCCLXXXVIII', 'MMCCCLXXXIX', 'MMCCCXC', 'MMCCCXCI', 'MMCCCXCII', 'MMCCCXCIII', 'MMCCCXCIV', 'MMCCCVC', 'MMCCCVCI', 'MMCCCVCII', 'MMCCCVCIII', 'MMCCCIC', 'MMCD', 'MMCDI', 'MMCDII', 'MMCDIII', 'MMCDIV', 'MMCDV', 'MMCDVI', 'MMCDVII', 'MMCDVIII', 'MMCDIX', 'MMCDX', 'MMCDXI', 'MMCDXII', 'MMCDXIII', 'MMCDXIV', 'MMCDXV', 'MMCDXVI', 'MMCDXVII', 'MMCDXVIII', 'MMCDXIX', 'MMCDXX', 'MMCDXXI', 'MMCDXXII', 'MMCDXXIII', 'MMCDXXIV', 'MMCDXXV', 'MMCDXXVI', 'MMCDXXVII', 'MMCDXXVIII', 'MMCDXXIX', 'MMCDXXX', 'MMCDXXXI', 'MMCDXXXII', 'MMCDXXXIII', 'MMCDXXXIV', 'MMCDXXXV', 'MMCDXXXVI', 'MMCDXXXVII', 'MMCDXXXVIII', 'MMCDXXXIX', 'MMCDXL', 'MMCDXLI', 'MMCDXLII', 'MMCDXLIII', 'MMCDXLIV', 'MMCDVL', 'MMCDVLI', 'MMCDVLII', 'MMCDVLIII', 'MMCDIL', 'MMLD', 'MMLDI', 'MMLDII', 'MMLDIII', 'MMLDIV', 'MMLDV', 'MMLDVI', 'MMLDVII', 'MMLDVIII', 'MMLDIX', 'MMLDX', 'MMLDXI', 'MMLDXII', 'MMLDXIII', 'MMLDXIV', 'MMLDXV', 'MMLDXVI', 'MMLDXVII', 'MMLDXVIII', 'MMLDXIX', 'MMLDXX', 'MMLDXXI', 'MMLDXXII', 'MMLDXXIII', 'MMLDXXIV', 'MMLDXXV', 'MMLDXXVI', 'MMLDXXVII', 'MMLDXXVIII', 'MMLDXXIX', 'MMLDXXX', 'MMLDXXXI', 'MMLDXXXII', 'MMLDXXXIII', 'MMLDXXXIV', 'MMLDXXXV', 'MMLDXXXVI', 'MMLDXXXVII', 'MMLDXXXVIII', 'MMLDXXXIX', 'MMXD', 'MMXDI', 'MMXDII', 'MMXDIII', 'MMXDIV', 'MMVD', 'MMVDI', 'MMVDII', 'MMVDIII', 'MMVDIV', 'MMD', 'MMDI', 'MMDII', 'MMDIII', 'MMDIV', 'MMDV', 'MMDVI', 'MMDVII', 'MMDVIII', 'MMDIX', 'MMDX', 'MMDXI', 'MMDXII', 'MMDXIII', 'MMDXIV', 'MMDXV', 'MMDXVI', 'MMDXVII', 'MMDXVIII', 'MMDXIX', 'MMDXX', 'MMDXXI', 'MMDXXII', 'MMDXXIII', 'MMDXXIV', 'MMDXXV', 'MMDXXVI', 'MMDXXVII', 'MMDXXVIII', 'MMDXXIX', 'MMDXXX', 'MMDXXXI', 'MMDXXXII', 'MMDXXXIII', 'MMDXXXIV', 'MMDXXXV', 'MMDXXXVI', 'MMDXXXVII', 'MMDXXXVIII', 'MMDXXXIX', 'MMDXL', 'MMDXLI', 'MMDXLII', 'MMDXLIII', 'MMDXLIV', 'MMDVL', 'MMDVLI', 'MMDVLII', 'MMDVLIII', 'MMDIL', 'MMDL', 'MMDLI', 'MMDLII', 'MMDLIII', 'MMDLIV', 'MMDLV', 'MMDLVI', 'MMDLVII', 'MMDLVIII', 'MMDLIX', 'MMDLX', 'MMDLXI', 'MMDLXII', 'MMDLXIII', 'MMDLXIV', 'MMDLXV', 'MMDLXVI', 'MMDLXVII', 'MMDLXVIII', 'MMDLXIX', 'MMDLXX', 'MMDLXXI', 'MMDLXXII', 'MMDLXXIII', 'MMDLXXIV', 'MMDLXXV', 'MMDLXXVI', 'MMDLXXVII', 'MMDLXXVIII', 'MMDLXXIX', 'MMDLXXX', 'MMDLXXXI', 'MMDLXXXII', 'MMDLXXXIII', 'MMDLXXXIV', 'MMDLXXXV', 'MMDLXXXVI', 'MMDLXXXVII', 'MMDLXXXVIII', 'MMDLXXXIX', 'MMDXC', 'MMDXCI', 'MMDXCII', 'MMDXCIII', 'MMDXCIV', 'MMDVC', 'MMDVCI', 'MMDVCII', 'MMDVCIII', 'MMDIC', 'MMDC', 'MMDCI', 'MMDCII', 'MMDCIII', 'MMDCIV', 'MMDCV', 'MMDCVI', 'MMDCVII', 'MMDCVIII', 'MMDCIX', 'MMDCX', 'MMDCXI', 'MMDCXII', 'MMDCXIII', 'MMDCXIV', 'MMDCXV', 'MMDCXVI', 'MMDCXVII', 'MMDCXVIII', 'MMDCXIX', 'MMDCXX', 'MMDCXXI', 'MMDCXXII', 'MMDCXXIII', 'MMDCXXIV', 'MMDCXXV', 'MMDCXXVI', 'MMDCXXVII', 'MMDCXXVIII', 'MMDCXXIX', 'MMDCXXX', 'MMDCXXXI', 'MMDCXXXII', 'MMDCXXXIII', 'MMDCXXXIV', 'MMDCXXXV', 'MMDCXXXVI', 'MMDCXXXVII', 'MMDCXXXVIII', 'MMDCXXXIX', 'MMDCXL', 'MMDCXLI', 'MMDCXLII', 'MMDCXLIII', 'MMDCXLIV', 'MMDCVL', 'MMDCVLI', 'MMDCVLII', 'MMDCVLIII', 'MMDCIL', 'MMDCL', 'MMDCLI', 'MMDCLII', 'MMDCLIII', 'MMDCLIV', 'MMDCLV', 'MMDCLVI', 'MMDCLVII', 'MMDCLVIII', 'MMDCLIX', 'MMDCLX', 'MMDCLXI', 'MMDCLXII', 'MMDCLXIII', 'MMDCLXIV', 'MMDCLXV', 'MMDCLXVI', 'MMDCLXVII', 'MMDCLXVIII', 'MMDCLXIX', 'MMDCLXX', 'MMDCLXXI', 'MMDCLXXII', 'MMDCLXXIII', 'MMDCLXXIV', 'MMDCLXXV', 'MMDCLXXVI', 'MMDCLXXVII', 'MMDCLXXVIII', 'MMDCLXXIX', 'MMDCLXXX', 'MMDCLXXXI', 'MMDCLXXXII', 'MMDCLXXXIII', 'MMDCLXXXIV', 'MMDCLXXXV', 'MMDCLXXXVI', 'MMDCLXXXVII', 'MMDCLXXXVIII', 'MMDCLXXXIX', 'MMDCXC', 'MMDCXCI', 'MMDCXCII', 'MMDCXCIII', 'MMDCXCIV', 'MMDCVC', 'MMDCVCI', 'MMDCVCII', 'MMDCVCIII', 'MMDCIC', 'MMDCC', 'MMDCCI', 'MMDCCII', 'MMDCCIII', 'MMDCCIV', 'MMDCCV', 'MMDCCVI', 'MMDCCVII', 'MMDCCVIII', 'MMDCCIX', 'MMDCCX', 'MMDCCXI', 'MMDCCXII', 'MMDCCXIII', 'MMDCCXIV', 'MMDCCXV', 'MMDCCXVI', 'MMDCCXVII', 'MMDCCXVIII', 'MMDCCXIX', 'MMDCCXX', 'MMDCCXXI', 'MMDCCXXII', 'MMDCCXXIII', 'MMDCCXXIV', 'MMDCCXXV', 'MMDCCXXVI', 'MMDCCXXVII', 'MMDCCXXVIII', 'MMDCCXXIX', 'MMDCCXXX', 'MMDCCXXXI', 'MMDCCXXXII', 'MMDCCXXXIII', 'MMDCCXXXIV', 'MMDCCXXXV', 'MMDCCXXXVI', 'MMDCCXXXVII', 'MMDCCXXXVIII', 'MMDCCXXXIX', 'MMDCCXL', 'MMDCCXLI', 'MMDCCXLII', 'MMDCCXLIII', 'MMDCCXLIV', 'MMDCCVL', 'MMDCCVLI', 'MMDCCVLII', 'MMDCCVLIII', 'MMDCCIL', 'MMDCCL', 'MMDCCLI', 'MMDCCLII', 'MMDCCLIII', 'MMDCCLIV', 'MMDCCLV', 'MMDCCLVI', 'MMDCCLVII', 'MMDCCLVIII', 'MMDCCLIX', 'MMDCCLX', 'MMDCCLXI', 'MMDCCLXII', 'MMDCCLXIII', 'MMDCCLXIV', 'MMDCCLXV', 'MMDCCLXVI', 'MMDCCLXVII', 'MMDCCLXVIII', 'MMDCCLXIX', 'MMDCCLXX', 'MMDCCLXXI', 'MMDCCLXXII', 'MMDCCLXXIII', 'MMDCCLXXIV', 'MMDCCLXXV', 'MMDCCLXXVI', 'MMDCCLXXVII', 'MMDCCLXXVIII', 'MMDCCLXXIX', 'MMDCCLXXX', 'MMDCCLXXXI', 'MMDCCLXXXII', 'MMDCCLXXXIII', 'MMDCCLXXXIV', 'MMDCCLXXXV', 'MMDCCLXXXVI', 'MMDCCLXXXVII', 'MMDCCLXXXVIII', 'MMDCCLXXXIX', 'MMDCCXC', 'MMDCCXCI', 'MMDCCXCII', 'MMDCCXCIII', 'MMDCCXCIV', 'MMDCCVC', 'MMDCCVCI', 'MMDCCVCII', 'MMDCCVCIII', 'MMDCCIC', 'MMDCCC', 'MMDCCCI', 'MMDCCCII', 'MMDCCCIII', 'MMDCCCIV', 'MMDCCCV', 'MMDCCCVI', 'MMDCCCVII', 'MMDCCCVIII', 'MMDCCCIX', 'MMDCCCX', 'MMDCCCXI', 'MMDCCCXII', 'MMDCCCXIII', 'MMDCCCXIV', 'MMDCCCXV', 'MMDCCCXVI', 'MMDCCCXVII', 'MMDCCCXVIII', 'MMDCCCXIX', 'MMDCCCXX', 'MMDCCCXXI', 'MMDCCCXXII', 'MMDCCCXXIII', 'MMDCCCXXIV', 'MMDCCCXXV', 'MMDCCCXXVI', 'MMDCCCXXVII', 'MMDCCCXXVIII', 'MMDCCCXXIX', 'MMDCCCXXX', 'MMDCCCXXXI', 'MMDCCCXXXII', 'MMDCCCXXXIII', 'MMDCCCXXXIV', 'MMDCCCXXXV', 'MMDCCCXXXVI', 'MMDCCCXXXVII', 'MMDCCCXXXVIII', 'MMDCCCXXXIX', 'MMDCCCXL', 'MMDCCCXLI', 'MMDCCCXLII', 'MMDCCCXLIII', 'MMDCCCXLIV', 'MMDCCCVL', 'MMDCCCVLI', 'MMDCCCVLII', 'MMDCCCVLIII', 'MMDCCCIL', 'MMDCCCL', 'MMDCCCLI', 'MMDCCCLII', 'MMDCCCLIII', 'MMDCCCLIV', 'MMDCCCLV', 'MMDCCCLVI', 'MMDCCCLVII', 'MMDCCCLVIII', 'MMDCCCLIX', 'MMDCCCLX', 'MMDCCCLXI', 'MMDCCCLXII', 'MMDCCCLXIII', 'MMDCCCLXIV', 'MMDCCCLXV', 'MMDCCCLXVI', 'MMDCCCLXVII', 'MMDCCCLXVIII', 'MMDCCCLXIX', 'MMDCCCLXX', 'MMDCCCLXXI', 'MMDCCCLXXII', 'MMDCCCLXXIII', 'MMDCCCLXXIV', 'MMDCCCLXXV', 'MMDCCCLXXVI', 'MMDCCCLXXVII', 'MMDCCCLXXVIII', 'MMDCCCLXXIX', 'MMDCCCLXXX', 'MMDCCCLXXXI', 'MMDCCCLXXXII', 'MMDCCCLXXXIII', 'MMDCCCLXXXIV', 'MMDCCCLXXXV', 'MMDCCCLXXXVI', 'MMDCCCLXXXVII', 'MMDCCCLXXXVIII', 'MMDCCCLXXXIX', 'MMDCCCXC', 'MMDCCCXCI', 'MMDCCCXCII', 'MMDCCCXCIII', 'MMDCCCXCIV', 'MMDCCCVC', 'MMDCCCVCI', 'MMDCCCVCII', 'MMDCCCVCIII', 'MMDCCCIC', 'MMCM', 'MMCMI', 'MMCMII', 'MMCMIII', 'MMCMIV', 'MMCMV', 'MMCMVI', 'MMCMVII', 'MMCMVIII', 'MMCMIX', 'MMCMX', 'MMCMXI', 'MMCMXII', 'MMCMXIII', 'MMCMXIV', 'MMCMXV', 'MMCMXVI', 'MMCMXVII', 'MMCMXVIII', 'MMCMXIX', 'MMCMXX', 'MMCMXXI', 'MMCMXXII', 'MMCMXXIII', 'MMCMXXIV', 'MMCMXXV', 'MMCMXXVI', 'MMCMXXVII', 'MMCMXXVIII', 'MMCMXXIX', 'MMCMXXX', 'MMCMXXXI', 'MMCMXXXII', 'MMCMXXXIII', 'MMCMXXXIV', 'MMCMXXXV', 'MMCMXXXVI', 'MMCMXXXVII', 'MMCMXXXVIII', 'MMCMXXXIX', 'MMCMXL', 'MMCMXLI', 'MMCMXLII', 'MMCMXLIII', 'MMCMXLIV', 'MMCMVL', 'MMCMVLI', 'MMCMVLII', 'MMCMVLIII', 'MMCMIL', 'MMLM', 'MMLMI', 'MMLMII', 'MMLMIII', 'MMLMIV', 'MMLMV', 'MMLMVI', 'MMLMVII', 'MMLMVIII', 'MMLMIX', 'MMLMX', 'MMLMXI', 'MMLMXII', 'MMLMXIII', 'MMLMXIV', 'MMLMXV', 'MMLMXVI', 'MMLMXVII', 'MMLMXVIII', 'MMLMXIX', 'MMLMXX', 'MMLMXXI', 'MMLMXXII', 'MMLMXXIII', 'MMLMXXIV', 'MMLMXXV', 'MMLMXXVI', 'MMLMXXVII', 'MMLMXXVIII', 'MMLMXXIX', 'MMLMXXX', 'MMLMXXXI', 'MMLMXXXII', 'MMLMXXXIII', 'MMLMXXXIV', 'MMLMXXXV', 'MMLMXXXVI', 'MMLMXXXVII', 'MMLMXXXVIII', 'MMLMXXXIX', 'MMXM', 'MMXMI', 'MMXMII', 'MMXMIII', 'MMXMIV', 'MMVM', 'MMVMI', 'MMVMII', 'MMVMIII', 'MMVMIV', 'MMM', 'MMMI', 'MMMII', 'MMMIII', 'MMMIV', 'MMMV', 'MMMVI', 'MMMVII', 'MMMVIII', 'MMMIX', 'MMMX', 'MMMXI', 'MMMXII', 'MMMXIII', 'MMMXIV', 'MMMXV', 'MMMXVI', 'MMMXVII', 'MMMXVIII', 'MMMXIX', 'MMMXX', 'MMMXXI', 'MMMXXII', 'MMMXXIII', 'MMMXXIV', 'MMMXXV', 'MMMXXVI', 'MMMXXVII', 'MMMXXVIII', 'MMMXXIX', 'MMMXXX', 'MMMXXXI', 'MMMXXXII', 'MMMXXXIII', 'MMMXXXIV', 'MMMXXXV', 'MMMXXXVI', 'MMMXXXVII', 'MMMXXXVIII', 'MMMXXXIX', 'MMMXL', 'MMMXLI', 'MMMXLII', 'MMMXLIII', 'MMMXLIV', 'MMMVL', 'MMMVLI', 'MMMVLII', 'MMMVLIII', 'MMMIL', 'MMML', 'MMMLI', 'MMMLII', 'MMMLIII', 'MMMLIV', 'MMMLV', 'MMMLVI', 'MMMLVII', 'MMMLVIII', 'MMMLIX', 'MMMLX', 'MMMLXI', 'MMMLXII', 'MMMLXIII', 'MMMLXIV', 'MMMLXV', 'MMMLXVI', 'MMMLXVII', 'MMMLXVIII', 'MMMLXIX', 'MMMLXX', 'MMMLXXI', 'MMMLXXII', 'MMMLXXIII', 'MMMLXXIV', 'MMMLXXV', 'MMMLXXVI', 'MMMLXXVII', 'MMMLXXVIII', 'MMMLXXIX', 'MMMLXXX', 'MMMLXXXI', 'MMMLXXXII', 'MMMLXXXIII', 'MMMLXXXIV', 'MMMLXXXV', 'MMMLXXXVI', 'MMMLXXXVII', 'MMMLXXXVIII', 'MMMLXXXIX', 'MMMXC', 'MMMXCI', 'MMMXCII', 'MMMXCIII', 'MMMXCIV', 'MMMVC', 'MMMVCI', 'MMMVCII', 'MMMVCIII', 'MMMIC', 'MMMC', 'MMMCI', 'MMMCII', 'MMMCIII', 'MMMCIV', 'MMMCV', 'MMMCVI', 'MMMCVII', 'MMMCVIII', 'MMMCIX', 'MMMCX', 'MMMCXI', 'MMMCXII', 'MMMCXIII', 'MMMCXIV', 'MMMCXV', 'MMMCXVI', 'MMMCXVII', 'MMMCXVIII', 'MMMCXIX', 'MMMCXX', 'MMMCXXI', 'MMMCXXII', 'MMMCXXIII', 'MMMCXXIV', 'MMMCXXV', 'MMMCXXVI', 'MMMCXXVII', 'MMMCXXVIII', 'MMMCXXIX', 'MMMCXXX', 'MMMCXXXI', 'MMMCXXXII', 'MMMCXXXIII', 'MMMCXXXIV', 'MMMCXXXV', 'MMMCXXXVI', 'MMMCXXXVII', 'MMMCXXXVIII', 'MMMCXXXIX', 'MMMCXL', 'MMMCXLI', 'MMMCXLII', 'MMMCXLIII', 'MMMCXLIV', 'MMMCVL', 'MMMCVLI', 'MMMCVLII', 'MMMCVLIII', 'MMMCIL', 'MMMCL', 'MMMCLI', 'MMMCLII', 'MMMCLIII', 'MMMCLIV', 'MMMCLV', 'MMMCLVI', 'MMMCLVII', 'MMMCLVIII', 'MMMCLIX', 'MMMCLX', 'MMMCLXI', 'MMMCLXII', 'MMMCLXIII', 'MMMCLXIV', 'MMMCLXV', 'MMMCLXVI', 'MMMCLXVII', 'MMMCLXVIII', 'MMMCLXIX', 'MMMCLXX', 'MMMCLXXI', 'MMMCLXXII', 'MMMCLXXIII', 'MMMCLXXIV', 'MMMCLXXV', 'MMMCLXXVI', 'MMMCLXXVII', 'MMMCLXXVIII', 'MMMCLXXIX', 'MMMCLXXX', 'MMMCLXXXI', 'MMMCLXXXII', 'MMMCLXXXIII', 'MMMCLXXXIV', 'MMMCLXXXV', 'MMMCLXXXVI', 'MMMCLXXXVII', 'MMMCLXXXVIII', 'MMMCLXXXIX', 'MMMCXC', 'MMMCXCI', 'MMMCXCII', 'MMMCXCIII', 'MMMCXCIV', 'MMMCVC', 'MMMCVCI', 'MMMCVCII', 'MMMCVCIII', 'MMMCIC', 'MMMCC', 'MMMCCI', 'MMMCCII', 'MMMCCIII', 'MMMCCIV', 'MMMCCV', 'MMMCCVI', 'MMMCCVII', 'MMMCCVIII', 'MMMCCIX', 'MMMCCX', 'MMMCCXI', 'MMMCCXII', 'MMMCCXIII', 'MMMCCXIV', 'MMMCCXV', 'MMMCCXVI', 'MMMCCXVII', 'MMMCCXVIII', 'MMMCCXIX', 'MMMCCXX', 'MMMCCXXI', 'MMMCCXXII', 'MMMCCXXIII', 'MMMCCXXIV', 'MMMCCXXV', 'MMMCCXXVI', 'MMMCCXXVII', 'MMMCCXXVIII', 'MMMCCXXIX', 'MMMCCXXX', 'MMMCCXXXI', 'MMMCCXXXII', 'MMMCCXXXIII', 'MMMCCXXXIV', 'MMMCCXXXV', 'MMMCCXXXVI', 'MMMCCXXXVII', 'MMMCCXXXVIII', 'MMMCCXXXIX', 'MMMCCXL', 'MMMCCXLI', 'MMMCCXLII', 'MMMCCXLIII', 'MMMCCXLIV', 'MMMCCVL', 'MMMCCVLI', 'MMMCCVLII', 'MMMCCVLIII', 'MMMCCIL', 'MMMCCL', 'MMMCCLI', 'MMMCCLII', 'MMMCCLIII', 'MMMCCLIV', 'MMMCCLV', 'MMMCCLVI', 'MMMCCLVII', 'MMMCCLVIII', 'MMMCCLIX', 'MMMCCLX', 'MMMCCLXI', 'MMMCCLXII', 'MMMCCLXIII', 'MMMCCLXIV', 'MMMCCLXV', 'MMMCCLXVI', 'MMMCCLXVII', 'MMMCCLXVIII', 'MMMCCLXIX', 'MMMCCLXX', 'MMMCCLXXI', 'MMMCCLXXII', 'MMMCCLXXIII', 'MMMCCLXXIV', 'MMMCCLXXV', 'MMMCCLXXVI', 'MMMCCLXXVII', 'MMMCCLXXVIII', 'MMMCCLXXIX', 'MMMCCLXXX', 'MMMCCLXXXI', 'MMMCCLXXXII', 'MMMCCLXXXIII', 'MMMCCLXXXIV', 'MMMCCLXXXV', 'MMMCCLXXXVI', 'MMMCCLXXXVII', 'MMMCCLXXXVIII', 'MMMCCLXXXIX', 'MMMCCXC', 'MMMCCXCI', 'MMMCCXCII', 'MMMCCXCIII', 'MMMCCXCIV', 'MMMCCVC', 'MMMCCVCI', 'MMMCCVCII', 'MMMCCVCIII', 'MMMCCIC', 'MMMCCC', 'MMMCCCI', 'MMMCCCII', 'MMMCCCIII', 'MMMCCCIV', 'MMMCCCV', 'MMMCCCVI', 'MMMCCCVII', 'MMMCCCVIII', 'MMMCCCIX', 'MMMCCCX', 'MMMCCCXI', 'MMMCCCXII', 'MMMCCCXIII', 'MMMCCCXIV', 'MMMCCCXV', 'MMMCCCXVI', 'MMMCCCXVII', 'MMMCCCXVIII', 'MMMCCCXIX', 'MMMCCCXX', 'MMMCCCXXI', 'MMMCCCXXII', 'MMMCCCXXIII', 'MMMCCCXXIV', 'MMMCCCXXV', 'MMMCCCXXVI', 'MMMCCCXXVII', 'MMMCCCXXVIII', 'MMMCCCXXIX', 'MMMCCCXXX', 'MMMCCCXXXI', 'MMMCCCXXXII', 'MMMCCCXXXIII', 'MMMCCCXXXIV', 'MMMCCCXXXV', 'MMMCCCXXXVI', 'MMMCCCXXXVII', 'MMMCCCXXXVIII', 'MMMCCCXXXIX', 'MMMCCCXL', 'MMMCCCXLI', 'MMMCCCXLII', 'MMMCCCXLIII', 'MMMCCCXLIV', 'MMMCCCVL', 'MMMCCCVLI', 'MMMCCCVLII', 'MMMCCCVLIII', 'MMMCCCIL', 'MMMCCCL', 'MMMCCCLI', 'MMMCCCLII', 'MMMCCCLIII', 'MMMCCCLIV', 'MMMCCCLV', 'MMMCCCLVI', 'MMMCCCLVII', 'MMMCCCLVIII', 'MMMCCCLIX', 'MMMCCCLX', 'MMMCCCLXI', 'MMMCCCLXII', 'MMMCCCLXIII', 'MMMCCCLXIV', 'MMMCCCLXV', 'MMMCCCLXVI', 'MMMCCCLXVII', 'MMMCCCLXVIII', 'MMMCCCLXIX', 'MMMCCCLXX', 'MMMCCCLXXI', 'MMMCCCLXXII', 'MMMCCCLXXIII', 'MMMCCCLXXIV', 'MMMCCCLXXV', 'MMMCCCLXXVI', 'MMMCCCLXXVII', 'MMMCCCLXXVIII', 'MMMCCCLXXIX', 'MMMCCCLXXX', 'MMMCCCLXXXI', 'MMMCCCLXXXII', 'MMMCCCLXXXIII', 'MMMCCCLXXXIV', 'MMMCCCLXXXV', 'MMMCCCLXXXVI', 'MMMCCCLXXXVII', 'MMMCCCLXXXVIII', 'MMMCCCLXXXIX', 'MMMCCCXC', 'MMMCCCXCI', 'MMMCCCXCII', 'MMMCCCXCIII', 'MMMCCCXCIV', 'MMMCCCVC', 'MMMCCCVCI', 'MMMCCCVCII', 'MMMCCCVCIII', 'MMMCCCIC', 'MMMCD', 'MMMCDI', 'MMMCDII', 'MMMCDIII', 'MMMCDIV', 'MMMCDV', 'MMMCDVI', 'MMMCDVII', 'MMMCDVIII', 'MMMCDIX', 'MMMCDX', 'MMMCDXI', 'MMMCDXII', 'MMMCDXIII', 'MMMCDXIV', 'MMMCDXV', 'MMMCDXVI', 'MMMCDXVII', 'MMMCDXVIII', 'MMMCDXIX', 'MMMCDXX', 'MMMCDXXI', 'MMMCDXXII', 'MMMCDXXIII', 'MMMCDXXIV', 'MMMCDXXV', 'MMMCDXXVI', 'MMMCDXXVII', 'MMMCDXXVIII', 'MMMCDXXIX', 'MMMCDXXX', 'MMMCDXXXI', 'MMMCDXXXII', 'MMMCDXXXIII', 'MMMCDXXXIV', 'MMMCDXXXV', 'MMMCDXXXVI', 'MMMCDXXXVII', 'MMMCDXXXVIII', 'MMMCDXXXIX', 'MMMCDXL', 'MMMCDXLI', 'MMMCDXLII', 'MMMCDXLIII', 'MMMCDXLIV', 'MMMCDVL', 'MMMCDVLI', 'MMMCDVLII', 'MMMCDVLIII', 'MMMCDIL', 'MMMLD', 'MMMLDI', 'MMMLDII', 'MMMLDIII', 'MMMLDIV', 'MMMLDV', 'MMMLDVI', 'MMMLDVII', 'MMMLDVIII', 'MMMLDIX', 'MMMLDX', 'MMMLDXI', 'MMMLDXII', 'MMMLDXIII', 'MMMLDXIV', 'MMMLDXV', 'MMMLDXVI', 'MMMLDXVII', 'MMMLDXVIII', 'MMMLDXIX', 'MMMLDXX', 'MMMLDXXI', 'MMMLDXXII', 'MMMLDXXIII', 'MMMLDXXIV', 'MMMLDXXV', 'MMMLDXXVI', 'MMMLDXXVII', 'MMMLDXXVIII', 'MMMLDXXIX', 'MMMLDXXX', 'MMMLDXXXI', 'MMMLDXXXII', 'MMMLDXXXIII', 'MMMLDXXXIV', 'MMMLDXXXV', 'MMMLDXXXVI', 'MMMLDXXXVII', 'MMMLDXXXVIII', 'MMMLDXXXIX', 'MMMXD', 'MMMXDI', 'MMMXDII', 'MMMXDIII', 'MMMXDIV', 'MMMVD', 'MMMVDI', 'MMMVDII', 'MMMVDIII', 'MMMVDIV', 'MMMD', 'MMMDI', 'MMMDII', 'MMMDIII', 'MMMDIV', 'MMMDV', 'MMMDVI', 'MMMDVII', 'MMMDVIII', 'MMMDIX', 'MMMDX', 'MMMDXI', 'MMMDXII', 'MMMDXIII', 'MMMDXIV', 'MMMDXV', 'MMMDXVI', 'MMMDXVII', 'MMMDXVIII', 'MMMDXIX', 'MMMDXX', 'MMMDXXI', 'MMMDXXII', 'MMMDXXIII', 'MMMDXXIV', 'MMMDXXV', 'MMMDXXVI', 'MMMDXXVII', 'MMMDXXVIII', 'MMMDXXIX', 'MMMDXXX', 'MMMDXXXI', 'MMMDXXXII', 'MMMDXXXIII', 'MMMDXXXIV', 'MMMDXXXV', 'MMMDXXXVI', 'MMMDXXXVII', 'MMMDXXXVIII', 'MMMDXXXIX', 'MMMDXL', 'MMMDXLI', 'MMMDXLII', 'MMMDXLIII', 'MMMDXLIV', 'MMMDVL', 'MMMDVLI', 'MMMDVLII', 'MMMDVLIII', 'MMMDIL', 'MMMDL', 'MMMDLI', 'MMMDLII', 'MMMDLIII', 'MMMDLIV', 'MMMDLV', 'MMMDLVI', 'MMMDLVII', 'MMMDLVIII', 'MMMDLIX', 'MMMDLX', 'MMMDLXI', 'MMMDLXII', 'MMMDLXIII', 'MMMDLXIV', 'MMMDLXV', 'MMMDLXVI', 'MMMDLXVII', 'MMMDLXVIII', 'MMMDLXIX', 'MMMDLXX', 'MMMDLXXI', 'MMMDLXXII', 'MMMDLXXIII', 'MMMDLXXIV', 'MMMDLXXV', 'MMMDLXXVI', 'MMMDLXXVII', 'MMMDLXXVIII', 'MMMDLXXIX', 'MMMDLXXX', 'MMMDLXXXI', 'MMMDLXXXII', 'MMMDLXXXIII', 'MMMDLXXXIV', 'MMMDLXXXV', 'MMMDLXXXVI', 'MMMDLXXXVII', 'MMMDLXXXVIII', 'MMMDLXXXIX', 'MMMDXC', 'MMMDXCI', 'MMMDXCII', 'MMMDXCIII', 'MMMDXCIV', 'MMMDVC', 'MMMDVCI', 'MMMDVCII', 'MMMDVCIII', 'MMMDIC', 'MMMDC', 'MMMDCI', 'MMMDCII', 'MMMDCIII', 'MMMDCIV', 'MMMDCV', 'MMMDCVI', 'MMMDCVII', 'MMMDCVIII', 'MMMDCIX', 'MMMDCX', 'MMMDCXI', 'MMMDCXII', 'MMMDCXIII', 'MMMDCXIV', 'MMMDCXV', 'MMMDCXVI', 'MMMDCXVII', 'MMMDCXVIII', 'MMMDCXIX', 'MMMDCXX', 'MMMDCXXI', 'MMMDCXXII', 'MMMDCXXIII', 'MMMDCXXIV', 'MMMDCXXV', 'MMMDCXXVI', 'MMMDCXXVII', 'MMMDCXXVIII', 'MMMDCXXIX', 'MMMDCXXX', 'MMMDCXXXI', 'MMMDCXXXII', 'MMMDCXXXIII', 'MMMDCXXXIV', 'MMMDCXXXV', 'MMMDCXXXVI', 'MMMDCXXXVII', 'MMMDCXXXVIII', 'MMMDCXXXIX', 'MMMDCXL', 'MMMDCXLI', 'MMMDCXLII', 'MMMDCXLIII', 'MMMDCXLIV', 'MMMDCVL', 'MMMDCVLI', 'MMMDCVLII', 'MMMDCVLIII', 'MMMDCIL', 'MMMDCL', 'MMMDCLI', 'MMMDCLII', 'MMMDCLIII', 'MMMDCLIV', 'MMMDCLV', 'MMMDCLVI', 'MMMDCLVII', 'MMMDCLVIII', 'MMMDCLIX', 'MMMDCLX', 'MMMDCLXI', 'MMMDCLXII', 'MMMDCLXIII', 'MMMDCLXIV', 'MMMDCLXV', 'MMMDCLXVI', 'MMMDCLXVII', 'MMMDCLXVIII', 'MMMDCLXIX', 'MMMDCLXX', 'MMMDCLXXI', 'MMMDCLXXII', 'MMMDCLXXIII', 'MMMDCLXXIV', 'MMMDCLXXV', 'MMMDCLXXVI', 'MMMDCLXXVII', 'MMMDCLXXVIII', 'MMMDCLXXIX', 'MMMDCLXXX', 'MMMDCLXXXI', 'MMMDCLXXXII', 'MMMDCLXXXIII', 'MMMDCLXXXIV', 'MMMDCLXXXV', 'MMMDCLXXXVI', 'MMMDCLXXXVII', 'MMMDCLXXXVIII', 'MMMDCLXXXIX', 'MMMDCXC', 'MMMDCXCI', 'MMMDCXCII', 'MMMDCXCIII', 'MMMDCXCIV', 'MMMDCVC', 'MMMDCVCI', 'MMMDCVCII', 'MMMDCVCIII', 'MMMDCIC', 'MMMDCC', 'MMMDCCI', 'MMMDCCII', 'MMMDCCIII', 'MMMDCCIV', 'MMMDCCV', 'MMMDCCVI', 'MMMDCCVII', 'MMMDCCVIII', 'MMMDCCIX', 'MMMDCCX', 'MMMDCCXI', 'MMMDCCXII', 'MMMDCCXIII', 'MMMDCCXIV', 'MMMDCCXV', 'MMMDCCXVI', 'MMMDCCXVII', 'MMMDCCXVIII', 'MMMDCCXIX', 'MMMDCCXX', 'MMMDCCXXI', 'MMMDCCXXII', 'MMMDCCXXIII', 'MMMDCCXXIV', 'MMMDCCXXV', 'MMMDCCXXVI', 'MMMDCCXXVII', 'MMMDCCXXVIII', 'MMMDCCXXIX', 'MMMDCCXXX', 'MMMDCCXXXI', 'MMMDCCXXXII', 'MMMDCCXXXIII', 'MMMDCCXXXIV', 'MMMDCCXXXV', 'MMMDCCXXXVI', 'MMMDCCXXXVII', 'MMMDCCXXXVIII', 'MMMDCCXXXIX', 'MMMDCCXL', 'MMMDCCXLI', 'MMMDCCXLII', 'MMMDCCXLIII', 'MMMDCCXLIV', 'MMMDCCVL', 'MMMDCCVLI', 'MMMDCCVLII', 'MMMDCCVLIII', 'MMMDCCIL', 'MMMDCCL', 'MMMDCCLI', 'MMMDCCLII', 'MMMDCCLIII', 'MMMDCCLIV', 'MMMDCCLV', 'MMMDCCLVI', 'MMMDCCLVII', 'MMMDCCLVIII', 'MMMDCCLIX', 'MMMDCCLX', 'MMMDCCLXI', 'MMMDCCLXII', 'MMMDCCLXIII', 'MMMDCCLXIV', 'MMMDCCLXV', 'MMMDCCLXVI', 'MMMDCCLXVII', 'MMMDCCLXVIII', 'MMMDCCLXIX', 'MMMDCCLXX', 'MMMDCCLXXI', 'MMMDCCLXXII', 'MMMDCCLXXIII', 'MMMDCCLXXIV', 'MMMDCCLXXV', 'MMMDCCLXXVI', 'MMMDCCLXXVII', 'MMMDCCLXXVIII', 'MMMDCCLXXIX', 'MMMDCCLXXX', 'MMMDCCLXXXI', 'MMMDCCLXXXII', 'MMMDCCLXXXIII', 'MMMDCCLXXXIV', 'MMMDCCLXXXV', 'MMMDCCLXXXVI', 'MMMDCCLXXXVII', 'MMMDCCLXXXVIII', 'MMMDCCLXXXIX', 'MMMDCCXC', 'MMMDCCXCI', 'MMMDCCXCII', 'MMMDCCXCIII', 'MMMDCCXCIV', 'MMMDCCVC', 'MMMDCCVCI', 'MMMDCCVCII', 'MMMDCCVCIII', 'MMMDCCIC', 'MMMDCCC', 'MMMDCCCI', 'MMMDCCCII', 'MMMDCCCIII', 'MMMDCCCIV', 'MMMDCCCV', 'MMMDCCCVI', 'MMMDCCCVII', 'MMMDCCCVIII', 'MMMDCCCIX', 'MMMDCCCX', 'MMMDCCCXI', 'MMMDCCCXII', 'MMMDCCCXIII', 'MMMDCCCXIV', 'MMMDCCCXV', 'MMMDCCCXVI', 'MMMDCCCXVII', 'MMMDCCCXVIII', 'MMMDCCCXIX', 'MMMDCCCXX', 'MMMDCCCXXI', 'MMMDCCCXXII', 'MMMDCCCXXIII', 'MMMDCCCXXIV', 'MMMDCCCXXV', 'MMMDCCCXXVI', 'MMMDCCCXXVII', 'MMMDCCCXXVIII', 'MMMDCCCXXIX', 'MMMDCCCXXX', 'MMMDCCCXXXI', 'MMMDCCCXXXII', 'MMMDCCCXXXIII', 'MMMDCCCXXXIV', 'MMMDCCCXXXV', 'MMMDCCCXXXVI', 'MMMDCCCXXXVII', 'MMMDCCCXXXVIII', 'MMMDCCCXXXIX', 'MMMDCCCXL', 'MMMDCCCXLI', 'MMMDCCCXLII', 'MMMDCCCXLIII', 'MMMDCCCXLIV', 'MMMDCCCVL', 'MMMDCCCVLI', 'MMMDCCCVLII', 'MMMDCCCVLIII', 'MMMDCCCIL', 'MMMDCCCL', 'MMMDCCCLI', 'MMMDCCCLII', 'MMMDCCCLIII', 'MMMDCCCLIV', 'MMMDCCCLV', 'MMMDCCCLVI', 'MMMDCCCLVII', 'MMMDCCCLVIII', 'MMMDCCCLIX', 'MMMDCCCLX', 'MMMDCCCLXI', 'MMMDCCCLXII', 'MMMDCCCLXIII', 'MMMDCCCLXIV', 'MMMDCCCLXV', 'MMMDCCCLXVI', 'MMMDCCCLXVII', 'MMMDCCCLXVIII', 'MMMDCCCLXIX', 'MMMDCCCLXX', 'MMMDCCCLXXI', 'MMMDCCCLXXII', 'MMMDCCCLXXIII', 'MMMDCCCLXXIV', 'MMMDCCCLXXV', 'MMMDCCCLXXVI', 'MMMDCCCLXXVII', 'MMMDCCCLXXVIII', 'MMMDCCCLXXIX', 'MMMDCCCLXXX', 'MMMDCCCLXXXI', 'MMMDCCCLXXXII', 'MMMDCCCLXXXIII', 'MMMDCCCLXXXIV', 'MMMDCCCLXXXV', 'MMMDCCCLXXXVI', 'MMMDCCCLXXXVII', 'MMMDCCCLXXXVIII', 'MMMDCCCLXXXIX', 'MMMDCCCXC', 'MMMDCCCXCI', 'MMMDCCCXCII', 'MMMDCCCXCIII', 'MMMDCCCXCIV', 'MMMDCCCVC', 'MMMDCCCVCI', 'MMMDCCCVCII', 'MMMDCCCVCIII', 'MMMDCCCIC', 'MMMCM', 'MMMCMI', 'MMMCMII', 'MMMCMIII', 'MMMCMIV', 'MMMCMV', 'MMMCMVI', 'MMMCMVII', 'MMMCMVIII', 'MMMCMIX', 'MMMCMX', 'MMMCMXI', 'MMMCMXII', 'MMMCMXIII', 'MMMCMXIV', 'MMMCMXV', 'MMMCMXVI', 'MMMCMXVII', 'MMMCMXVIII', 'MMMCMXIX', 'MMMCMXX', 'MMMCMXXI', 'MMMCMXXII', 'MMMCMXXIII', 'MMMCMXXIV', 'MMMCMXXV', 'MMMCMXXVI', 'MMMCMXXVII', 'MMMCMXXVIII', 'MMMCMXXIX', 'MMMCMXXX', 'MMMCMXXXI', 'MMMCMXXXII', 'MMMCMXXXIII', 'MMMCMXXXIV', 'MMMCMXXXV', 'MMMCMXXXVI', 'MMMCMXXXVII', 'MMMCMXXXVIII', 'MMMCMXXXIX', 'MMMCMXL', 'MMMCMXLI', 'MMMCMXLII', 'MMMCMXLIII', 'MMMCMXLIV', 'MMMCMVL', 'MMMCMVLI', 'MMMCMVLII', 'MMMCMVLIII', 'MMMCMIL', 'MMMLM', 'MMMLMI', 'MMMLMII', 'MMMLMIII', 'MMMLMIV', 'MMMLMV', 'MMMLMVI', 'MMMLMVII', 'MMMLMVIII', 'MMMLMIX', 'MMMLMX', 'MMMLMXI', 'MMMLMXII', 'MMMLMXIII', 'MMMLMXIV', 'MMMLMXV', 'MMMLMXVI', 'MMMLMXVII', 'MMMLMXVIII', 'MMMLMXIX', 'MMMLMXX', 'MMMLMXXI', 'MMMLMXXII', 'MMMLMXXIII', 'MMMLMXXIV', 'MMMLMXXV', 'MMMLMXXVI', 'MMMLMXXVII', 'MMMLMXXVIII', 'MMMLMXXIX', 'MMMLMXXX', 'MMMLMXXXI', 'MMMLMXXXII', 'MMMLMXXXIII', 'MMMLMXXXIV', 'MMMLMXXXV', 'MMMLMXXXVI', 'MMMLMXXXVII', 'MMMLMXXXVIII', 'MMMLMXXXIX', 'MMMXM', 'MMMXMI', 'MMMXMII', 'MMMXMIII', 'MMMXMIV', 'MMMVM', 'MMMVMI', 'MMMVMII', 'MMMVMIII', 'MMMVMIV', ] -const mode4 = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX', 'XXI', 'XXII', 'XXIII', 'XXIV', 'XXV', 'XXVI', 'XXVII', 'XXVIII', 'XXIX', 'XXX', 'XXXI', 'XXXII', 'XXXIII', 'XXXIV', 'XXXV', 'XXXVI', 'XXXVII', 'XXXVIII', 'XXXIX', 'XL', 'XLI', 'XLII', 'XLIII', 'XLIV', 'VL', 'VLI', 'VLII', 'VLIII', 'IL', 'L', 'LI', 'LII', 'LIII', 'LIV', 'LV', 'LVI', 'LVII', 'LVIII', 'LIX', 'LX', 'LXI', 'LXII', 'LXIII', 'LXIV', 'LXV', 'LXVI', 'LXVII', 'LXVIII', 'LXIX', 'LXX', 'LXXI', 'LXXII', 'LXXIII', 'LXXIV', 'LXXV', 'LXXVI', 'LXXVII', 'LXXVIII', 'LXXIX', 'LXXX', 'LXXXI', 'LXXXII', 'LXXXIII', 'LXXXIV', 'LXXXV', 'LXXXVI', 'LXXXVII', 'LXXXVIII', 'LXXXIX', 'XC', 'XCI', 'XCII', 'XCIII', 'XCIV', 'VC', 'VCI', 'VCII', 'VCIII', 'IC', 'C', 'CI', 'CII', 'CIII', 'CIV', 'CV', 'CVI', 'CVII', 'CVIII', 'CIX', 'CX', 'CXI', 'CXII', 'CXIII', 'CXIV', 'CXV', 'CXVI', 'CXVII', 'CXVIII', 'CXIX', 'CXX', 'CXXI', 'CXXII', 'CXXIII', 'CXXIV', 'CXXV', 'CXXVI', 'CXXVII', 'CXXVIII', 'CXXIX', 'CXXX', 'CXXXI', 'CXXXII', 'CXXXIII', 'CXXXIV', 'CXXXV', 'CXXXVI', 'CXXXVII', 'CXXXVIII', 'CXXXIX', 'CXL', 'CXLI', 'CXLII', 'CXLIII', 'CXLIV', 'CVL', 'CVLI', 'CVLII', 'CVLIII', 'CIL', 'CL', 'CLI', 'CLII', 'CLIII', 'CLIV', 'CLV', 'CLVI', 'CLVII', 'CLVIII', 'CLIX', 'CLX', 'CLXI', 'CLXII', 'CLXIII', 'CLXIV', 'CLXV', 'CLXVI', 'CLXVII', 'CLXVIII', 'CLXIX', 'CLXX', 'CLXXI', 'CLXXII', 'CLXXIII', 'CLXXIV', 'CLXXV', 'CLXXVI', 'CLXXVII', 'CLXXVIII', 'CLXXIX', 'CLXXX', 'CLXXXI', 'CLXXXII', 'CLXXXIII', 'CLXXXIV', 'CLXXXV', 'CLXXXVI', 'CLXXXVII', 'CLXXXVIII', 'CLXXXIX', 'CXC', 'CXCI', 'CXCII', 'CXCIII', 'CXCIV', 'CVC', 'CVCI', 'CVCII', 'CVCIII', 'CIC', 'CC', 'CCI', 'CCII', 'CCIII', 'CCIV', 'CCV', 'CCVI', 'CCVII', 'CCVIII', 'CCIX', 'CCX', 'CCXI', 'CCXII', 'CCXIII', 'CCXIV', 'CCXV', 'CCXVI', 'CCXVII', 'CCXVIII', 'CCXIX', 'CCXX', 'CCXXI', 'CCXXII', 'CCXXIII', 'CCXXIV', 'CCXXV', 'CCXXVI', 'CCXXVII', 'CCXXVIII', 'CCXXIX', 'CCXXX', 'CCXXXI', 'CCXXXII', 'CCXXXIII', 'CCXXXIV', 'CCXXXV', 'CCXXXVI', 'CCXXXVII', 'CCXXXVIII', 'CCXXXIX', 'CCXL', 'CCXLI', 'CCXLII', 'CCXLIII', 'CCXLIV', 'CCVL', 'CCVLI', 'CCVLII', 'CCVLIII', 'CCIL', 'CCL', 'CCLI', 'CCLII', 'CCLIII', 'CCLIV', 'CCLV', 'CCLVI', 'CCLVII', 'CCLVIII', 'CCLIX', 'CCLX', 'CCLXI', 'CCLXII', 'CCLXIII', 'CCLXIV', 'CCLXV', 'CCLXVI', 'CCLXVII', 'CCLXVIII', 'CCLXIX', 'CCLXX', 'CCLXXI', 'CCLXXII', 'CCLXXIII', 'CCLXXIV', 'CCLXXV', 'CCLXXVI', 'CCLXXVII', 'CCLXXVIII', 'CCLXXIX', 'CCLXXX', 'CCLXXXI', 'CCLXXXII', 'CCLXXXIII', 'CCLXXXIV', 'CCLXXXV', 'CCLXXXVI', 'CCLXXXVII', 'CCLXXXVIII', 'CCLXXXIX', 'CCXC', 'CCXCI', 'CCXCII', 'CCXCIII', 'CCXCIV', 'CCVC', 'CCVCI', 'CCVCII', 'CCVCIII', 'CCIC', 'CCC', 'CCCI', 'CCCII', 'CCCIII', 'CCCIV', 'CCCV', 'CCCVI', 'CCCVII', 'CCCVIII', 'CCCIX', 'CCCX', 'CCCXI', 'CCCXII', 'CCCXIII', 'CCCXIV', 'CCCXV', 'CCCXVI', 'CCCXVII', 'CCCXVIII', 'CCCXIX', 'CCCXX', 'CCCXXI', 'CCCXXII', 'CCCXXIII', 'CCCXXIV', 'CCCXXV', 'CCCXXVI', 'CCCXXVII', 'CCCXXVIII', 'CCCXXIX', 'CCCXXX', 'CCCXXXI', 'CCCXXXII', 'CCCXXXIII', 'CCCXXXIV', 'CCCXXXV', 'CCCXXXVI', 'CCCXXXVII', 'CCCXXXVIII', 'CCCXXXIX', 'CCCXL', 'CCCXLI', 'CCCXLII', 'CCCXLIII', 'CCCXLIV', 'CCCVL', 'CCCVLI', 'CCCVLII', 'CCCVLIII', 'CCCIL', 'CCCL', 'CCCLI', 'CCCLII', 'CCCLIII', 'CCCLIV', 'CCCLV', 'CCCLVI', 'CCCLVII', 'CCCLVIII', 'CCCLIX', 'CCCLX', 'CCCLXI', 'CCCLXII', 'CCCLXIII', 'CCCLXIV', 'CCCLXV', 'CCCLXVI', 'CCCLXVII', 'CCCLXVIII', 'CCCLXIX', 'CCCLXX', 'CCCLXXI', 'CCCLXXII', 'CCCLXXIII', 'CCCLXXIV', 'CCCLXXV', 'CCCLXXVI', 'CCCLXXVII', 'CCCLXXVIII', 'CCCLXXIX', 'CCCLXXX', 'CCCLXXXI', 'CCCLXXXII', 'CCCLXXXIII', 'CCCLXXXIV', 'CCCLXXXV', 'CCCLXXXVI', 'CCCLXXXVII', 'CCCLXXXVIII', 'CCCLXXXIX', 'CCCXC', 'CCCXCI', 'CCCXCII', 'CCCXCIII', 'CCCXCIV', 'CCCVC', 'CCCVCI', 'CCCVCII', 'CCCVCIII', 'CCCIC', 'CD', 'CDI', 'CDII', 'CDIII', 'CDIV', 'CDV', 'CDVI', 'CDVII', 'CDVIII', 'CDIX', 'CDX', 'CDXI', 'CDXII', 'CDXIII', 'CDXIV', 'CDXV', 'CDXVI', 'CDXVII', 'CDXVIII', 'CDXIX', 'CDXX', 'CDXXI', 'CDXXII', 'CDXXIII', 'CDXXIV', 'CDXXV', 'CDXXVI', 'CDXXVII', 'CDXXVIII', 'CDXXIX', 'CDXXX', 'CDXXXI', 'CDXXXII', 'CDXXXIII', 'CDXXXIV', 'CDXXXV', 'CDXXXVI', 'CDXXXVII', 'CDXXXVIII', 'CDXXXIX', 'CDXL', 'CDXLI', 'CDXLII', 'CDXLIII', 'CDXLIV', 'CDVL', 'CDVLI', 'CDVLII', 'CDVLIII', 'CDIL', 'LD', 'LDI', 'LDII', 'LDIII', 'LDIV', 'LDV', 'LDVI', 'LDVII', 'LDVIII', 'LDIX', 'LDX', 'LDXI', 'LDXII', 'LDXIII', 'LDXIV', 'LDXV', 'LDXVI', 'LDXVII', 'LDXVIII', 'LDXIX', 'LDXX', 'LDXXI', 'LDXXII', 'LDXXIII', 'LDXXIV', 'LDXXV', 'LDXXVI', 'LDXXVII', 'LDXXVIII', 'LDXXIX', 'LDXXX', 'LDXXXI', 'LDXXXII', 'LDXXXIII', 'LDXXXIV', 'LDXXXV', 'LDXXXVI', 'LDXXXVII', 'LDXXXVIII', 'LDXXXIX', 'XD', 'XDI', 'XDII', 'XDIII', 'XDIV', 'VD', 'VDI', 'VDII', 'VDIII', 'ID', 'D', 'DI', 'DII', 'DIII', 'DIV', 'DV', 'DVI', 'DVII', 'DVIII', 'DIX', 'DX', 'DXI', 'DXII', 'DXIII', 'DXIV', 'DXV', 'DXVI', 'DXVII', 'DXVIII', 'DXIX', 'DXX', 'DXXI', 'DXXII', 'DXXIII', 'DXXIV', 'DXXV', 'DXXVI', 'DXXVII', 'DXXVIII', 'DXXIX', 'DXXX', 'DXXXI', 'DXXXII', 'DXXXIII', 'DXXXIV', 'DXXXV', 'DXXXVI', 'DXXXVII', 'DXXXVIII', 'DXXXIX', 'DXL', 'DXLI', 'DXLII', 'DXLIII', 'DXLIV', 'DVL', 'DVLI', 'DVLII', 'DVLIII', 'DIL', 'DL', 'DLI', 'DLII', 'DLIII', 'DLIV', 'DLV', 'DLVI', 'DLVII', 'DLVIII', 'DLIX', 'DLX', 'DLXI', 'DLXII', 'DLXIII', 'DLXIV', 'DLXV', 'DLXVI', 'DLXVII', 'DLXVIII', 'DLXIX', 'DLXX', 'DLXXI', 'DLXXII', 'DLXXIII', 'DLXXIV', 'DLXXV', 'DLXXVI', 'DLXXVII', 'DLXXVIII', 'DLXXIX', 'DLXXX', 'DLXXXI', 'DLXXXII', 'DLXXXIII', 'DLXXXIV', 'DLXXXV', 'DLXXXVI', 'DLXXXVII', 'DLXXXVIII', 'DLXXXIX', 'DXC', 'DXCI', 'DXCII', 'DXCIII', 'DXCIV', 'DVC', 'DVCI', 'DVCII', 'DVCIII', 'DIC', 'DC', 'DCI', 'DCII', 'DCIII', 'DCIV', 'DCV', 'DCVI', 'DCVII', 'DCVIII', 'DCIX', 'DCX', 'DCXI', 'DCXII', 'DCXIII', 'DCXIV', 'DCXV', 'DCXVI', 'DCXVII', 'DCXVIII', 'DCXIX', 'DCXX', 'DCXXI', 'DCXXII', 'DCXXIII', 'DCXXIV', 'DCXXV', 'DCXXVI', 'DCXXVII', 'DCXXVIII', 'DCXXIX', 'DCXXX', 'DCXXXI', 'DCXXXII', 'DCXXXIII', 'DCXXXIV', 'DCXXXV', 'DCXXXVI', 'DCXXXVII', 'DCXXXVIII', 'DCXXXIX', 'DCXL', 'DCXLI', 'DCXLII', 'DCXLIII', 'DCXLIV', 'DCVL', 'DCVLI', 'DCVLII', 'DCVLIII', 'DCIL', 'DCL', 'DCLI', 'DCLII', 'DCLIII', 'DCLIV', 'DCLV', 'DCLVI', 'DCLVII', 'DCLVIII', 'DCLIX', 'DCLX', 'DCLXI', 'DCLXII', 'DCLXIII', 'DCLXIV', 'DCLXV', 'DCLXVI', 'DCLXVII', 'DCLXVIII', 'DCLXIX', 'DCLXX', 'DCLXXI', 'DCLXXII', 'DCLXXIII', 'DCLXXIV', 'DCLXXV', 'DCLXXVI', 'DCLXXVII', 'DCLXXVIII', 'DCLXXIX', 'DCLXXX', 'DCLXXXI', 'DCLXXXII', 'DCLXXXIII', 'DCLXXXIV', 'DCLXXXV', 'DCLXXXVI', 'DCLXXXVII', 'DCLXXXVIII', 'DCLXXXIX', 'DCXC', 'DCXCI', 'DCXCII', 'DCXCIII', 'DCXCIV', 'DCVC', 'DCVCI', 'DCVCII', 'DCVCIII', 'DCIC', 'DCC', 'DCCI', 'DCCII', 'DCCIII', 'DCCIV', 'DCCV', 'DCCVI', 'DCCVII', 'DCCVIII', 'DCCIX', 'DCCX', 'DCCXI', 'DCCXII', 'DCCXIII', 'DCCXIV', 'DCCXV', 'DCCXVI', 'DCCXVII', 'DCCXVIII', 'DCCXIX', 'DCCXX', 'DCCXXI', 'DCCXXII', 'DCCXXIII', 'DCCXXIV', 'DCCXXV', 'DCCXXVI', 'DCCXXVII', 'DCCXXVIII', 'DCCXXIX', 'DCCXXX', 'DCCXXXI', 'DCCXXXII', 'DCCXXXIII', 'DCCXXXIV', 'DCCXXXV', 'DCCXXXVI', 'DCCXXXVII', 'DCCXXXVIII', 'DCCXXXIX', 'DCCXL', 'DCCXLI', 'DCCXLII', 'DCCXLIII', 'DCCXLIV', 'DCCVL', 'DCCVLI', 'DCCVLII', 'DCCVLIII', 'DCCIL', 'DCCL', 'DCCLI', 'DCCLII', 'DCCLIII', 'DCCLIV', 'DCCLV', 'DCCLVI', 'DCCLVII', 'DCCLVIII', 'DCCLIX', 'DCCLX', 'DCCLXI', 'DCCLXII', 'DCCLXIII', 'DCCLXIV', 'DCCLXV', 'DCCLXVI', 'DCCLXVII', 'DCCLXVIII', 'DCCLXIX', 'DCCLXX', 'DCCLXXI', 'DCCLXXII', 'DCCLXXIII', 'DCCLXXIV', 'DCCLXXV', 'DCCLXXVI', 'DCCLXXVII', 'DCCLXXVIII', 'DCCLXXIX', 'DCCLXXX', 'DCCLXXXI', 'DCCLXXXII', 'DCCLXXXIII', 'DCCLXXXIV', 'DCCLXXXV', 'DCCLXXXVI', 'DCCLXXXVII', 'DCCLXXXVIII', 'DCCLXXXIX', 'DCCXC', 'DCCXCI', 'DCCXCII', 'DCCXCIII', 'DCCXCIV', 'DCCVC', 'DCCVCI', 'DCCVCII', 'DCCVCIII', 'DCCIC', 'DCCC', 'DCCCI', 'DCCCII', 'DCCCIII', 'DCCCIV', 'DCCCV', 'DCCCVI', 'DCCCVII', 'DCCCVIII', 'DCCCIX', 'DCCCX', 'DCCCXI', 'DCCCXII', 'DCCCXIII', 'DCCCXIV', 'DCCCXV', 'DCCCXVI', 'DCCCXVII', 'DCCCXVIII', 'DCCCXIX', 'DCCCXX', 'DCCCXXI', 'DCCCXXII', 'DCCCXXIII', 'DCCCXXIV', 'DCCCXXV', 'DCCCXXVI', 'DCCCXXVII', 'DCCCXXVIII', 'DCCCXXIX', 'DCCCXXX', 'DCCCXXXI', 'DCCCXXXII', 'DCCCXXXIII', 'DCCCXXXIV', 'DCCCXXXV', 'DCCCXXXVI', 'DCCCXXXVII', 'DCCCXXXVIII', 'DCCCXXXIX', 'DCCCXL', 'DCCCXLI', 'DCCCXLII', 'DCCCXLIII', 'DCCCXLIV', 'DCCCVL', 'DCCCVLI', 'DCCCVLII', 'DCCCVLIII', 'DCCCIL', 'DCCCL', 'DCCCLI', 'DCCCLII', 'DCCCLIII', 'DCCCLIV', 'DCCCLV', 'DCCCLVI', 'DCCCLVII', 'DCCCLVIII', 'DCCCLIX', 'DCCCLX', 'DCCCLXI', 'DCCCLXII', 'DCCCLXIII', 'DCCCLXIV', 'DCCCLXV', 'DCCCLXVI', 'DCCCLXVII', 'DCCCLXVIII', 'DCCCLXIX', 'DCCCLXX', 'DCCCLXXI', 'DCCCLXXII', 'DCCCLXXIII', 'DCCCLXXIV', 'DCCCLXXV', 'DCCCLXXVI', 'DCCCLXXVII', 'DCCCLXXVIII', 'DCCCLXXIX', 'DCCCLXXX', 'DCCCLXXXI', 'DCCCLXXXII', 'DCCCLXXXIII', 'DCCCLXXXIV', 'DCCCLXXXV', 'DCCCLXXXVI', 'DCCCLXXXVII', 'DCCCLXXXVIII', 'DCCCLXXXIX', 'DCCCXC', 'DCCCXCI', 'DCCCXCII', 'DCCCXCIII', 'DCCCXCIV', 'DCCCVC', 'DCCCVCI', 'DCCCVCII', 'DCCCVCIII', 'DCCCIC', 'CM', 'CMI', 'CMII', 'CMIII', 'CMIV', 'CMV', 'CMVI', 'CMVII', 'CMVIII', 'CMIX', 'CMX', 'CMXI', 'CMXII', 'CMXIII', 'CMXIV', 'CMXV', 'CMXVI', 'CMXVII', 'CMXVIII', 'CMXIX', 'CMXX', 'CMXXI', 'CMXXII', 'CMXXIII', 'CMXXIV', 'CMXXV', 'CMXXVI', 'CMXXVII', 'CMXXVIII', 'CMXXIX', 'CMXXX', 'CMXXXI', 'CMXXXII', 'CMXXXIII', 'CMXXXIV', 'CMXXXV', 'CMXXXVI', 'CMXXXVII', 'CMXXXVIII', 'CMXXXIX', 'CMXL', 'CMXLI', 'CMXLII', 'CMXLIII', 'CMXLIV', 'CMVL', 'CMVLI', 'CMVLII', 'CMVLIII', 'CMIL', 'LM', 'LMI', 'LMII', 'LMIII', 'LMIV', 'LMV', 'LMVI', 'LMVII', 'LMVIII', 'LMIX', 'LMX', 'LMXI', 'LMXII', 'LMXIII', 'LMXIV', 'LMXV', 'LMXVI', 'LMXVII', 'LMXVIII', 'LMXIX', 'LMXX', 'LMXXI', 'LMXXII', 'LMXXIII', 'LMXXIV', 'LMXXV', 'LMXXVI', 'LMXXVII', 'LMXXVIII', 'LMXXIX', 'LMXXX', 'LMXXXI', 'LMXXXII', 'LMXXXIII', 'LMXXXIV', 'LMXXXV', 'LMXXXVI', 'LMXXXVII', 'LMXXXVIII', 'LMXXXIX', 'XM', 'XMI', 'XMII', 'XMIII', 'XMIV', 'VM', 'VMI', 'VMII', 'VMIII', 'IM', 'M', 'MI', 'MII', 'MIII', 'MIV', 'MV', 'MVI', 'MVII', 'MVIII', 'MIX', 'MX', 'MXI', 'MXII', 'MXIII', 'MXIV', 'MXV', 'MXVI', 'MXVII', 'MXVIII', 'MXIX', 'MXX', 'MXXI', 'MXXII', 'MXXIII', 'MXXIV', 'MXXV', 'MXXVI', 'MXXVII', 'MXXVIII', 'MXXIX', 'MXXX', 'MXXXI', 'MXXXII', 'MXXXIII', 'MXXXIV', 'MXXXV', 'MXXXVI', 'MXXXVII', 'MXXXVIII', 'MXXXIX', 'MXL', 'MXLI', 'MXLII', 'MXLIII', 'MXLIV', 'MVL', 'MVLI', 'MVLII', 'MVLIII', 'MIL', 'ML', 'MLI', 'MLII', 'MLIII', 'MLIV', 'MLV', 'MLVI', 'MLVII', 'MLVIII', 'MLIX', 'MLX', 'MLXI', 'MLXII', 'MLXIII', 'MLXIV', 'MLXV', 'MLXVI', 'MLXVII', 'MLXVIII', 'MLXIX', 'MLXX', 'MLXXI', 'MLXXII', 'MLXXIII', 'MLXXIV', 'MLXXV', 'MLXXVI', 'MLXXVII', 'MLXXVIII', 'MLXXIX', 'MLXXX', 'MLXXXI', 'MLXXXII', 'MLXXXIII', 'MLXXXIV', 'MLXXXV', 'MLXXXVI', 'MLXXXVII', 'MLXXXVIII', 'MLXXXIX', 'MXC', 'MXCI', 'MXCII', 'MXCIII', 'MXCIV', 'MVC', 'MVCI', 'MVCII', 'MVCIII', 'MIC', 'MC', 'MCI', 'MCII', 'MCIII', 'MCIV', 'MCV', 'MCVI', 'MCVII', 'MCVIII', 'MCIX', 'MCX', 'MCXI', 'MCXII', 'MCXIII', 'MCXIV', 'MCXV', 'MCXVI', 'MCXVII', 'MCXVIII', 'MCXIX', 'MCXX', 'MCXXI', 'MCXXII', 'MCXXIII', 'MCXXIV', 'MCXXV', 'MCXXVI', 'MCXXVII', 'MCXXVIII', 'MCXXIX', 'MCXXX', 'MCXXXI', 'MCXXXII', 'MCXXXIII', 'MCXXXIV', 'MCXXXV', 'MCXXXVI', 'MCXXXVII', 'MCXXXVIII', 'MCXXXIX', 'MCXL', 'MCXLI', 'MCXLII', 'MCXLIII', 'MCXLIV', 'MCVL', 'MCVLI', 'MCVLII', 'MCVLIII', 'MCIL', 'MCL', 'MCLI', 'MCLII', 'MCLIII', 'MCLIV', 'MCLV', 'MCLVI', 'MCLVII', 'MCLVIII', 'MCLIX', 'MCLX', 'MCLXI', 'MCLXII', 'MCLXIII', 'MCLXIV', 'MCLXV', 'MCLXVI', 'MCLXVII', 'MCLXVIII', 'MCLXIX', 'MCLXX', 'MCLXXI', 'MCLXXII', 'MCLXXIII', 'MCLXXIV', 'MCLXXV', 'MCLXXVI', 'MCLXXVII', 'MCLXXVIII', 'MCLXXIX', 'MCLXXX', 'MCLXXXI', 'MCLXXXII', 'MCLXXXIII', 'MCLXXXIV', 'MCLXXXV', 'MCLXXXVI', 'MCLXXXVII', 'MCLXXXVIII', 'MCLXXXIX', 'MCXC', 'MCXCI', 'MCXCII', 'MCXCIII', 'MCXCIV', 'MCVC', 'MCVCI', 'MCVCII', 'MCVCIII', 'MCIC', 'MCC', 'MCCI', 'MCCII', 'MCCIII', 'MCCIV', 'MCCV', 'MCCVI', 'MCCVII', 'MCCVIII', 'MCCIX', 'MCCX', 'MCCXI', 'MCCXII', 'MCCXIII', 'MCCXIV', 'MCCXV', 'MCCXVI', 'MCCXVII', 'MCCXVIII', 'MCCXIX', 'MCCXX', 'MCCXXI', 'MCCXXII', 'MCCXXIII', 'MCCXXIV', 'MCCXXV', 'MCCXXVI', 'MCCXXVII', 'MCCXXVIII', 'MCCXXIX', 'MCCXXX', 'MCCXXXI', 'MCCXXXII', 'MCCXXXIII', 'MCCXXXIV', 'MCCXXXV', 'MCCXXXVI', 'MCCXXXVII', 'MCCXXXVIII', 'MCCXXXIX', 'MCCXL', 'MCCXLI', 'MCCXLII', 'MCCXLIII', 'MCCXLIV', 'MCCVL', 'MCCVLI', 'MCCVLII', 'MCCVLIII', 'MCCIL', 'MCCL', 'MCCLI', 'MCCLII', 'MCCLIII', 'MCCLIV', 'MCCLV', 'MCCLVI', 'MCCLVII', 'MCCLVIII', 'MCCLIX', 'MCCLX', 'MCCLXI', 'MCCLXII', 'MCCLXIII', 'MCCLXIV', 'MCCLXV', 'MCCLXVI', 'MCCLXVII', 'MCCLXVIII', 'MCCLXIX', 'MCCLXX', 'MCCLXXI', 'MCCLXXII', 'MCCLXXIII', 'MCCLXXIV', 'MCCLXXV', 'MCCLXXVI', 'MCCLXXVII', 'MCCLXXVIII', 'MCCLXXIX', 'MCCLXXX', 'MCCLXXXI', 'MCCLXXXII', 'MCCLXXXIII', 'MCCLXXXIV', 'MCCLXXXV', 'MCCLXXXVI', 'MCCLXXXVII', 'MCCLXXXVIII', 'MCCLXXXIX', 'MCCXC', 'MCCXCI', 'MCCXCII', 'MCCXCIII', 'MCCXCIV', 'MCCVC', 'MCCVCI', 'MCCVCII', 'MCCVCIII', 'MCCIC', 'MCCC', 'MCCCI', 'MCCCII', 'MCCCIII', 'MCCCIV', 'MCCCV', 'MCCCVI', 'MCCCVII', 'MCCCVIII', 'MCCCIX', 'MCCCX', 'MCCCXI', 'MCCCXII', 'MCCCXIII', 'MCCCXIV', 'MCCCXV', 'MCCCXVI', 'MCCCXVII', 'MCCCXVIII', 'MCCCXIX', 'MCCCXX', 'MCCCXXI', 'MCCCXXII', 'MCCCXXIII', 'MCCCXXIV', 'MCCCXXV', 'MCCCXXVI', 'MCCCXXVII', 'MCCCXXVIII', 'MCCCXXIX', 'MCCCXXX', 'MCCCXXXI', 'MCCCXXXII', 'MCCCXXXIII', 'MCCCXXXIV', 'MCCCXXXV', 'MCCCXXXVI', 'MCCCXXXVII', 'MCCCXXXVIII', 'MCCCXXXIX', 'MCCCXL', 'MCCCXLI', 'MCCCXLII', 'MCCCXLIII', 'MCCCXLIV', 'MCCCVL', 'MCCCVLI', 'MCCCVLII', 'MCCCVLIII', 'MCCCIL', 'MCCCL', 'MCCCLI', 'MCCCLII', 'MCCCLIII', 'MCCCLIV', 'MCCCLV', 'MCCCLVI', 'MCCCLVII', 'MCCCLVIII', 'MCCCLIX', 'MCCCLX', 'MCCCLXI', 'MCCCLXII', 'MCCCLXIII', 'MCCCLXIV', 'MCCCLXV', 'MCCCLXVI', 'MCCCLXVII', 'MCCCLXVIII', 'MCCCLXIX', 'MCCCLXX', 'MCCCLXXI', 'MCCCLXXII', 'MCCCLXXIII', 'MCCCLXXIV', 'MCCCLXXV', 'MCCCLXXVI', 'MCCCLXXVII', 'MCCCLXXVIII', 'MCCCLXXIX', 'MCCCLXXX', 'MCCCLXXXI', 'MCCCLXXXII', 'MCCCLXXXIII', 'MCCCLXXXIV', 'MCCCLXXXV', 'MCCCLXXXVI', 'MCCCLXXXVII', 'MCCCLXXXVIII', 'MCCCLXXXIX', 'MCCCXC', 'MCCCXCI', 'MCCCXCII', 'MCCCXCIII', 'MCCCXCIV', 'MCCCVC', 'MCCCVCI', 'MCCCVCII', 'MCCCVCIII', 'MCCCIC', 'MCD', 'MCDI', 'MCDII', 'MCDIII', 'MCDIV', 'MCDV', 'MCDVI', 'MCDVII', 'MCDVIII', 'MCDIX', 'MCDX', 'MCDXI', 'MCDXII', 'MCDXIII', 'MCDXIV', 'MCDXV', 'MCDXVI', 'MCDXVII', 'MCDXVIII', 'MCDXIX', 'MCDXX', 'MCDXXI', 'MCDXXII', 'MCDXXIII', 'MCDXXIV', 'MCDXXV', 'MCDXXVI', 'MCDXXVII', 'MCDXXVIII', 'MCDXXIX', 'MCDXXX', 'MCDXXXI', 'MCDXXXII', 'MCDXXXIII', 'MCDXXXIV', 'MCDXXXV', 'MCDXXXVI', 'MCDXXXVII', 'MCDXXXVIII', 'MCDXXXIX', 'MCDXL', 'MCDXLI', 'MCDXLII', 'MCDXLIII', 'MCDXLIV', 'MCDVL', 'MCDVLI', 'MCDVLII', 'MCDVLIII', 'MCDIL', 'MLD', 'MLDI', 'MLDII', 'MLDIII', 'MLDIV', 'MLDV', 'MLDVI', 'MLDVII', 'MLDVIII', 'MLDIX', 'MLDX', 'MLDXI', 'MLDXII', 'MLDXIII', 'MLDXIV', 'MLDXV', 'MLDXVI', 'MLDXVII', 'MLDXVIII', 'MLDXIX', 'MLDXX', 'MLDXXI', 'MLDXXII', 'MLDXXIII', 'MLDXXIV', 'MLDXXV', 'MLDXXVI', 'MLDXXVII', 'MLDXXVIII', 'MLDXXIX', 'MLDXXX', 'MLDXXXI', 'MLDXXXII', 'MLDXXXIII', 'MLDXXXIV', 'MLDXXXV', 'MLDXXXVI', 'MLDXXXVII', 'MLDXXXVIII', 'MLDXXXIX', 'MXD', 'MXDI', 'MXDII', 'MXDIII', 'MXDIV', 'MVD', 'MVDI', 'MVDII', 'MVDIII', 'MID', 'MD', 'MDI', 'MDII', 'MDIII', 'MDIV', 'MDV', 'MDVI', 'MDVII', 'MDVIII', 'MDIX', 'MDX', 'MDXI', 'MDXII', 'MDXIII', 'MDXIV', 'MDXV', 'MDXVI', 'MDXVII', 'MDXVIII', 'MDXIX', 'MDXX', 'MDXXI', 'MDXXII', 'MDXXIII', 'MDXXIV', 'MDXXV', 'MDXXVI', 'MDXXVII', 'MDXXVIII', 'MDXXIX', 'MDXXX', 'MDXXXI', 'MDXXXII', 'MDXXXIII', 'MDXXXIV', 'MDXXXV', 'MDXXXVI', 'MDXXXVII', 'MDXXXVIII', 'MDXXXIX', 'MDXL', 'MDXLI', 'MDXLII', 'MDXLIII', 'MDXLIV', 'MDVL', 'MDVLI', 'MDVLII', 'MDVLIII', 'MDIL', 'MDL', 'MDLI', 'MDLII', 'MDLIII', 'MDLIV', 'MDLV', 'MDLVI', 'MDLVII', 'MDLVIII', 'MDLIX', 'MDLX', 'MDLXI', 'MDLXII', 'MDLXIII', 'MDLXIV', 'MDLXV', 'MDLXVI', 'MDLXVII', 'MDLXVIII', 'MDLXIX', 'MDLXX', 'MDLXXI', 'MDLXXII', 'MDLXXIII', 'MDLXXIV', 'MDLXXV', 'MDLXXVI', 'MDLXXVII', 'MDLXXVIII', 'MDLXXIX', 'MDLXXX', 'MDLXXXI', 'MDLXXXII', 'MDLXXXIII', 'MDLXXXIV', 'MDLXXXV', 'MDLXXXVI', 'MDLXXXVII', 'MDLXXXVIII', 'MDLXXXIX', 'MDXC', 'MDXCI', 'MDXCII', 'MDXCIII', 'MDXCIV', 'MDVC', 'MDVCI', 'MDVCII', 'MDVCIII', 'MDIC', 'MDC', 'MDCI', 'MDCII', 'MDCIII', 'MDCIV', 'MDCV', 'MDCVI', 'MDCVII', 'MDCVIII', 'MDCIX', 'MDCX', 'MDCXI', 'MDCXII', 'MDCXIII', 'MDCXIV', 'MDCXV', 'MDCXVI', 'MDCXVII', 'MDCXVIII', 'MDCXIX', 'MDCXX', 'MDCXXI', 'MDCXXII', 'MDCXXIII', 'MDCXXIV', 'MDCXXV', 'MDCXXVI', 'MDCXXVII', 'MDCXXVIII', 'MDCXXIX', 'MDCXXX', 'MDCXXXI', 'MDCXXXII', 'MDCXXXIII', 'MDCXXXIV', 'MDCXXXV', 'MDCXXXVI', 'MDCXXXVII', 'MDCXXXVIII', 'MDCXXXIX', 'MDCXL', 'MDCXLI', 'MDCXLII', 'MDCXLIII', 'MDCXLIV', 'MDCVL', 'MDCVLI', 'MDCVLII', 'MDCVLIII', 'MDCIL', 'MDCL', 'MDCLI', 'MDCLII', 'MDCLIII', 'MDCLIV', 'MDCLV', 'MDCLVI', 'MDCLVII', 'MDCLVIII', 'MDCLIX', 'MDCLX', 'MDCLXI', 'MDCLXII', 'MDCLXIII', 'MDCLXIV', 'MDCLXV', 'MDCLXVI', 'MDCLXVII', 'MDCLXVIII', 'MDCLXIX', 'MDCLXX', 'MDCLXXI', 'MDCLXXII', 'MDCLXXIII', 'MDCLXXIV', 'MDCLXXV', 'MDCLXXVI', 'MDCLXXVII', 'MDCLXXVIII', 'MDCLXXIX', 'MDCLXXX', 'MDCLXXXI', 'MDCLXXXII', 'MDCLXXXIII', 'MDCLXXXIV', 'MDCLXXXV', 'MDCLXXXVI', 'MDCLXXXVII', 'MDCLXXXVIII', 'MDCLXXXIX', 'MDCXC', 'MDCXCI', 'MDCXCII', 'MDCXCIII', 'MDCXCIV', 'MDCVC', 'MDCVCI', 'MDCVCII', 'MDCVCIII', 'MDCIC', 'MDCC', 'MDCCI', 'MDCCII', 'MDCCIII', 'MDCCIV', 'MDCCV', 'MDCCVI', 'MDCCVII', 'MDCCVIII', 'MDCCIX', 'MDCCX', 'MDCCXI', 'MDCCXII', 'MDCCXIII', 'MDCCXIV', 'MDCCXV', 'MDCCXVI', 'MDCCXVII', 'MDCCXVIII', 'MDCCXIX', 'MDCCXX', 'MDCCXXI', 'MDCCXXII', 'MDCCXXIII', 'MDCCXXIV', 'MDCCXXV', 'MDCCXXVI', 'MDCCXXVII', 'MDCCXXVIII', 'MDCCXXIX', 'MDCCXXX', 'MDCCXXXI', 'MDCCXXXII', 'MDCCXXXIII', 'MDCCXXXIV', 'MDCCXXXV', 'MDCCXXXVI', 'MDCCXXXVII', 'MDCCXXXVIII', 'MDCCXXXIX', 'MDCCXL', 'MDCCXLI', 'MDCCXLII', 'MDCCXLIII', 'MDCCXLIV', 'MDCCVL', 'MDCCVLI', 'MDCCVLII', 'MDCCVLIII', 'MDCCIL', 'MDCCL', 'MDCCLI', 'MDCCLII', 'MDCCLIII', 'MDCCLIV', 'MDCCLV', 'MDCCLVI', 'MDCCLVII', 'MDCCLVIII', 'MDCCLIX', 'MDCCLX', 'MDCCLXI', 'MDCCLXII', 'MDCCLXIII', 'MDCCLXIV', 'MDCCLXV', 'MDCCLXVI', 'MDCCLXVII', 'MDCCLXVIII', 'MDCCLXIX', 'MDCCLXX', 'MDCCLXXI', 'MDCCLXXII', 'MDCCLXXIII', 'MDCCLXXIV', 'MDCCLXXV', 'MDCCLXXVI', 'MDCCLXXVII', 'MDCCLXXVIII', 'MDCCLXXIX', 'MDCCLXXX', 'MDCCLXXXI', 'MDCCLXXXII', 'MDCCLXXXIII', 'MDCCLXXXIV', 'MDCCLXXXV', 'MDCCLXXXVI', 'MDCCLXXXVII', 'MDCCLXXXVIII', 'MDCCLXXXIX', 'MDCCXC', 'MDCCXCI', 'MDCCXCII', 'MDCCXCIII', 'MDCCXCIV', 'MDCCVC', 'MDCCVCI', 'MDCCVCII', 'MDCCVCIII', 'MDCCIC', 'MDCCC', 'MDCCCI', 'MDCCCII', 'MDCCCIII', 'MDCCCIV', 'MDCCCV', 'MDCCCVI', 'MDCCCVII', 'MDCCCVIII', 'MDCCCIX', 'MDCCCX', 'MDCCCXI', 'MDCCCXII', 'MDCCCXIII', 'MDCCCXIV', 'MDCCCXV', 'MDCCCXVI', 'MDCCCXVII', 'MDCCCXVIII', 'MDCCCXIX', 'MDCCCXX', 'MDCCCXXI', 'MDCCCXXII', 'MDCCCXXIII', 'MDCCCXXIV', 'MDCCCXXV', 'MDCCCXXVI', 'MDCCCXXVII', 'MDCCCXXVIII', 'MDCCCXXIX', 'MDCCCXXX', 'MDCCCXXXI', 'MDCCCXXXII', 'MDCCCXXXIII', 'MDCCCXXXIV', 'MDCCCXXXV', 'MDCCCXXXVI', 'MDCCCXXXVII', 'MDCCCXXXVIII', 'MDCCCXXXIX', 'MDCCCXL', 'MDCCCXLI', 'MDCCCXLII', 'MDCCCXLIII', 'MDCCCXLIV', 'MDCCCVL', 'MDCCCVLI', 'MDCCCVLII', 'MDCCCVLIII', 'MDCCCIL', 'MDCCCL', 'MDCCCLI', 'MDCCCLII', 'MDCCCLIII', 'MDCCCLIV', 'MDCCCLV', 'MDCCCLVI', 'MDCCCLVII', 'MDCCCLVIII', 'MDCCCLIX', 'MDCCCLX', 'MDCCCLXI', 'MDCCCLXII', 'MDCCCLXIII', 'MDCCCLXIV', 'MDCCCLXV', 'MDCCCLXVI', 'MDCCCLXVII', 'MDCCCLXVIII', 'MDCCCLXIX', 'MDCCCLXX', 'MDCCCLXXI', 'MDCCCLXXII', 'MDCCCLXXIII', 'MDCCCLXXIV', 'MDCCCLXXV', 'MDCCCLXXVI', 'MDCCCLXXVII', 'MDCCCLXXVIII', 'MDCCCLXXIX', 'MDCCCLXXX', 'MDCCCLXXXI', 'MDCCCLXXXII', 'MDCCCLXXXIII', 'MDCCCLXXXIV', 'MDCCCLXXXV', 'MDCCCLXXXVI', 'MDCCCLXXXVII', 'MDCCCLXXXVIII', 'MDCCCLXXXIX', 'MDCCCXC', 'MDCCCXCI', 'MDCCCXCII', 'MDCCCXCIII', 'MDCCCXCIV', 'MDCCCVC', 'MDCCCVCI', 'MDCCCVCII', 'MDCCCVCIII', 'MDCCCIC', 'MCM', 'MCMI', 'MCMII', 'MCMIII', 'MCMIV', 'MCMV', 'MCMVI', 'MCMVII', 'MCMVIII', 'MCMIX', 'MCMX', 'MCMXI', 'MCMXII', 'MCMXIII', 'MCMXIV', 'MCMXV', 'MCMXVI', 'MCMXVII', 'MCMXVIII', 'MCMXIX', 'MCMXX', 'MCMXXI', 'MCMXXII', 'MCMXXIII', 'MCMXXIV', 'MCMXXV', 'MCMXXVI', 'MCMXXVII', 'MCMXXVIII', 'MCMXXIX', 'MCMXXX', 'MCMXXXI', 'MCMXXXII', 'MCMXXXIII', 'MCMXXXIV', 'MCMXXXV', 'MCMXXXVI', 'MCMXXXVII', 'MCMXXXVIII', 'MCMXXXIX', 'MCMXL', 'MCMXLI', 'MCMXLII', 'MCMXLIII', 'MCMXLIV', 'MCMVL', 'MCMVLI', 'MCMVLII', 'MCMVLIII', 'MCMIL', 'MLM', 'MLMI', 'MLMII', 'MLMIII', 'MLMIV', 'MLMV', 'MLMVI', 'MLMVII', 'MLMVIII', 'MLMIX', 'MLMX', 'MLMXI', 'MLMXII', 'MLMXIII', 'MLMXIV', 'MLMXV', 'MLMXVI', 'MLMXVII', 'MLMXVIII', 'MLMXIX', 'MLMXX', 'MLMXXI', 'MLMXXII', 'MLMXXIII', 'MLMXXIV', 'MLMXXV', 'MLMXXVI', 'MLMXXVII', 'MLMXXVIII', 'MLMXXIX', 'MLMXXX', 'MLMXXXI', 'MLMXXXII', 'MLMXXXIII', 'MLMXXXIV', 'MLMXXXV', 'MLMXXXVI', 'MLMXXXVII', 'MLMXXXVIII', 'MLMXXXIX', 'MXM', 'MXMI', 'MXMII', 'MXMIII', 'MXMIV', 'MVM', 'MVMI', 'MVMII', 'MVMIII', 'MIM', 'MM', 'MMI', 'MMII', 'MMIII', 'MMIV', 'MMV', 'MMVI', 'MMVII', 'MMVIII', 'MMIX', 'MMX', 'MMXI', 'MMXII', 'MMXIII', 'MMXIV', 'MMXV', 'MMXVI', 'MMXVII', 'MMXVIII', 'MMXIX', 'MMXX', 'MMXXI', 'MMXXII', 'MMXXIII', 'MMXXIV', 'MMXXV', 'MMXXVI', 'MMXXVII', 'MMXXVIII', 'MMXXIX', 'MMXXX', 'MMXXXI', 'MMXXXII', 'MMXXXIII', 'MMXXXIV', 'MMXXXV', 'MMXXXVI', 'MMXXXVII', 'MMXXXVIII', 'MMXXXIX', 'MMXL', 'MMXLI', 'MMXLII', 'MMXLIII', 'MMXLIV', 'MMVL', 'MMVLI', 'MMVLII', 'MMVLIII', 'MMIL', 'MML', 'MMLI', 'MMLII', 'MMLIII', 'MMLIV', 'MMLV', 'MMLVI', 'MMLVII', 'MMLVIII', 'MMLIX', 'MMLX', 'MMLXI', 'MMLXII', 'MMLXIII', 'MMLXIV', 'MMLXV', 'MMLXVI', 'MMLXVII', 'MMLXVIII', 'MMLXIX', 'MMLXX', 'MMLXXI', 'MMLXXII', 'MMLXXIII', 'MMLXXIV', 'MMLXXV', 'MMLXXVI', 'MMLXXVII', 'MMLXXVIII', 'MMLXXIX', 'MMLXXX', 'MMLXXXI', 'MMLXXXII', 'MMLXXXIII', 'MMLXXXIV', 'MMLXXXV', 'MMLXXXVI', 'MMLXXXVII', 'MMLXXXVIII', 'MMLXXXIX', 'MMXC', 'MMXCI', 'MMXCII', 'MMXCIII', 'MMXCIV', 'MMVC', 'MMVCI', 'MMVCII', 'MMVCIII', 'MMIC', 'MMC', 'MMCI', 'MMCII', 'MMCIII', 'MMCIV', 'MMCV', 'MMCVI', 'MMCVII', 'MMCVIII', 'MMCIX', 'MMCX', 'MMCXI', 'MMCXII', 'MMCXIII', 'MMCXIV', 'MMCXV', 'MMCXVI', 'MMCXVII', 'MMCXVIII', 'MMCXIX', 'MMCXX', 'MMCXXI', 'MMCXXII', 'MMCXXIII', 'MMCXXIV', 'MMCXXV', 'MMCXXVI', 'MMCXXVII', 'MMCXXVIII', 'MMCXXIX', 'MMCXXX', 'MMCXXXI', 'MMCXXXII', 'MMCXXXIII', 'MMCXXXIV', 'MMCXXXV', 'MMCXXXVI', 'MMCXXXVII', 'MMCXXXVIII', 'MMCXXXIX', 'MMCXL', 'MMCXLI', 'MMCXLII', 'MMCXLIII', 'MMCXLIV', 'MMCVL', 'MMCVLI', 'MMCVLII', 'MMCVLIII', 'MMCIL', 'MMCL', 'MMCLI', 'MMCLII', 'MMCLIII', 'MMCLIV', 'MMCLV', 'MMCLVI', 'MMCLVII', 'MMCLVIII', 'MMCLIX', 'MMCLX', 'MMCLXI', 'MMCLXII', 'MMCLXIII', 'MMCLXIV', 'MMCLXV', 'MMCLXVI', 'MMCLXVII', 'MMCLXVIII', 'MMCLXIX', 'MMCLXX', 'MMCLXXI', 'MMCLXXII', 'MMCLXXIII', 'MMCLXXIV', 'MMCLXXV', 'MMCLXXVI', 'MMCLXXVII', 'MMCLXXVIII', 'MMCLXXIX', 'MMCLXXX', 'MMCLXXXI', 'MMCLXXXII', 'MMCLXXXIII', 'MMCLXXXIV', 'MMCLXXXV', 'MMCLXXXVI', 'MMCLXXXVII', 'MMCLXXXVIII', 'MMCLXXXIX', 'MMCXC', 'MMCXCI', 'MMCXCII', 'MMCXCIII', 'MMCXCIV', 'MMCVC', 'MMCVCI', 'MMCVCII', 'MMCVCIII', 'MMCIC', 'MMCC', 'MMCCI', 'MMCCII', 'MMCCIII', 'MMCCIV', 'MMCCV', 'MMCCVI', 'MMCCVII', 'MMCCVIII', 'MMCCIX', 'MMCCX', 'MMCCXI', 'MMCCXII', 'MMCCXIII', 'MMCCXIV', 'MMCCXV', 'MMCCXVI', 'MMCCXVII', 'MMCCXVIII', 'MMCCXIX', 'MMCCXX', 'MMCCXXI', 'MMCCXXII', 'MMCCXXIII', 'MMCCXXIV', 'MMCCXXV', 'MMCCXXVI', 'MMCCXXVII', 'MMCCXXVIII', 'MMCCXXIX', 'MMCCXXX', 'MMCCXXXI', 'MMCCXXXII', 'MMCCXXXIII', 'MMCCXXXIV', 'MMCCXXXV', 'MMCCXXXVI', 'MMCCXXXVII', 'MMCCXXXVIII', 'MMCCXXXIX', 'MMCCXL', 'MMCCXLI', 'MMCCXLII', 'MMCCXLIII', 'MMCCXLIV', 'MMCCVL', 'MMCCVLI', 'MMCCVLII', 'MMCCVLIII', 'MMCCIL', 'MMCCL', 'MMCCLI', 'MMCCLII', 'MMCCLIII', 'MMCCLIV', 'MMCCLV', 'MMCCLVI', 'MMCCLVII', 'MMCCLVIII', 'MMCCLIX', 'MMCCLX', 'MMCCLXI', 'MMCCLXII', 'MMCCLXIII', 'MMCCLXIV', 'MMCCLXV', 'MMCCLXVI', 'MMCCLXVII', 'MMCCLXVIII', 'MMCCLXIX', 'MMCCLXX', 'MMCCLXXI', 'MMCCLXXII', 'MMCCLXXIII', 'MMCCLXXIV', 'MMCCLXXV', 'MMCCLXXVI', 'MMCCLXXVII', 'MMCCLXXVIII', 'MMCCLXXIX', 'MMCCLXXX', 'MMCCLXXXI', 'MMCCLXXXII', 'MMCCLXXXIII', 'MMCCLXXXIV', 'MMCCLXXXV', 'MMCCLXXXVI', 'MMCCLXXXVII', 'MMCCLXXXVIII', 'MMCCLXXXIX', 'MMCCXC', 'MMCCXCI', 'MMCCXCII', 'MMCCXCIII', 'MMCCXCIV', 'MMCCVC', 'MMCCVCI', 'MMCCVCII', 'MMCCVCIII', 'MMCCIC', 'MMCCC', 'MMCCCI', 'MMCCCII', 'MMCCCIII', 'MMCCCIV', 'MMCCCV', 'MMCCCVI', 'MMCCCVII', 'MMCCCVIII', 'MMCCCIX', 'MMCCCX', 'MMCCCXI', 'MMCCCXII', 'MMCCCXIII', 'MMCCCXIV', 'MMCCCXV', 'MMCCCXVI', 'MMCCCXVII', 'MMCCCXVIII', 'MMCCCXIX', 'MMCCCXX', 'MMCCCXXI', 'MMCCCXXII', 'MMCCCXXIII', 'MMCCCXXIV', 'MMCCCXXV', 'MMCCCXXVI', 'MMCCCXXVII', 'MMCCCXXVIII', 'MMCCCXXIX', 'MMCCCXXX', 'MMCCCXXXI', 'MMCCCXXXII', 'MMCCCXXXIII', 'MMCCCXXXIV', 'MMCCCXXXV', 'MMCCCXXXVI', 'MMCCCXXXVII', 'MMCCCXXXVIII', 'MMCCCXXXIX', 'MMCCCXL', 'MMCCCXLI', 'MMCCCXLII', 'MMCCCXLIII', 'MMCCCXLIV', 'MMCCCVL', 'MMCCCVLI', 'MMCCCVLII', 'MMCCCVLIII', 'MMCCCIL', 'MMCCCL', 'MMCCCLI', 'MMCCCLII', 'MMCCCLIII', 'MMCCCLIV', 'MMCCCLV', 'MMCCCLVI', 'MMCCCLVII', 'MMCCCLVIII', 'MMCCCLIX', 'MMCCCLX', 'MMCCCLXI', 'MMCCCLXII', 'MMCCCLXIII', 'MMCCCLXIV', 'MMCCCLXV', 'MMCCCLXVI', 'MMCCCLXVII', 'MMCCCLXVIII', 'MMCCCLXIX', 'MMCCCLXX', 'MMCCCLXXI', 'MMCCCLXXII', 'MMCCCLXXIII', 'MMCCCLXXIV', 'MMCCCLXXV', 'MMCCCLXXVI', 'MMCCCLXXVII', 'MMCCCLXXVIII', 'MMCCCLXXIX', 'MMCCCLXXX', 'MMCCCLXXXI', 'MMCCCLXXXII', 'MMCCCLXXXIII', 'MMCCCLXXXIV', 'MMCCCLXXXV', 'MMCCCLXXXVI', 'MMCCCLXXXVII', 'MMCCCLXXXVIII', 'MMCCCLXXXIX', 'MMCCCXC', 'MMCCCXCI', 'MMCCCXCII', 'MMCCCXCIII', 'MMCCCXCIV', 'MMCCCVC', 'MMCCCVCI', 'MMCCCVCII', 'MMCCCVCIII', 'MMCCCIC', 'MMCD', 'MMCDI', 'MMCDII', 'MMCDIII', 'MMCDIV', 'MMCDV', 'MMCDVI', 'MMCDVII', 'MMCDVIII', 'MMCDIX', 'MMCDX', 'MMCDXI', 'MMCDXII', 'MMCDXIII', 'MMCDXIV', 'MMCDXV', 'MMCDXVI', 'MMCDXVII', 'MMCDXVIII', 'MMCDXIX', 'MMCDXX', 'MMCDXXI', 'MMCDXXII', 'MMCDXXIII', 'MMCDXXIV', 'MMCDXXV', 'MMCDXXVI', 'MMCDXXVII', 'MMCDXXVIII', 'MMCDXXIX', 'MMCDXXX', 'MMCDXXXI', 'MMCDXXXII', 'MMCDXXXIII', 'MMCDXXXIV', 'MMCDXXXV', 'MMCDXXXVI', 'MMCDXXXVII', 'MMCDXXXVIII', 'MMCDXXXIX', 'MMCDXL', 'MMCDXLI', 'MMCDXLII', 'MMCDXLIII', 'MMCDXLIV', 'MMCDVL', 'MMCDVLI', 'MMCDVLII', 'MMCDVLIII', 'MMCDIL', 'MMLD', 'MMLDI', 'MMLDII', 'MMLDIII', 'MMLDIV', 'MMLDV', 'MMLDVI', 'MMLDVII', 'MMLDVIII', 'MMLDIX', 'MMLDX', 'MMLDXI', 'MMLDXII', 'MMLDXIII', 'MMLDXIV', 'MMLDXV', 'MMLDXVI', 'MMLDXVII', 'MMLDXVIII', 'MMLDXIX', 'MMLDXX', 'MMLDXXI', 'MMLDXXII', 'MMLDXXIII', 'MMLDXXIV', 'MMLDXXV', 'MMLDXXVI', 'MMLDXXVII', 'MMLDXXVIII', 'MMLDXXIX', 'MMLDXXX', 'MMLDXXXI', 'MMLDXXXII', 'MMLDXXXIII', 'MMLDXXXIV', 'MMLDXXXV', 'MMLDXXXVI', 'MMLDXXXVII', 'MMLDXXXVIII', 'MMLDXXXIX', 'MMXD', 'MMXDI', 'MMXDII', 'MMXDIII', 'MMXDIV', 'MMVD', 'MMVDI', 'MMVDII', 'MMVDIII', 'MMID', 'MMD', 'MMDI', 'MMDII', 'MMDIII', 'MMDIV', 'MMDV', 'MMDVI', 'MMDVII', 'MMDVIII', 'MMDIX', 'MMDX', 'MMDXI', 'MMDXII', 'MMDXIII', 'MMDXIV', 'MMDXV', 'MMDXVI', 'MMDXVII', 'MMDXVIII', 'MMDXIX', 'MMDXX', 'MMDXXI', 'MMDXXII', 'MMDXXIII', 'MMDXXIV', 'MMDXXV', 'MMDXXVI', 'MMDXXVII', 'MMDXXVIII', 'MMDXXIX', 'MMDXXX', 'MMDXXXI', 'MMDXXXII', 'MMDXXXIII', 'MMDXXXIV', 'MMDXXXV', 'MMDXXXVI', 'MMDXXXVII', 'MMDXXXVIII', 'MMDXXXIX', 'MMDXL', 'MMDXLI', 'MMDXLII', 'MMDXLIII', 'MMDXLIV', 'MMDVL', 'MMDVLI', 'MMDVLII', 'MMDVLIII', 'MMDIL', 'MMDL', 'MMDLI', 'MMDLII', 'MMDLIII', 'MMDLIV', 'MMDLV', 'MMDLVI', 'MMDLVII', 'MMDLVIII', 'MMDLIX', 'MMDLX', 'MMDLXI', 'MMDLXII', 'MMDLXIII', 'MMDLXIV', 'MMDLXV', 'MMDLXVI', 'MMDLXVII', 'MMDLXVIII', 'MMDLXIX', 'MMDLXX', 'MMDLXXI', 'MMDLXXII', 'MMDLXXIII', 'MMDLXXIV', 'MMDLXXV', 'MMDLXXVI', 'MMDLXXVII', 'MMDLXXVIII', 'MMDLXXIX', 'MMDLXXX', 'MMDLXXXI', 'MMDLXXXII', 'MMDLXXXIII', 'MMDLXXXIV', 'MMDLXXXV', 'MMDLXXXVI', 'MMDLXXXVII', 'MMDLXXXVIII', 'MMDLXXXIX', 'MMDXC', 'MMDXCI', 'MMDXCII', 'MMDXCIII', 'MMDXCIV', 'MMDVC', 'MMDVCI', 'MMDVCII', 'MMDVCIII', 'MMDIC', 'MMDC', 'MMDCI', 'MMDCII', 'MMDCIII', 'MMDCIV', 'MMDCV', 'MMDCVI', 'MMDCVII', 'MMDCVIII', 'MMDCIX', 'MMDCX', 'MMDCXI', 'MMDCXII', 'MMDCXIII', 'MMDCXIV', 'MMDCXV', 'MMDCXVI', 'MMDCXVII', 'MMDCXVIII', 'MMDCXIX', 'MMDCXX', 'MMDCXXI', 'MMDCXXII', 'MMDCXXIII', 'MMDCXXIV', 'MMDCXXV', 'MMDCXXVI', 'MMDCXXVII', 'MMDCXXVIII', 'MMDCXXIX', 'MMDCXXX', 'MMDCXXXI', 'MMDCXXXII', 'MMDCXXXIII', 'MMDCXXXIV', 'MMDCXXXV', 'MMDCXXXVI', 'MMDCXXXVII', 'MMDCXXXVIII', 'MMDCXXXIX', 'MMDCXL', 'MMDCXLI', 'MMDCXLII', 'MMDCXLIII', 'MMDCXLIV', 'MMDCVL', 'MMDCVLI', 'MMDCVLII', 'MMDCVLIII', 'MMDCIL', 'MMDCL', 'MMDCLI', 'MMDCLII', 'MMDCLIII', 'MMDCLIV', 'MMDCLV', 'MMDCLVI', 'MMDCLVII', 'MMDCLVIII', 'MMDCLIX', 'MMDCLX', 'MMDCLXI', 'MMDCLXII', 'MMDCLXIII', 'MMDCLXIV', 'MMDCLXV', 'MMDCLXVI', 'MMDCLXVII', 'MMDCLXVIII', 'MMDCLXIX', 'MMDCLXX', 'MMDCLXXI', 'MMDCLXXII', 'MMDCLXXIII', 'MMDCLXXIV', 'MMDCLXXV', 'MMDCLXXVI', 'MMDCLXXVII', 'MMDCLXXVIII', 'MMDCLXXIX', 'MMDCLXXX', 'MMDCLXXXI', 'MMDCLXXXII', 'MMDCLXXXIII', 'MMDCLXXXIV', 'MMDCLXXXV', 'MMDCLXXXVI', 'MMDCLXXXVII', 'MMDCLXXXVIII', 'MMDCLXXXIX', 'MMDCXC', 'MMDCXCI', 'MMDCXCII', 'MMDCXCIII', 'MMDCXCIV', 'MMDCVC', 'MMDCVCI', 'MMDCVCII', 'MMDCVCIII', 'MMDCIC', 'MMDCC', 'MMDCCI', 'MMDCCII', 'MMDCCIII', 'MMDCCIV', 'MMDCCV', 'MMDCCVI', 'MMDCCVII', 'MMDCCVIII', 'MMDCCIX', 'MMDCCX', 'MMDCCXI', 'MMDCCXII', 'MMDCCXIII', 'MMDCCXIV', 'MMDCCXV', 'MMDCCXVI', 'MMDCCXVII', 'MMDCCXVIII', 'MMDCCXIX', 'MMDCCXX', 'MMDCCXXI', 'MMDCCXXII', 'MMDCCXXIII', 'MMDCCXXIV', 'MMDCCXXV', 'MMDCCXXVI', 'MMDCCXXVII', 'MMDCCXXVIII', 'MMDCCXXIX', 'MMDCCXXX', 'MMDCCXXXI', 'MMDCCXXXII', 'MMDCCXXXIII', 'MMDCCXXXIV', 'MMDCCXXXV', 'MMDCCXXXVI', 'MMDCCXXXVII', 'MMDCCXXXVIII', 'MMDCCXXXIX', 'MMDCCXL', 'MMDCCXLI', 'MMDCCXLII', 'MMDCCXLIII', 'MMDCCXLIV', 'MMDCCVL', 'MMDCCVLI', 'MMDCCVLII', 'MMDCCVLIII', 'MMDCCIL', 'MMDCCL', 'MMDCCLI', 'MMDCCLII', 'MMDCCLIII', 'MMDCCLIV', 'MMDCCLV', 'MMDCCLVI', 'MMDCCLVII', 'MMDCCLVIII', 'MMDCCLIX', 'MMDCCLX', 'MMDCCLXI', 'MMDCCLXII', 'MMDCCLXIII', 'MMDCCLXIV', 'MMDCCLXV', 'MMDCCLXVI', 'MMDCCLXVII', 'MMDCCLXVIII', 'MMDCCLXIX', 'MMDCCLXX', 'MMDCCLXXI', 'MMDCCLXXII', 'MMDCCLXXIII', 'MMDCCLXXIV', 'MMDCCLXXV', 'MMDCCLXXVI', 'MMDCCLXXVII', 'MMDCCLXXVIII', 'MMDCCLXXIX', 'MMDCCLXXX', 'MMDCCLXXXI', 'MMDCCLXXXII', 'MMDCCLXXXIII', 'MMDCCLXXXIV', 'MMDCCLXXXV', 'MMDCCLXXXVI', 'MMDCCLXXXVII', 'MMDCCLXXXVIII', 'MMDCCLXXXIX', 'MMDCCXC', 'MMDCCXCI', 'MMDCCXCII', 'MMDCCXCIII', 'MMDCCXCIV', 'MMDCCVC', 'MMDCCVCI', 'MMDCCVCII', 'MMDCCVCIII', 'MMDCCIC', 'MMDCCC', 'MMDCCCI', 'MMDCCCII', 'MMDCCCIII', 'MMDCCCIV', 'MMDCCCV', 'MMDCCCVI', 'MMDCCCVII', 'MMDCCCVIII', 'MMDCCCIX', 'MMDCCCX', 'MMDCCCXI', 'MMDCCCXII', 'MMDCCCXIII', 'MMDCCCXIV', 'MMDCCCXV', 'MMDCCCXVI', 'MMDCCCXVII', 'MMDCCCXVIII', 'MMDCCCXIX', 'MMDCCCXX', 'MMDCCCXXI', 'MMDCCCXXII', 'MMDCCCXXIII', 'MMDCCCXXIV', 'MMDCCCXXV', 'MMDCCCXXVI', 'MMDCCCXXVII', 'MMDCCCXXVIII', 'MMDCCCXXIX', 'MMDCCCXXX', 'MMDCCCXXXI', 'MMDCCCXXXII', 'MMDCCCXXXIII', 'MMDCCCXXXIV', 'MMDCCCXXXV', 'MMDCCCXXXVI', 'MMDCCCXXXVII', 'MMDCCCXXXVIII', 'MMDCCCXXXIX', 'MMDCCCXL', 'MMDCCCXLI', 'MMDCCCXLII', 'MMDCCCXLIII', 'MMDCCCXLIV', 'MMDCCCVL', 'MMDCCCVLI', 'MMDCCCVLII', 'MMDCCCVLIII', 'MMDCCCIL', 'MMDCCCL', 'MMDCCCLI', 'MMDCCCLII', 'MMDCCCLIII', 'MMDCCCLIV', 'MMDCCCLV', 'MMDCCCLVI', 'MMDCCCLVII', 'MMDCCCLVIII', 'MMDCCCLIX', 'MMDCCCLX', 'MMDCCCLXI', 'MMDCCCLXII', 'MMDCCCLXIII', 'MMDCCCLXIV', 'MMDCCCLXV', 'MMDCCCLXVI', 'MMDCCCLXVII', 'MMDCCCLXVIII', 'MMDCCCLXIX', 'MMDCCCLXX', 'MMDCCCLXXI', 'MMDCCCLXXII', 'MMDCCCLXXIII', 'MMDCCCLXXIV', 'MMDCCCLXXV', 'MMDCCCLXXVI', 'MMDCCCLXXVII', 'MMDCCCLXXVIII', 'MMDCCCLXXIX', 'MMDCCCLXXX', 'MMDCCCLXXXI', 'MMDCCCLXXXII', 'MMDCCCLXXXIII', 'MMDCCCLXXXIV', 'MMDCCCLXXXV', 'MMDCCCLXXXVI', 'MMDCCCLXXXVII', 'MMDCCCLXXXVIII', 'MMDCCCLXXXIX', 'MMDCCCXC', 'MMDCCCXCI', 'MMDCCCXCII', 'MMDCCCXCIII', 'MMDCCCXCIV', 'MMDCCCVC', 'MMDCCCVCI', 'MMDCCCVCII', 'MMDCCCVCIII', 'MMDCCCIC', 'MMCM', 'MMCMI', 'MMCMII', 'MMCMIII', 'MMCMIV', 'MMCMV', 'MMCMVI', 'MMCMVII', 'MMCMVIII', 'MMCMIX', 'MMCMX', 'MMCMXI', 'MMCMXII', 'MMCMXIII', 'MMCMXIV', 'MMCMXV', 'MMCMXVI', 'MMCMXVII', 'MMCMXVIII', 'MMCMXIX', 'MMCMXX', 'MMCMXXI', 'MMCMXXII', 'MMCMXXIII', 'MMCMXXIV', 'MMCMXXV', 'MMCMXXVI', 'MMCMXXVII', 'MMCMXXVIII', 'MMCMXXIX', 'MMCMXXX', 'MMCMXXXI', 'MMCMXXXII', 'MMCMXXXIII', 'MMCMXXXIV', 'MMCMXXXV', 'MMCMXXXVI', 'MMCMXXXVII', 'MMCMXXXVIII', 'MMCMXXXIX', 'MMCMXL', 'MMCMXLI', 'MMCMXLII', 'MMCMXLIII', 'MMCMXLIV', 'MMCMVL', 'MMCMVLI', 'MMCMVLII', 'MMCMVLIII', 'MMCMIL', 'MMLM', 'MMLMI', 'MMLMII', 'MMLMIII', 'MMLMIV', 'MMLMV', 'MMLMVI', 'MMLMVII', 'MMLMVIII', 'MMLMIX', 'MMLMX', 'MMLMXI', 'MMLMXII', 'MMLMXIII', 'MMLMXIV', 'MMLMXV', 'MMLMXVI', 'MMLMXVII', 'MMLMXVIII', 'MMLMXIX', 'MMLMXX', 'MMLMXXI', 'MMLMXXII', 'MMLMXXIII', 'MMLMXXIV', 'MMLMXXV', 'MMLMXXVI', 'MMLMXXVII', 'MMLMXXVIII', 'MMLMXXIX', 'MMLMXXX', 'MMLMXXXI', 'MMLMXXXII', 'MMLMXXXIII', 'MMLMXXXIV', 'MMLMXXXV', 'MMLMXXXVI', 'MMLMXXXVII', 'MMLMXXXVIII', 'MMLMXXXIX', 'MMXM', 'MMXMI', 'MMXMII', 'MMXMIII', 'MMXMIV', 'MMVM', 'MMVMI', 'MMVMII', 'MMVMIII', 'MMIM', 'MMM', 'MMMI', 'MMMII', 'MMMIII', 'MMMIV', 'MMMV', 'MMMVI', 'MMMVII', 'MMMVIII', 'MMMIX', 'MMMX', 'MMMXI', 'MMMXII', 'MMMXIII', 'MMMXIV', 'MMMXV', 'MMMXVI', 'MMMXVII', 'MMMXVIII', 'MMMXIX', 'MMMXX', 'MMMXXI', 'MMMXXII', 'MMMXXIII', 'MMMXXIV', 'MMMXXV', 'MMMXXVI', 'MMMXXVII', 'MMMXXVIII', 'MMMXXIX', 'MMMXXX', 'MMMXXXI', 'MMMXXXII', 'MMMXXXIII', 'MMMXXXIV', 'MMMXXXV', 'MMMXXXVI', 'MMMXXXVII', 'MMMXXXVIII', 'MMMXXXIX', 'MMMXL', 'MMMXLI', 'MMMXLII', 'MMMXLIII', 'MMMXLIV', 'MMMVL', 'MMMVLI', 'MMMVLII', 'MMMVLIII', 'MMMIL', 'MMML', 'MMMLI', 'MMMLII', 'MMMLIII', 'MMMLIV', 'MMMLV', 'MMMLVI', 'MMMLVII', 'MMMLVIII', 'MMMLIX', 'MMMLX', 'MMMLXI', 'MMMLXII', 'MMMLXIII', 'MMMLXIV', 'MMMLXV', 'MMMLXVI', 'MMMLXVII', 'MMMLXVIII', 'MMMLXIX', 'MMMLXX', 'MMMLXXI', 'MMMLXXII', 'MMMLXXIII', 'MMMLXXIV', 'MMMLXXV', 'MMMLXXVI', 'MMMLXXVII', 'MMMLXXVIII', 'MMMLXXIX', 'MMMLXXX', 'MMMLXXXI', 'MMMLXXXII', 'MMMLXXXIII', 'MMMLXXXIV', 'MMMLXXXV', 'MMMLXXXVI', 'MMMLXXXVII', 'MMMLXXXVIII', 'MMMLXXXIX', 'MMMXC', 'MMMXCI', 'MMMXCII', 'MMMXCIII', 'MMMXCIV', 'MMMVC', 'MMMVCI', 'MMMVCII', 'MMMVCIII', 'MMMIC', 'MMMC', 'MMMCI', 'MMMCII', 'MMMCIII', 'MMMCIV', 'MMMCV', 'MMMCVI', 'MMMCVII', 'MMMCVIII', 'MMMCIX', 'MMMCX', 'MMMCXI', 'MMMCXII', 'MMMCXIII', 'MMMCXIV', 'MMMCXV', 'MMMCXVI', 'MMMCXVII', 'MMMCXVIII', 'MMMCXIX', 'MMMCXX', 'MMMCXXI', 'MMMCXXII', 'MMMCXXIII', 'MMMCXXIV', 'MMMCXXV', 'MMMCXXVI', 'MMMCXXVII', 'MMMCXXVIII', 'MMMCXXIX', 'MMMCXXX', 'MMMCXXXI', 'MMMCXXXII', 'MMMCXXXIII', 'MMMCXXXIV', 'MMMCXXXV', 'MMMCXXXVI', 'MMMCXXXVII', 'MMMCXXXVIII', 'MMMCXXXIX', 'MMMCXL', 'MMMCXLI', 'MMMCXLII', 'MMMCXLIII', 'MMMCXLIV', 'MMMCVL', 'MMMCVLI', 'MMMCVLII', 'MMMCVLIII', 'MMMCIL', 'MMMCL', 'MMMCLI', 'MMMCLII', 'MMMCLIII', 'MMMCLIV', 'MMMCLV', 'MMMCLVI', 'MMMCLVII', 'MMMCLVIII', 'MMMCLIX', 'MMMCLX', 'MMMCLXI', 'MMMCLXII', 'MMMCLXIII', 'MMMCLXIV', 'MMMCLXV', 'MMMCLXVI', 'MMMCLXVII', 'MMMCLXVIII', 'MMMCLXIX', 'MMMCLXX', 'MMMCLXXI', 'MMMCLXXII', 'MMMCLXXIII', 'MMMCLXXIV', 'MMMCLXXV', 'MMMCLXXVI', 'MMMCLXXVII', 'MMMCLXXVIII', 'MMMCLXXIX', 'MMMCLXXX', 'MMMCLXXXI', 'MMMCLXXXII', 'MMMCLXXXIII', 'MMMCLXXXIV', 'MMMCLXXXV', 'MMMCLXXXVI', 'MMMCLXXXVII', 'MMMCLXXXVIII', 'MMMCLXXXIX', 'MMMCXC', 'MMMCXCI', 'MMMCXCII', 'MMMCXCIII', 'MMMCXCIV', 'MMMCVC', 'MMMCVCI', 'MMMCVCII', 'MMMCVCIII', 'MMMCIC', 'MMMCC', 'MMMCCI', 'MMMCCII', 'MMMCCIII', 'MMMCCIV', 'MMMCCV', 'MMMCCVI', 'MMMCCVII', 'MMMCCVIII', 'MMMCCIX', 'MMMCCX', 'MMMCCXI', 'MMMCCXII', 'MMMCCXIII', 'MMMCCXIV', 'MMMCCXV', 'MMMCCXVI', 'MMMCCXVII', 'MMMCCXVIII', 'MMMCCXIX', 'MMMCCXX', 'MMMCCXXI', 'MMMCCXXII', 'MMMCCXXIII', 'MMMCCXXIV', 'MMMCCXXV', 'MMMCCXXVI', 'MMMCCXXVII', 'MMMCCXXVIII', 'MMMCCXXIX', 'MMMCCXXX', 'MMMCCXXXI', 'MMMCCXXXII', 'MMMCCXXXIII', 'MMMCCXXXIV', 'MMMCCXXXV', 'MMMCCXXXVI', 'MMMCCXXXVII', 'MMMCCXXXVIII', 'MMMCCXXXIX', 'MMMCCXL', 'MMMCCXLI', 'MMMCCXLII', 'MMMCCXLIII', 'MMMCCXLIV', 'MMMCCVL', 'MMMCCVLI', 'MMMCCVLII', 'MMMCCVLIII', 'MMMCCIL', 'MMMCCL', 'MMMCCLI', 'MMMCCLII', 'MMMCCLIII', 'MMMCCLIV', 'MMMCCLV', 'MMMCCLVI', 'MMMCCLVII', 'MMMCCLVIII', 'MMMCCLIX', 'MMMCCLX', 'MMMCCLXI', 'MMMCCLXII', 'MMMCCLXIII', 'MMMCCLXIV', 'MMMCCLXV', 'MMMCCLXVI', 'MMMCCLXVII', 'MMMCCLXVIII', 'MMMCCLXIX', 'MMMCCLXX', 'MMMCCLXXI', 'MMMCCLXXII', 'MMMCCLXXIII', 'MMMCCLXXIV', 'MMMCCLXXV', 'MMMCCLXXVI', 'MMMCCLXXVII', 'MMMCCLXXVIII', 'MMMCCLXXIX', 'MMMCCLXXX', 'MMMCCLXXXI', 'MMMCCLXXXII', 'MMMCCLXXXIII', 'MMMCCLXXXIV', 'MMMCCLXXXV', 'MMMCCLXXXVI', 'MMMCCLXXXVII', 'MMMCCLXXXVIII', 'MMMCCLXXXIX', 'MMMCCXC', 'MMMCCXCI', 'MMMCCXCII', 'MMMCCXCIII', 'MMMCCXCIV', 'MMMCCVC', 'MMMCCVCI', 'MMMCCVCII', 'MMMCCVCIII', 'MMMCCIC', 'MMMCCC', 'MMMCCCI', 'MMMCCCII', 'MMMCCCIII', 'MMMCCCIV', 'MMMCCCV', 'MMMCCCVI', 'MMMCCCVII', 'MMMCCCVIII', 'MMMCCCIX', 'MMMCCCX', 'MMMCCCXI', 'MMMCCCXII', 'MMMCCCXIII', 'MMMCCCXIV', 'MMMCCCXV', 'MMMCCCXVI', 'MMMCCCXVII', 'MMMCCCXVIII', 'MMMCCCXIX', 'MMMCCCXX', 'MMMCCCXXI', 'MMMCCCXXII', 'MMMCCCXXIII', 'MMMCCCXXIV', 'MMMCCCXXV', 'MMMCCCXXVI', 'MMMCCCXXVII', 'MMMCCCXXVIII', 'MMMCCCXXIX', 'MMMCCCXXX', 'MMMCCCXXXI', 'MMMCCCXXXII', 'MMMCCCXXXIII', 'MMMCCCXXXIV', 'MMMCCCXXXV', 'MMMCCCXXXVI', 'MMMCCCXXXVII', 'MMMCCCXXXVIII', 'MMMCCCXXXIX', 'MMMCCCXL', 'MMMCCCXLI', 'MMMCCCXLII', 'MMMCCCXLIII', 'MMMCCCXLIV', 'MMMCCCVL', 'MMMCCCVLI', 'MMMCCCVLII', 'MMMCCCVLIII', 'MMMCCCIL', 'MMMCCCL', 'MMMCCCLI', 'MMMCCCLII', 'MMMCCCLIII', 'MMMCCCLIV', 'MMMCCCLV', 'MMMCCCLVI', 'MMMCCCLVII', 'MMMCCCLVIII', 'MMMCCCLIX', 'MMMCCCLX', 'MMMCCCLXI', 'MMMCCCLXII', 'MMMCCCLXIII', 'MMMCCCLXIV', 'MMMCCCLXV', 'MMMCCCLXVI', 'MMMCCCLXVII', 'MMMCCCLXVIII', 'MMMCCCLXIX', 'MMMCCCLXX', 'MMMCCCLXXI', 'MMMCCCLXXII', 'MMMCCCLXXIII', 'MMMCCCLXXIV', 'MMMCCCLXXV', 'MMMCCCLXXVI', 'MMMCCCLXXVII', 'MMMCCCLXXVIII', 'MMMCCCLXXIX', 'MMMCCCLXXX', 'MMMCCCLXXXI', 'MMMCCCLXXXII', 'MMMCCCLXXXIII', 'MMMCCCLXXXIV', 'MMMCCCLXXXV', 'MMMCCCLXXXVI', 'MMMCCCLXXXVII', 'MMMCCCLXXXVIII', 'MMMCCCLXXXIX', 'MMMCCCXC', 'MMMCCCXCI', 'MMMCCCXCII', 'MMMCCCXCIII', 'MMMCCCXCIV', 'MMMCCCVC', 'MMMCCCVCI', 'MMMCCCVCII', 'MMMCCCVCIII', 'MMMCCCIC', 'MMMCD', 'MMMCDI', 'MMMCDII', 'MMMCDIII', 'MMMCDIV', 'MMMCDV', 'MMMCDVI', 'MMMCDVII', 'MMMCDVIII', 'MMMCDIX', 'MMMCDX', 'MMMCDXI', 'MMMCDXII', 'MMMCDXIII', 'MMMCDXIV', 'MMMCDXV', 'MMMCDXVI', 'MMMCDXVII', 'MMMCDXVIII', 'MMMCDXIX', 'MMMCDXX', 'MMMCDXXI', 'MMMCDXXII', 'MMMCDXXIII', 'MMMCDXXIV', 'MMMCDXXV', 'MMMCDXXVI', 'MMMCDXXVII', 'MMMCDXXVIII', 'MMMCDXXIX', 'MMMCDXXX', 'MMMCDXXXI', 'MMMCDXXXII', 'MMMCDXXXIII', 'MMMCDXXXIV', 'MMMCDXXXV', 'MMMCDXXXVI', 'MMMCDXXXVII', 'MMMCDXXXVIII', 'MMMCDXXXIX', 'MMMCDXL', 'MMMCDXLI', 'MMMCDXLII', 'MMMCDXLIII', 'MMMCDXLIV', 'MMMCDVL', 'MMMCDVLI', 'MMMCDVLII', 'MMMCDVLIII', 'MMMCDIL', 'MMMLD', 'MMMLDI', 'MMMLDII', 'MMMLDIII', 'MMMLDIV', 'MMMLDV', 'MMMLDVI', 'MMMLDVII', 'MMMLDVIII', 'MMMLDIX', 'MMMLDX', 'MMMLDXI', 'MMMLDXII', 'MMMLDXIII', 'MMMLDXIV', 'MMMLDXV', 'MMMLDXVI', 'MMMLDXVII', 'MMMLDXVIII', 'MMMLDXIX', 'MMMLDXX', 'MMMLDXXI', 'MMMLDXXII', 'MMMLDXXIII', 'MMMLDXXIV', 'MMMLDXXV', 'MMMLDXXVI', 'MMMLDXXVII', 'MMMLDXXVIII', 'MMMLDXXIX', 'MMMLDXXX', 'MMMLDXXXI', 'MMMLDXXXII', 'MMMLDXXXIII', 'MMMLDXXXIV', 'MMMLDXXXV', 'MMMLDXXXVI', 'MMMLDXXXVII', 'MMMLDXXXVIII', 'MMMLDXXXIX', 'MMMXD', 'MMMXDI', 'MMMXDII', 'MMMXDIII', 'MMMXDIV', 'MMMVD', 'MMMVDI', 'MMMVDII', 'MMMVDIII', 'MMMID', 'MMMD', 'MMMDI', 'MMMDII', 'MMMDIII', 'MMMDIV', 'MMMDV', 'MMMDVI', 'MMMDVII', 'MMMDVIII', 'MMMDIX', 'MMMDX', 'MMMDXI', 'MMMDXII', 'MMMDXIII', 'MMMDXIV', 'MMMDXV', 'MMMDXVI', 'MMMDXVII', 'MMMDXVIII', 'MMMDXIX', 'MMMDXX', 'MMMDXXI', 'MMMDXXII', 'MMMDXXIII', 'MMMDXXIV', 'MMMDXXV', 'MMMDXXVI', 'MMMDXXVII', 'MMMDXXVIII', 'MMMDXXIX', 'MMMDXXX', 'MMMDXXXI', 'MMMDXXXII', 'MMMDXXXIII', 'MMMDXXXIV', 'MMMDXXXV', 'MMMDXXXVI', 'MMMDXXXVII', 'MMMDXXXVIII', 'MMMDXXXIX', 'MMMDXL', 'MMMDXLI', 'MMMDXLII', 'MMMDXLIII', 'MMMDXLIV', 'MMMDVL', 'MMMDVLI', 'MMMDVLII', 'MMMDVLIII', 'MMMDIL', 'MMMDL', 'MMMDLI', 'MMMDLII', 'MMMDLIII', 'MMMDLIV', 'MMMDLV', 'MMMDLVI', 'MMMDLVII', 'MMMDLVIII', 'MMMDLIX', 'MMMDLX', 'MMMDLXI', 'MMMDLXII', 'MMMDLXIII', 'MMMDLXIV', 'MMMDLXV', 'MMMDLXVI', 'MMMDLXVII', 'MMMDLXVIII', 'MMMDLXIX', 'MMMDLXX', 'MMMDLXXI', 'MMMDLXXII', 'MMMDLXXIII', 'MMMDLXXIV', 'MMMDLXXV', 'MMMDLXXVI', 'MMMDLXXVII', 'MMMDLXXVIII', 'MMMDLXXIX', 'MMMDLXXX', 'MMMDLXXXI', 'MMMDLXXXII', 'MMMDLXXXIII', 'MMMDLXXXIV', 'MMMDLXXXV', 'MMMDLXXXVI', 'MMMDLXXXVII', 'MMMDLXXXVIII', 'MMMDLXXXIX', 'MMMDXC', 'MMMDXCI', 'MMMDXCII', 'MMMDXCIII', 'MMMDXCIV', 'MMMDVC', 'MMMDVCI', 'MMMDVCII', 'MMMDVCIII', 'MMMDIC', 'MMMDC', 'MMMDCI', 'MMMDCII', 'MMMDCIII', 'MMMDCIV', 'MMMDCV', 'MMMDCVI', 'MMMDCVII', 'MMMDCVIII', 'MMMDCIX', 'MMMDCX', 'MMMDCXI', 'MMMDCXII', 'MMMDCXIII', 'MMMDCXIV', 'MMMDCXV', 'MMMDCXVI', 'MMMDCXVII', 'MMMDCXVIII', 'MMMDCXIX', 'MMMDCXX', 'MMMDCXXI', 'MMMDCXXII', 'MMMDCXXIII', 'MMMDCXXIV', 'MMMDCXXV', 'MMMDCXXVI', 'MMMDCXXVII', 'MMMDCXXVIII', 'MMMDCXXIX', 'MMMDCXXX', 'MMMDCXXXI', 'MMMDCXXXII', 'MMMDCXXXIII', 'MMMDCXXXIV', 'MMMDCXXXV', 'MMMDCXXXVI', 'MMMDCXXXVII', 'MMMDCXXXVIII', 'MMMDCXXXIX', 'MMMDCXL', 'MMMDCXLI', 'MMMDCXLII', 'MMMDCXLIII', 'MMMDCXLIV', 'MMMDCVL', 'MMMDCVLI', 'MMMDCVLII', 'MMMDCVLIII', 'MMMDCIL', 'MMMDCL', 'MMMDCLI', 'MMMDCLII', 'MMMDCLIII', 'MMMDCLIV', 'MMMDCLV', 'MMMDCLVI', 'MMMDCLVII', 'MMMDCLVIII', 'MMMDCLIX', 'MMMDCLX', 'MMMDCLXI', 'MMMDCLXII', 'MMMDCLXIII', 'MMMDCLXIV', 'MMMDCLXV', 'MMMDCLXVI', 'MMMDCLXVII', 'MMMDCLXVIII', 'MMMDCLXIX', 'MMMDCLXX', 'MMMDCLXXI', 'MMMDCLXXII', 'MMMDCLXXIII', 'MMMDCLXXIV', 'MMMDCLXXV', 'MMMDCLXXVI', 'MMMDCLXXVII', 'MMMDCLXXVIII', 'MMMDCLXXIX', 'MMMDCLXXX', 'MMMDCLXXXI', 'MMMDCLXXXII', 'MMMDCLXXXIII', 'MMMDCLXXXIV', 'MMMDCLXXXV', 'MMMDCLXXXVI', 'MMMDCLXXXVII', 'MMMDCLXXXVIII', 'MMMDCLXXXIX', 'MMMDCXC', 'MMMDCXCI', 'MMMDCXCII', 'MMMDCXCIII', 'MMMDCXCIV', 'MMMDCVC', 'MMMDCVCI', 'MMMDCVCII', 'MMMDCVCIII', 'MMMDCIC', 'MMMDCC', 'MMMDCCI', 'MMMDCCII', 'MMMDCCIII', 'MMMDCCIV', 'MMMDCCV', 'MMMDCCVI', 'MMMDCCVII', 'MMMDCCVIII', 'MMMDCCIX', 'MMMDCCX', 'MMMDCCXI', 'MMMDCCXII', 'MMMDCCXIII', 'MMMDCCXIV', 'MMMDCCXV', 'MMMDCCXVI', 'MMMDCCXVII', 'MMMDCCXVIII', 'MMMDCCXIX', 'MMMDCCXX', 'MMMDCCXXI', 'MMMDCCXXII', 'MMMDCCXXIII', 'MMMDCCXXIV', 'MMMDCCXXV', 'MMMDCCXXVI', 'MMMDCCXXVII', 'MMMDCCXXVIII', 'MMMDCCXXIX', 'MMMDCCXXX', 'MMMDCCXXXI', 'MMMDCCXXXII', 'MMMDCCXXXIII', 'MMMDCCXXXIV', 'MMMDCCXXXV', 'MMMDCCXXXVI', 'MMMDCCXXXVII', 'MMMDCCXXXVIII', 'MMMDCCXXXIX', 'MMMDCCXL', 'MMMDCCXLI', 'MMMDCCXLII', 'MMMDCCXLIII', 'MMMDCCXLIV', 'MMMDCCVL', 'MMMDCCVLI', 'MMMDCCVLII', 'MMMDCCVLIII', 'MMMDCCIL', 'MMMDCCL', 'MMMDCCLI', 'MMMDCCLII', 'MMMDCCLIII', 'MMMDCCLIV', 'MMMDCCLV', 'MMMDCCLVI', 'MMMDCCLVII', 'MMMDCCLVIII', 'MMMDCCLIX', 'MMMDCCLX', 'MMMDCCLXI', 'MMMDCCLXII', 'MMMDCCLXIII', 'MMMDCCLXIV', 'MMMDCCLXV', 'MMMDCCLXVI', 'MMMDCCLXVII', 'MMMDCCLXVIII', 'MMMDCCLXIX', 'MMMDCCLXX', 'MMMDCCLXXI', 'MMMDCCLXXII', 'MMMDCCLXXIII', 'MMMDCCLXXIV', 'MMMDCCLXXV', 'MMMDCCLXXVI', 'MMMDCCLXXVII', 'MMMDCCLXXVIII', 'MMMDCCLXXIX', 'MMMDCCLXXX', 'MMMDCCLXXXI', 'MMMDCCLXXXII', 'MMMDCCLXXXIII', 'MMMDCCLXXXIV', 'MMMDCCLXXXV', 'MMMDCCLXXXVI', 'MMMDCCLXXXVII', 'MMMDCCLXXXVIII', 'MMMDCCLXXXIX', 'MMMDCCXC', 'MMMDCCXCI', 'MMMDCCXCII', 'MMMDCCXCIII', 'MMMDCCXCIV', 'MMMDCCVC', 'MMMDCCVCI', 'MMMDCCVCII', 'MMMDCCVCIII', 'MMMDCCIC', 'MMMDCCC', 'MMMDCCCI', 'MMMDCCCII', 'MMMDCCCIII', 'MMMDCCCIV', 'MMMDCCCV', 'MMMDCCCVI', 'MMMDCCCVII', 'MMMDCCCVIII', 'MMMDCCCIX', 'MMMDCCCX', 'MMMDCCCXI', 'MMMDCCCXII', 'MMMDCCCXIII', 'MMMDCCCXIV', 'MMMDCCCXV', 'MMMDCCCXVI', 'MMMDCCCXVII', 'MMMDCCCXVIII', 'MMMDCCCXIX', 'MMMDCCCXX', 'MMMDCCCXXI', 'MMMDCCCXXII', 'MMMDCCCXXIII', 'MMMDCCCXXIV', 'MMMDCCCXXV', 'MMMDCCCXXVI', 'MMMDCCCXXVII', 'MMMDCCCXXVIII', 'MMMDCCCXXIX', 'MMMDCCCXXX', 'MMMDCCCXXXI', 'MMMDCCCXXXII', 'MMMDCCCXXXIII', 'MMMDCCCXXXIV', 'MMMDCCCXXXV', 'MMMDCCCXXXVI', 'MMMDCCCXXXVII', 'MMMDCCCXXXVIII', 'MMMDCCCXXXIX', 'MMMDCCCXL', 'MMMDCCCXLI', 'MMMDCCCXLII', 'MMMDCCCXLIII', 'MMMDCCCXLIV', 'MMMDCCCVL', 'MMMDCCCVLI', 'MMMDCCCVLII', 'MMMDCCCVLIII', 'MMMDCCCIL', 'MMMDCCCL', 'MMMDCCCLI', 'MMMDCCCLII', 'MMMDCCCLIII', 'MMMDCCCLIV', 'MMMDCCCLV', 'MMMDCCCLVI', 'MMMDCCCLVII', 'MMMDCCCLVIII', 'MMMDCCCLIX', 'MMMDCCCLX', 'MMMDCCCLXI', 'MMMDCCCLXII', 'MMMDCCCLXIII', 'MMMDCCCLXIV', 'MMMDCCCLXV', 'MMMDCCCLXVI', 'MMMDCCCLXVII', 'MMMDCCCLXVIII', 'MMMDCCCLXIX', 'MMMDCCCLXX', 'MMMDCCCLXXI', 'MMMDCCCLXXII', 'MMMDCCCLXXIII', 'MMMDCCCLXXIV', 'MMMDCCCLXXV', 'MMMDCCCLXXVI', 'MMMDCCCLXXVII', 'MMMDCCCLXXVIII', 'MMMDCCCLXXIX', 'MMMDCCCLXXX', 'MMMDCCCLXXXI', 'MMMDCCCLXXXII', 'MMMDCCCLXXXIII', 'MMMDCCCLXXXIV', 'MMMDCCCLXXXV', 'MMMDCCCLXXXVI', 'MMMDCCCLXXXVII', 'MMMDCCCLXXXVIII', 'MMMDCCCLXXXIX', 'MMMDCCCXC', 'MMMDCCCXCI', 'MMMDCCCXCII', 'MMMDCCCXCIII', 'MMMDCCCXCIV', 'MMMDCCCVC', 'MMMDCCCVCI', 'MMMDCCCVCII', 'MMMDCCCVCIII', 'MMMDCCCIC', 'MMMCM', 'MMMCMI', 'MMMCMII', 'MMMCMIII', 'MMMCMIV', 'MMMCMV', 'MMMCMVI', 'MMMCMVII', 'MMMCMVIII', 'MMMCMIX', 'MMMCMX', 'MMMCMXI', 'MMMCMXII', 'MMMCMXIII', 'MMMCMXIV', 'MMMCMXV', 'MMMCMXVI', 'MMMCMXVII', 'MMMCMXVIII', 'MMMCMXIX', 'MMMCMXX', 'MMMCMXXI', 'MMMCMXXII', 'MMMCMXXIII', 'MMMCMXXIV', 'MMMCMXXV', 'MMMCMXXVI', 'MMMCMXXVII', 'MMMCMXXVIII', 'MMMCMXXIX', 'MMMCMXXX', 'MMMCMXXXI', 'MMMCMXXXII', 'MMMCMXXXIII', 'MMMCMXXXIV', 'MMMCMXXXV', 'MMMCMXXXVI', 'MMMCMXXXVII', 'MMMCMXXXVIII', 'MMMCMXXXIX', 'MMMCMXL', 'MMMCMXLI', 'MMMCMXLII', 'MMMCMXLIII', 'MMMCMXLIV', 'MMMCMVL', 'MMMCMVLI', 'MMMCMVLII', 'MMMCMVLIII', 'MMMCMIL', 'MMMLM', 'MMMLMI', 'MMMLMII', 'MMMLMIII', 'MMMLMIV', 'MMMLMV', 'MMMLMVI', 'MMMLMVII', 'MMMLMVIII', 'MMMLMIX', 'MMMLMX', 'MMMLMXI', 'MMMLMXII', 'MMMLMXIII', 'MMMLMXIV', 'MMMLMXV', 'MMMLMXVI', 'MMMLMXVII', 'MMMLMXVIII', 'MMMLMXIX', 'MMMLMXX', 'MMMLMXXI', 'MMMLMXXII', 'MMMLMXXIII', 'MMMLMXXIV', 'MMMLMXXV', 'MMMLMXXVI', 'MMMLMXXVII', 'MMMLMXXVIII', 'MMMLMXXIX', 'MMMLMXXX', 'MMMLMXXXI', 'MMMLMXXXII', 'MMMLMXXXIII', 'MMMLMXXXIV', 'MMMLMXXXV', 'MMMLMXXXVI', 'MMMLMXXXVII', 'MMMLMXXXVIII', 'MMMLMXXXIX', 'MMMXM', 'MMMXMI', 'MMMXMII', 'MMMXMIII', 'MMMXMIV', 'MMMVM', 'MMMVMI', 'MMMVMII', 'MMMVMIII', 'MMMIM', ] diff --git a/test/unit/interpreter/function-round.spec.ts b/test/unit/interpreter/function-round.spec.ts deleted file mode 100644 index a3070155f7..0000000000 --- a/test/unit/interpreter/function-round.spec.ts +++ /dev/null @@ -1,79 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ROUND', () => { - it('number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUND()', '=ROUND(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works for positive numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUND(1.3)', '=ROUND(1.7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('B1'))).toBe(2) - }) - - it('works for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUND(-1.3)', '=ROUND(-1.7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(-1) - expect(engine.getCellValue(adr('B1'))).toBe(-2) - }) - - it('no -0', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUND(-0.001)', '=ROUND(0.001)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - expect(engine.getCellValue(adr('B1'))).toBe(0) - }) - - it('works with positive rounding argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUND(1.43, 1)', '=ROUND(1.47, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1.4) - expect(engine.getCellValue(adr('B1'))).toBe(1.5) - }) - - it('works with negative rounding argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUND(43, -1)', '=ROUND(47, -1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(40) - expect(engine.getCellValue(adr('B1'))).toBe(50) - }) - - it('use coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUND("42.3")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(42) - }) - - it('propagates error', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/0'], - ['=ROUND(A1)', '=ROUND(42, A1)', '=ROUND(A1, FOO())'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('C2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-rounddown.spec.ts b/test/unit/interpreter/function-rounddown.spec.ts deleted file mode 100644 index f4070d4681..0000000000 --- a/test/unit/interpreter/function-rounddown.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ROUNDDOWN', () => { - it('number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUNDDOWN()', '=ROUNDDOWN(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works for positive numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUNDDOWN(1.3)', '=ROUNDDOWN(1.7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('B1'))).toBe(1) - }) - - it('works for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUNDDOWN(-1.3)', '=ROUNDDOWN(-1.7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(-1) - expect(engine.getCellValue(adr('B1'))).toBe(-1) - }) - - it('works with positive rounding argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUNDDOWN(1.43, 1)', '=ROUNDDOWN(1.47, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1.4) - expect(engine.getCellValue(adr('B1'))).toBe(1.4) - }) - - it('works with negative rounding argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUNDDOWN(43, -1)', '=ROUNDDOWN(47, -1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(40) - expect(engine.getCellValue(adr('B1'))).toBe(40) - }) - - it('use coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUNDDOWN("42.3")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(42) - }) - - it('propagates error', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/0'], - ['=ROUNDDOWN(A1)', '=ROUNDDOWN(42, A1)', '=ROUNDDOWN(A1, FOO())'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('C2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-roundup.spec.ts b/test/unit/interpreter/function-roundup.spec.ts deleted file mode 100644 index 92569e54dd..0000000000 --- a/test/unit/interpreter/function-roundup.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ROUNDUP', () => { - it('number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUNDUP()', '=ROUNDUP(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works for positive numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUNDUP(1.3)', '=ROUNDUP(1.7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(2) - expect(engine.getCellValue(adr('B1'))).toBe(2) - }) - - it('works for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUNDUP(-1.3)', '=ROUNDUP(-1.7)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(-2) - expect(engine.getCellValue(adr('B1'))).toBe(-2) - }) - - it('works with positive rounding argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUNDUP(1.43, 1)', '=ROUNDUP(1.47, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1.5) - expect(engine.getCellValue(adr('B1'))).toBe(1.5) - }) - - it('works with negative rounding argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUNDUP(43, -1)', '=ROUNDUP(47, -1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(50) - expect(engine.getCellValue(adr('B1'))).toBe(50) - }) - - it('use coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROUNDUP("42.3")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(43) - }) - - it('propagates error', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/0'], - ['=ROUNDUP(A1)', '=ROUNDUP(42, A1)', '=ROUNDUP(A1, FOO())'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('C2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-row.spec.ts b/test/unit/interpreter/function-row.spec.ts deleted file mode 100644 index e07fac26fb..0000000000 --- a/test/unit/interpreter/function-row.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ROW', () => { - it('should take one or zero arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROW(B1, B2)'], - ['=ROW(B1, B2, B3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should take only reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROW(42)'], - ['=ROW("foo")'], - ['=ROW(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.CellRefExpected)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.CellRefExpected)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.CellRefExpected)) - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROW(1/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should return row of a reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROW(B1)'], - ['=ROW(B7)'], - ['=ROW(F$5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(7) - expect(engine.getCellValue(adr('A3'))).toEqual(5) - }) - - it('should work for itself', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROW(A1)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('should return row of a cell in which formula is', () => { - const engine = HyperFormula.buildFromArray([ - [null, '=ROW()'], - ['=ROW()'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - }) - - it('should return row of range start', () => { - const engine = HyperFormula.buildFromArray([ - ['=ROW(A3:A4)'], - ['=ROW(B1:B2)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - }) - - it('should be dependent on sheet structure changes', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['=ROW(A1)'] - ]) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - - engine.addRows(0, [0, 1]) - - expect(engine.getCellValue(adr('A3'))).toEqual(2) - }) -}) diff --git a/test/unit/interpreter/function-rows.spec.ts b/test/unit/interpreter/function-rows.spec.ts deleted file mode 100644 index 4316f07589..0000000000 --- a/test/unit/interpreter/function-rows.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function ROWS', () => { - it('accepts exactly one argument', () => { - const engine = HyperFormula.buildFromArray([['=ROWS()', '=ROWS(A2:A3, B2:B4)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works for range', () => { - const engine = HyperFormula.buildFromArray([['=ROWS(A1:C2)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('works for row range', () => { - const engine = HyperFormula.buildFromArray([['=ROWS(1:3)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('works for column range', () => { - const engine = HyperFormula.buildFromArray([['=ROWS(A:C)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(engine.getConfig().maxRows) - }) - - it('works for array', () => { - const engine = HyperFormula.buildFromArray([['=ROWS({1;2;3})']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('works with cell reference', () => { - const engine = HyperFormula.buildFromArray([['=ROWS(A1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('propagates only direct errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=4/0'], - ['=ROWS(4/0)'], - ['=ROWS(A1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - }) - - // Inconsistency with Product 1 - it('works with formulas', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1'], - ['1', '1'], - ['=ROWS(MMULT(A1:B2, A1:B2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(2) - }) - - it('should work when adding column', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=ROWS(A1:A2)'], - ['1'], - ]) - - engine.addRows(0, [1, 1]) - - expect(engine.getCellValue(adr('B1'))).toEqual(3) - }) -}) diff --git a/test/unit/interpreter/function-rri.spec.ts b/test/unit/interpreter/function-rri.spec.ts deleted file mode 100644 index bbe8607392..0000000000 --- a/test/unit/interpreter/function-rri.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function RRI', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=RRI(1,1)', '=RRI(1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=RRI(1, 2, 1)', '=RRI(2, 1, 2)', '=RRI(0.1, 2, 1)'], - ['=RRI(1, -1, -1)', '=RRI(1, -1, 1)', '=RRI(1, 1, -1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-0.5) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.414213562373095) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(-0.9990234375) - //inconsistency with product #1 (returns #NUM!) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.NUM)) - expect(engine.getCellValue(adr('C2'))).toEqualError(detailedError(ErrorType.NUM)) - }) -}) diff --git a/test/unit/interpreter/function-rsq.spec.ts b/test/unit/interpreter/function-rsq.spec.ts deleted file mode 100644 index b593d4b62f..0000000000 --- a/test/unit/interpreter/function-rsq.spec.ts +++ /dev/null @@ -1,92 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('RSQ', () => { - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=RSQ(B1:B5)'], - ['=RSQ(B1:B5, C1:C5, D1:D5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('ranges need to have same amount of elements', () => { - const engine = HyperFormula.buildFromArray([ - ['=RSQ(B1:B5, C1:C6)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.EqualLength)) - }) - - it('works (simple)', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['2', '20'], - ['=RSQ(A1:A2, B1:B2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBe(1) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['2', '4'], - ['5', '3'], - ['7', '6'], - ['1', '1'], - ['8', '5'], - ['=RSQ(A1:A5, B1:B5)'] - ]) - - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(0.628378378378378) - }) - - it('error when not enough data', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['=RSQ(A1:A1, B1:B1)'], - ['=RSQ(42, 43)'], - ['=RSQ("foo", "bar")'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.TwoValues)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.TwoValues)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.TwoValues)) - }) - - it('doesnt do coercions, nonnumeric values are skipped', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['="2"', '50'], - ['3', '30'], - ['=RSQ(A1:A3, B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(1) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ['=RSQ(MMULT(A1:B2, A1:B2), MMULT(B1:C2, B1:C2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.998496749220189, 6) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['=4/0', '50'], - ['3', '30'], - ['=RSQ(A1:A3, B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-search.spec.ts b/test/unit/interpreter/function-search.spec.ts deleted file mode 100644 index be47f9f734..0000000000 --- a/test/unit/interpreter/function-search.spec.ts +++ /dev/null @@ -1,157 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SEARCH', () => { - it('should return N/A when number of arguments is incorrect', () => { - const engine = HyperFormula.buildFromArray([ - ['=SEARCH()'], - ['=SEARCH("foo")'], - ['=SEARCH("foo", 1, 2, 3)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return VALUE when wrong type of third parameter', () => { - const engine = HyperFormula.buildFromArray([ - ['=SEARCH("foo", "bar", "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return VALUE if third parameter is not between 1 and text length', () => { - const engine = HyperFormula.buildFromArray([ - ['=SEARCH("foo", "bar", 0)'], - ['=SEARCH("foo", "bar", -1)'], - ['=SEARCH("foo", "bar", 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LengthBounds)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LengthBounds)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LengthBounds)) - }) - - it('should work with simple strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=SEARCH("f", "foo")'], - ['=SEARCH("o", "foo")'], - ['=SEARCH("o", "foo", 3)'], - ['=SEARCH("g", "foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.PatternNotFound)) - }) - - it('should work with wildcards', () => { - const engine = HyperFormula.buildFromArray([ - ['=SEARCH("*f", "foobarbaz")'], - ['=SEARCH("b*b", "foobarbaz")'], - ['=SEARCH("b?z", "foobarbaz")'], - ['=SEARCH("b?b", "foobarbaz")'], - ['=SEARCH("?b", "foobarbaz", 5)'], - ['=SEARCH("?b~", "foobarb~az", 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(4) - expect(engine.getCellValue(adr('A3'))).toEqual(7) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.PatternNotFound)) - expect(engine.getCellValue(adr('A5'))).toEqual(6) - expect(engine.getCellValue(adr('A6'))).toEqual(6) - }) - - it('should work with regular expressions', () => { - const engine = HyperFormula.buildFromArray([ - ['=SEARCH(".*f", "foobarbaz")'], - ['=SEARCH("b.*b", "foobarbaz")'], - ['=SEARCH("b.z", "foobarbaz")'], - ['=SEARCH("b.b", "foobarbaz")'], - ['=SEARCH(".b", "foobarbaz", 5)'], - ['=SEARCH(".B", "fooBarBaz")'], - ['=SEARCH(".B", "fooBarBaz", 5)'], - ['=SEARCH(".b", "fooBarBaz")'], - ['=SEARCH(".b", "fooBarBaz", 5)'], - ], {useRegularExpressions: true}) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(4) - expect(engine.getCellValue(adr('A3'))).toEqual(7) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.PatternNotFound)) - expect(engine.getCellValue(adr('A5'))).toEqual(6) - expect(engine.getCellValue(adr('A6'))).toEqual(3) - expect(engine.getCellValue(adr('A7'))).toEqual(6) - expect(engine.getCellValue(adr('A8'))).toEqual(3) - expect(engine.getCellValue(adr('A9'))).toEqual(6) - }) - - it('should work with regular expressions also when "caseSensitive: true" is set', () => { - const engine = HyperFormula.buildFromArray([ - ['=SEARCH(".*f", "foobarbaz")'], - ['=SEARCH("b.*b", "foobarbaz")'], - ['=SEARCH("b.z", "foobarbaz")'], - ['=SEARCH("b.b", "foobarbaz")'], - ['=SEARCH(".b", "foobarbaz", 5)'], - ['=SEARCH(".B", "fooBarBaz")'], - ['=SEARCH(".B", "fooBarBaz", 5)'], - ['=SEARCH(".b", "fooBarBaz")'], - ['=SEARCH(".b", "fooBarBaz", 5)'], - ], { useRegularExpressions: true, caseSensitive: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(4) - expect(engine.getCellValue(adr('A3'))).toEqual(7) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.PatternNotFound)) - expect(engine.getCellValue(adr('A5'))).toEqual(6) - expect(engine.getCellValue(adr('A6'))).toEqual(3) - expect(engine.getCellValue(adr('A7'))).toEqual(6) - expect(engine.getCellValue(adr('A8'))).toEqual(3) - expect(engine.getCellValue(adr('A9'))).toEqual(6) - }) - - it('should be case insensitive', () => { - const engine = HyperFormula.buildFromArray([ - ['=SEARCH("R", "bar")'], - ['=SEARCH("r", "baR")'], - ['=SEARCH("?R", "bar")'], - ['=SEARCH("*r", "baR")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - expect(engine.getCellValue(adr('A2'))).toEqual(3) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - }) - - it('should be case insensitive even when "caseSensitive: true" is set', () => { - const engine = HyperFormula.buildFromArray([ - ['=SEARCH("R", "bar")'], - ['=SEARCH("r", "baR")'], - ['=SEARCH("?R", "bar")'], - ['=SEARCH("*r", "baR")'], - ], { caseSensitive: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - expect(engine.getCellValue(adr('A2'))).toEqual(3) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - }) - - it('should coerce other types to string', () => { - const engine = HyperFormula.buildFromArray([ - ['=SEARCH(1, 1, 1)'], - ['=SEARCH(0, 5+5)'], - ['=SEARCH("U", TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - }) -}) diff --git a/test/unit/interpreter/function-sec.spec.ts b/test/unit/interpreter/function-sec.spec.ts deleted file mode 100644 index 8f5c72a5e6..0000000000 --- a/test/unit/interpreter/function-sec.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SEC', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=SEC(0)', '=SEC(1)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(1.85081571768093) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=SEC("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=SEC()', '=SEC(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=SEC(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(1.85081571768093) - }) - - it('close to div/zero', () => { - const engine = HyperFormula.buildFromArray([ - [1.57079632679486, '=SEC(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(27249001701268.1, -3) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=SEC(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-sech.spec.ts b/test/unit/interpreter/function-sech.spec.ts deleted file mode 100644 index d4c0ef5664..0000000000 --- a/test/unit/interpreter/function-sech.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SECH', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=SECH(0)', '=SECH(0.5)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.886818883970074) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=SECH("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=SECH()', '=SECH(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=SECH(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.648054273663886) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=SECH(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-second.spec.ts b/test/unit/interpreter/function-second.spec.ts deleted file mode 100644 index eef2653bf7..0000000000 --- a/test/unit/interpreter/function-second.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SECOND', () => { - it('with wrong arguments', () => { - const engine = HyperFormula.buildFromArray([['=SECOND("foo")', '=SECOND("12/30/2018")', '=SECOND(1, 2)', '=SECOND()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('with numerical arguments', () => { - const engine = HyperFormula.buildFromArray([['=SECOND(0.5123456)', '=SECOND(0)', '=SECOND(0.999999)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(47) - expect(engine.getCellValue(adr('B1'))).toEqual(0) - expect(engine.getCellValue(adr('C1'))).toEqual(0) - }) - - it('with string arguments', () => { - const engine = HyperFormula.buildFromArray([['=SECOND("14:42:59")', '=SECOND("01/01/1900 03:01:02am")', '=SECOND("31/12/2018")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(59) - expect(engine.getCellValue(adr('B1'))).toEqual(2) - expect(engine.getCellValue(adr('C1'))).toEqual(0) - }) - - it('use datenumber coercion for 1st argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=SECOND(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) - - it('propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=SECOND(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-seriessum.spec.ts b/test/unit/interpreter/function-seriessum.spec.ts deleted file mode 100644 index 77eedea191..0000000000 --- a/test/unit/interpreter/function-seriessum.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SERIESSUM', () => { - it('checks required number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SERIESSUM(1,2,3)'], - ['=SERIESSUM(1,2,3,4,5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('computes correct answer', () => { - const engine = HyperFormula.buildFromArray([ - ['=SERIESSUM(2,3,4,A2:D2)'], - [1, 2, 3, 4] - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(137480) - }) - - it('ignores nulls', () => { - const engine = HyperFormula.buildFromArray([ - ['=SERIESSUM(2,3,4,A2:D2)'], - [1, null, 3, 4] - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(8584) - }) - - it('throws error for non-numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=SERIESSUM(2,3,4,A2:D2)'], - [1, '\'1', 3, 4] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberExpected)) - }) - - it('works for non-integer args', () => { - const engine = HyperFormula.buildFromArray([ - ['=SERIESSUM(2,3.1,4,A3:D3)'], - ['=SERIESSUM(2,3,4.1,A3:D3)'], - [1, 2, 3, 4] - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(147347.41562949, 5) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(168708.537245456, 5) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=SERIESSUM(2,3,4,A2:D2)'], - [1, '=NA()', 3, 4] - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-sheet.spec.ts b/test/unit/interpreter/function-sheet.spec.ts deleted file mode 100644 index d4864e287d..0000000000 --- a/test/unit/interpreter/function-sheet.spec.ts +++ /dev/null @@ -1,88 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SHEET', () => { - it('should return formula sheet number', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=SHEET()']], - 'Sheet2': [['=SHEET()']], - }) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A1', 1))).toEqual(2) - }) - - it('should return reference sheet number for self sheet reference', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=SHEET(B1)']], - 'Sheet2': [['=SHEET(B1)', '=1/0']], - }) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A1', 1))).toEqual(2) - }) - - it('should return reference sheet number for absolute sheet reference', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=SHEET(Sheet1!B1)', '=SHEET(Sheet2!B1)']], - 'Sheet2': [['=SHEET(Sheet1!B1)', '=SHEET(Sheet2!B2)']], - }) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('B1'))).toEqual(2) - expect(engine.getCellValue(adr('A1', 1))).toEqual(1) - expect(engine.getCellValue(adr('B1', 1))).toEqual(2) - }) - - it('should return range sheet number', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=SHEET(B1:B2)', '=SHEET(Sheet2!A1:B1)']], - 'Sheet2': [['=SHEET(B1:B2)', '=SHEET(Sheet1!A1:B1)']], - }) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('B1'))).toEqual(2) - expect(engine.getCellValue(adr('A1', 1))).toEqual(2) - expect(engine.getCellValue(adr('B1', 1))).toEqual(1) - }) - - it('should return VALUE for non existing sheet', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=SHEET("FOO")', '=SHEET(1)']], - }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.SheetRef)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.SheetRef)) - }) - - it('should coerce', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addSheet('TRUE') - engine.addSheet('1') - - engine.setCellContents(adr('A1'), [['=SHEET(1=1)']]) - engine.setCellContents(adr('B1'), [['=SHEET(1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(engine.getCellValue(adr('B1'))).toEqual(3) - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([['=SHEET(1/0)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should work for itself', () => { - const engine = HyperFormula.buildFromArray([['=SHEET(A1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('should make cycle for non-refs', () => { - const engine = HyperFormula.buildFromArray([['=SHEET(1+A1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.CYCLE)) - }) -}) diff --git a/test/unit/interpreter/function-sheets.spec.ts b/test/unit/interpreter/function-sheets.spec.ts deleted file mode 100644 index 4435d71a71..0000000000 --- a/test/unit/interpreter/function-sheets.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SHEETS', () => { - it('should return number of sheets if no argument provided', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=SHEETS()']], - 'Sheet2': [], - }) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('should return 1 for a valid reference', () => { - const engine = HyperFormula.buildFromArray([['=SHEETS(B1)', '=SHEETS(A1:A2)', '=SHEETS(A:B)', '=SHEETS(1:2)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('C1'))).toEqual(1) - expect(engine.getCellValue(adr('D1'))).toEqual(1) - }) - - it('should return VALUE for non-reference parameter', () => { - const engine = HyperFormula.buildFromArray([['=SHEETS(1)', '=SHEETS("foo")', '=SHEETS(TRUE())']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.CellRefExpected)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.CellRefExpected)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.CellRefExpected)) - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([['=SHEETS(1/0)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should work for itself', () => { - const engine = HyperFormula.buildFromArray([['=SHEETS(A1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) -}) diff --git a/test/unit/interpreter/function-sign.spec.ts b/test/unit/interpreter/function-sign.spec.ts deleted file mode 100644 index b7aafd1b5a..0000000000 --- a/test/unit/interpreter/function-sign.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SIGN', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SIGN()'], - ['=SIGN(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=SIGN("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=SIGN(5)'], - ['=SIGN(0)'], - ['=SIGN(-12.1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toEqual(-1) - }) -}) diff --git a/test/unit/interpreter/function-sin.spec.ts b/test/unit/interpreter/function-sin.spec.ts deleted file mode 100644 index 4c3f4ffadf..0000000000 --- a/test/unit/interpreter/function-sin.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SIN', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=SIN(0)', '=SIN(0.5)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.479425538604203) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=SIN("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=SIN()', '=SIN(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=SIN(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-0.841470984807897) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=SIN(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-sinh.spec.ts b/test/unit/interpreter/function-sinh.spec.ts deleted file mode 100644 index 54bc18ba7b..0000000000 --- a/test/unit/interpreter/function-sinh.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SINH', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=SINH(0)', '=SINH(0.5)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.521095305493747) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=SINH("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=SINH()', '=SINH(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=SINH(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-1.1752011936438) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=SINH(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-skew.p.spec.ts b/test/unit/interpreter/function-skew.p.spec.ts deleted file mode 100644 index 340769c7f2..0000000000 --- a/test/unit/interpreter/function-skew.p.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SKEW.P', () => { - it('simple case', () => { - const engine = HyperFormula.buildFromArray([ - ['=SKEW.P(1, 2, 4, 8)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.6568077345, 6) - }) - - it('works with ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '9', '0', '10'], - ['=SKEW.P(A1:D1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0164833284967738, 6) - }) - - it('propagates error from regular argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=NA()', '=SKEW.P(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('propagates first error from range argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=NA()', '=FOO(', '=SKEW.P(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NA)) - }) - - /** - * product #1 does not coerce the input - */ - it('does coercions of nonnumeric explicit arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SKEW.P(TRUE(),FALSE(),)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.707106781186548, 6) - }) - - it('ignores nonnumeric values in ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['=SKEW.P(A2:F2)'], - [1, 0, 0, false, null, '\'0'] - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.707106781186548, 6) - }) - - it('validates range size', () => { - const engine = HyperFormula.buildFromArray([ - ['=SKEW.P(0,0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.ThreeValues)) - }) -}) diff --git a/test/unit/interpreter/function-skew.spec.ts b/test/unit/interpreter/function-skew.spec.ts deleted file mode 100644 index f49fbbcbdf..0000000000 --- a/test/unit/interpreter/function-skew.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SKEW', () => { - it('simple case', () => { - const engine = HyperFormula.buildFromArray([ - ['=SKEW(1, 2, 4, 8)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.137624367, 6) - }) - - it('works with ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '9', '0', '10'], - ['=SKEW(A1:D1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.02854996243, 6) - }) - - it('propagates error from regular argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=NA()', '=SKEW(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('propagates first error from range argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=NA()', '=FOO(', '=SKEW(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NA)) - }) - - /** - * product #1 does not coerce the input - */ - it('does coercions of nonnumeric explicit arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SKEW(TRUE(),FALSE(),)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.732050808, 6) - }) - - it('ignores nonnumeric values in ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['=SKEW(A2:F2)'], - [1, 0, 0, false, null, '\'0'] - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.732050808, 6) - }) - - it('validates range size', () => { - const engine = HyperFormula.buildFromArray([ - ['=SKEW(0,0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.ThreeValues)) - }) -}) diff --git a/test/unit/interpreter/function-sln.spec.ts b/test/unit/interpreter/function-sln.spec.ts deleted file mode 100644 index b873f6e769..0000000000 --- a/test/unit/interpreter/function-sln.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SLN', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SLN(1,1)', '=SLN(1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=SLN(1, 2, 1)', '=SLN(2, 1, -2)', '=SLN(3, 2, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-1) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-0.5) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-slope.spec.ts b/test/unit/interpreter/function-slope.spec.ts deleted file mode 100644 index ef49acddcb..0000000000 --- a/test/unit/interpreter/function-slope.spec.ts +++ /dev/null @@ -1,93 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('SLOPE', () => { - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SLOPE(B1:B5)'], - ['=SLOPE(B1:B5, C1:C5, D1:D5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('ranges need to have same amount of elements', () => { - const engine = HyperFormula.buildFromArray([ - ['=SLOPE(B1:B5, C1:C6)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.EqualLength)) - }) - - it('works (simple)', () => { - const engine = HyperFormula.buildFromArray([ - [0, 0, 1], - [0, 1, 0], - ['=SLOPE(A1:C1, A2:C2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(-0.5) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['2', '4'], - ['5', '3'], - ['7', '6'], - ['1', '1'], - ['8', '5'], - ['=SLOPE(A1:A5, B1:B5)'] - ]) - - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(1.256756757, 6) - }) - - it('error when not enough data', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['=SLOPE(A1:A1, B1:B1)'], - ['=SLOPE(42, 43)'], - ['=SLOPE("foo", "bar")'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.TwoValues)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.TwoValues)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.TwoValues)) - }) - - it('doesnt do coercions, nonnumeric values are skipped', () => { - const engine = HyperFormula.buildFromArray([ - [0, 0], - ['="2"', '50'], - [1, 0], - [0, 1], - ['=SLOPE(A1:A4, B1:B4)'], - ]) - - expect(engine.getCellValue(adr('A5'))).toEqual(-0.5) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ['=SLOPE(MMULT(A1:B2, A1:B2), MMULT(B1:C2, B1:C2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.75346687211094, 6) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['=NA()', '50'], - ['3', '30'], - ['=SLOPE(A1:A3, B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-small.spec.ts b/test/unit/interpreter/function-small.spec.ts deleted file mode 100644 index b7b239f564..0000000000 --- a/test/unit/interpreter/function-small.spec.ts +++ /dev/null @@ -1,86 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SMALL', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SMALL(1)'], - ['=SMALL(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=SMALL(1, "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=SMALL(A2:D2,0)', '=SMALL(A2:D2,1)', '=SMALL(A2:D2,2)', '=SMALL(A2:D2,3)', '=SMALL(A2:D2,4)', '=SMALL(A2:D2,5)'], - [1, 4, 2, 4], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('C1'))).toEqual(2) - expect(engine.getCellValue(adr('D1'))).toEqual(4) - expect(engine.getCellValue(adr('E1'))).toEqual(4) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('should ignore non-numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=SMALL(A2:D2,0)', '=SMALL(A2:D2,1)', '=SMALL(A2:D2,2)', '=SMALL(A2:D2,3)'], - [1, 4, true, 'abcd'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('C1'))).toEqual(4) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=SMALL(A2:D2,0)', '=SMALL(A2:D2,1)', '=SMALL(A2:D2,2)', '=SMALL(A2:D2,3)'], - [1, 4, '=NA()', 'abcd'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NA)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('should truncate second arg', () => { - const engine = HyperFormula.buildFromArray([ - ['=SMALL(A2:D2,0.9)', '=SMALL(A2:D2,1.9)', '=SMALL(A2:D2,2.9)', '=SMALL(A2:D2,3.9)', '=SMALL(A2:D2,4.9)', '=SMALL(A2:D2,5.9)'], - [1, 4, 2, 4], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('C1'))).toEqual(2) - expect(engine.getCellValue(adr('D1'))).toEqual(4) - expect(engine.getCellValue(adr('E1'))).toEqual(4) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) - - it('should work for non-ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['=SMALL(1,0)', '=SMALL(1,1)', '=SMALL(1,2)', '=SMALL(TRUE(),1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('B1'))).toEqual(1) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - }) -}) diff --git a/test/unit/interpreter/function-split.spec.ts b/test/unit/interpreter/function-split.spec.ts deleted file mode 100644 index 16bebe4bac..0000000000 --- a/test/unit/interpreter/function-split.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SPLIT', () => { - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=SPLIT(1)', '=SPLIT("a","b","c")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['some words', '=SPLIT(A1, 0)']]) - - expect(engine.getCellValue(adr('B1'))).toEqual('some') - }) - - it('bigger index', () => { - const engine = HyperFormula.buildFromArray([['some words', '=SPLIT(A1, 1)']]) - - expect(engine.getCellValue(adr('B1'))).toEqual('words') - }) - - it('when continuous delimeters', () => { - const engine = HyperFormula.buildFromArray([['some words', '=SPLIT(A1, 1)', '=SPLIT(A1, 2)']]) - - expect(engine.getCellValue(adr('B1'))).toEqual('') - expect(engine.getCellValue(adr('C1'))).toEqual('words') - }) - - it('coerce first argument to string', () => { - const engine = HyperFormula.buildFromArray([['42', '=SPLIT(A1, 1)']]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.IndexBounds)) - }) - - it('when 2nd arg not a number', () => { - const engine = HyperFormula.buildFromArray([['some words', '=SPLIT(A1, "foo")']]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('when index arg is not value within bounds', () => { - const engine = HyperFormula.buildFromArray([['some words', '=SPLIT(A1, 17)', '=SPLIT(A1, -1)']]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.IndexBounds)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.IndexBounds)) - }) -}) diff --git a/test/unit/interpreter/function-sqrt.spec.ts b/test/unit/interpreter/function-sqrt.spec.ts deleted file mode 100644 index 11ffd2337e..0000000000 --- a/test/unit/interpreter/function-sqrt.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SQRT', () => { - it('should return error for negative numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=SQRT(-2)'], - ['=SQRT(-2)+1'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SQRT()'], - ['=SQRT(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=SQRT("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=SQRT(0)'], - ['=SQRT(16)'], - ['=SQRT(2)'], - ], {smartRounding: false}) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toEqual(4) - expect(engine.getCellValue(adr('A3'))).toEqual(1.4142135623730951) - }) -}) diff --git a/test/unit/interpreter/function-sqrtpi.spec.ts b/test/unit/interpreter/function-sqrtpi.spec.ts deleted file mode 100644 index 101c15d41a..0000000000 --- a/test/unit/interpreter/function-sqrtpi.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SQRTPI', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SQRTPI()', '=SQRTPI(1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['=SQRTPI(0)'], - ['=SQRTPI(1)'], - ['=SQRTPI(PI())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(1.77245385090552, 6) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(3.14159265358979, 6) - }) - - it('pass error', () => { - const engine = HyperFormula.buildFromArray([ - ['=SQRTPI(NA())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-standardize.spec.ts b/test/unit/interpreter/function-standardize.spec.ts deleted file mode 100644 index de5e9ee615..0000000000 --- a/test/unit/interpreter/function-standardize.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function STANDARDIZE', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=STANDARDIZE(1, 2)'], - ['=STANDARDIZE(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=STANDARDIZE("foo", 1, 2)'], - ['=STANDARDIZE(1, "foo", 2)'], - ['=STANDARDIZE(1, 2, "foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=STANDARDIZE(1, 2, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(-0.25) - }) - - it('should check bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=STANDARDIZE(1, 2, 0.001)'], - ['=STANDARDIZE(1, 2, 0)'], - ['=STANDARDIZE(1, 2, -0.001)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(-1000) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-stdev.p.spec.ts b/test/unit/interpreter/function-stdev.p.spec.ts deleted file mode 100644 index 2e3ae7fc54..0000000000 --- a/test/unit/interpreter/function-stdev.p.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function STDEV.P', () => { - it('should take at least one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEV.P()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate standard deviation (population)', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEV.P(2, 3)'], - ['=STDEV.P(1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(0.5) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) - - it('should coerce explicit argument to numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEV.P(2, 3, 4, TRUE(), FALSE(), "1",)'], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.39970842444753, 6) //inconsistency with product #1 - }) - - it('should ignore non-numeric values in ranges, including ignoring logical values and text representation of numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEV.P(B1:I1)', 2, 3, 4, true, false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.816496580927726, 6) - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEV.P(B1:I1)', 2, 3, 4, '=NA()', false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-stdev.s.spec.ts b/test/unit/interpreter/function-stdev.s.spec.ts deleted file mode 100644 index 4fb94c8570..0000000000 --- a/test/unit/interpreter/function-stdev.s.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function STDEV.S', () => { - it('should take at least two arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEV.S()'], - ['=STDEV.S(1)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should calculate standard deviation (sample)', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEV.S(2, 3)'], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.707106781186548, 6) - }) - - it('should coerce explicit argument to numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEV.S(2, 3, 4, TRUE(), FALSE(), "1",)'], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.51185789203691, 6) //inconsistency with product #1 - }) - - it('should ignore non-numeric values in ranges, including ignoring logical values and text representation of numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEV.S(B1:I1)', 2, 3, 4, true, false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEV.S(B1:I1)', 2, 3, 4, '=NA()', false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-stdeva.spec.ts b/test/unit/interpreter/function-stdeva.spec.ts deleted file mode 100644 index 7a309e800d..0000000000 --- a/test/unit/interpreter/function-stdeva.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function STDEVA', () => { - it('should take at least two arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEVA()'], - ['=STDEVA(1)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should calculate standard deviation (sample)', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEVA(2, 3)'], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.707106781186548, 11) - }) - - it('should coerce explicit argument to numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEVA(2, 3, 4, TRUE(), FALSE(), "1",)'], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.51185789203691) - }) - - it('should evaluate TRUE to 1, FALSE to 0 and text to 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEVA(B1:I1)', 2, 3, 4, true, false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.61834718742537) - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEVA(B1:I1)', 2, 3, 4, '=NA()', false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-stdevpa.spec.ts b/test/unit/interpreter/function-stdevpa.spec.ts deleted file mode 100644 index b74b51749b..0000000000 --- a/test/unit/interpreter/function-stdevpa.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function STDEVPA', () => { - it('should take at least one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEVPA()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate standard deviation (population)', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEVPA(2, 3)'], - ['=STDEVPA(1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(0.5) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) - - it('should coerce explicit argument to numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEVPA(2, 3, 4, TRUE(), FALSE(), "1",)'], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.39970842444753, 6) - }) - - it('should evaluate TRUE to 1, FALSE to 0 and text to 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEVPA(B1:I1)', 2, 3, 4, true, false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.49829835452879, 6) - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=STDEVPA(B1:I1)', 2, 3, 4, '=NA()', false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-steyx.spec.ts b/test/unit/interpreter/function-steyx.spec.ts deleted file mode 100644 index 753f2dafd7..0000000000 --- a/test/unit/interpreter/function-steyx.spec.ts +++ /dev/null @@ -1,93 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('STEYX', () => { - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=STEYX(B1:B5)'], - ['=STEYX(B1:B5, C1:C5, D1:D5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('ranges need to have same amount of elements', () => { - const engine = HyperFormula.buildFromArray([ - ['=STEYX(B1:B5, C1:C6)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.EqualLength)) - }) - - it('works (simple)', () => { - const engine = HyperFormula.buildFromArray([ - [0, 0, 1], - [0, 1, 0], - ['=STEYX(A1:C1, A2:C2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.7071067812, 6) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['2', '4'], - ['5', '3'], - ['7', '6'], - ['1', '1'], - ['8', '5'], - ['=STEYX(A1:A5, B1:B5)'] - ]) - - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(2.146650439, 6) - }) - - it('error when not enough data', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['=STEYX(A1:B1, A1:B1)'], - ['=STEYX(42, 43)'], - ['=STEYX("foo", "bar")'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.ThreeValues)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.ThreeValues)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.ThreeValues)) - }) - - it('doesnt do coercions, nonnumeric values are skipped', () => { - const engine = HyperFormula.buildFromArray([ - [0, 0], - ['="2"', '50'], - [1, 0], - [0, 1], - ['=STEYX(A1:A4, B1:B4)'], - ]) - - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(0.707106781186548, 6) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['4', '5', '6'], - ['=STEYX(MMULT(A1:B2, A1:B2), MMULT(B1:C2, B1:C2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.526640075265055, 6) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['=NA()', '50'], - ['3', '30'], - ['=STEYX(A1:A3, B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-substitute.spec.ts b/test/unit/interpreter/function-substitute.spec.ts deleted file mode 100644 index 5f3b09687c..0000000000 --- a/test/unit/interpreter/function-substitute.spec.ts +++ /dev/null @@ -1,145 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SUBSTITUTE', () => { - it('should take three or four parameters', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBSTITUTE("foo", "f")'], - ['=SUBSTITUTE("foobar", "o", "uu", 4, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should substitute new text for all occurrences of old text in a string', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBSTITUTE("foo", "f", "bb")'], - ['=SUBSTITUTE("foobar", "o", "uu")'], - ['=SUBSTITUTE("fooobar", "oo", "x")'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('bboo') - expect(engine.getCellValue(adr('A2'))).toEqual('fuuuubar') - expect(engine.getCellValue(adr('A3'))).toEqual('fxobar') - }) - - it('should substitute new text for nth occurrence of a string', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBSTITUTE("foobar", "o", "f", 1)'], - ['=SUBSTITUTE("foobar", "o", "OO", 2)'], - ['=SUBSTITUTE("foobar", "o", "OO", 3)'], - ['=SUBSTITUTE("fofofofofo", "o", "u", 4)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('ffobar') - expect(engine.getCellValue(adr('A2'))).toEqual('foOObar') - expect(engine.getCellValue(adr('A3'))).toEqual('foobar') - expect(engine.getCellValue(adr('A4'))).toEqual('fofofofufo') - }) - - it('should return the original text if there are not enough occurrences of the search string', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBSTITUTE("foobar", "o", "BAZ", 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('foobar') - }) - - it('should accept "." character in the search string', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBSTITUTE("foo.bar", ".", "BAZ")'], - ['=SUBSTITUTE("foo.bar", "foo.", "BAZ")'], - ['=SUBSTITUTE("foo.bar", ".bar", "BAZ")'], - ['=SUBSTITUTE("foo.foo.foo.bar.", ".", "BAZ", 1)'], - ['=SUBSTITUTE("foo.foo.foo.bar.", ".", "BAZ", 2)'], - ['=SUBSTITUTE("foo.foo.foo.bar.", ".", "BAZ", 3)'], - ['=SUBSTITUTE("foo.foo.foo.bar.", ".", "BAZ", 4)'], - ['=SUBSTITUTE("foo.foo.foo.bar.", ".", "BAZ", 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('fooBAZbar') - expect(engine.getCellValue(adr('A2'))).toEqual('BAZbar') - expect(engine.getCellValue(adr('A3'))).toEqual('fooBAZ') - expect(engine.getCellValue(adr('A4'))).toEqual('fooBAZfoo.foo.bar.') - expect(engine.getCellValue(adr('A5'))).toEqual('foo.fooBAZfoo.bar.') - expect(engine.getCellValue(adr('A6'))).toEqual('foo.foo.fooBAZbar.') - expect(engine.getCellValue(adr('A7'))).toEqual('foo.foo.foo.barBAZ') - expect(engine.getCellValue(adr('A8'))).toEqual('foo.foo.foo.bar.') - }) - - it('should accept regexp special characters in the search string', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBSTITUTE("foo[bar", "[", "BAZ")'], - ['=SUBSTITUTE("foo]bar", "]", "BAZ")'], - ['=SUBSTITUTE("foo-bar", "-", "BAZ")'], - ['=SUBSTITUTE("foo*bar", "*", "BAZ")'], - ['=SUBSTITUTE("foo+bar", "+", "BAZ")'], - ['=SUBSTITUTE("foo?bar", "?", "BAZ")'], - ['=SUBSTITUTE("foo^bar", "^", "BAZ")'], - ['=SUBSTITUTE("foo$bar", "$", "BAZ")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('fooBAZbar') - expect(engine.getCellValue(adr('A2'))).toEqual('fooBAZbar') - expect(engine.getCellValue(adr('A3'))).toEqual('fooBAZbar') - expect(engine.getCellValue(adr('A4'))).toEqual('fooBAZbar') - expect(engine.getCellValue(adr('A5'))).toEqual('fooBAZbar') - expect(engine.getCellValue(adr('A6'))).toEqual('fooBAZbar') - expect(engine.getCellValue(adr('A7'))).toEqual('fooBAZbar') - expect(engine.getCellValue(adr('A8'))).toEqual('fooBAZbar') - }) - - it('should work with search strings that look like regular expressions', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBSTITUTE("foo.*bar", ".*", "BAZ")'], - ['=SUBSTITUTE("foo[a-z]+bar", "[a-z]+", "BAZ")'], - ['=SUBSTITUTE("foo[^-]bar", "[^-]", "BAZ")'], - ['=SUBSTITUTE("foo[^*]bar", "[^*]", "BAZ")'], - ['=SUBSTITUTE("foo/.*/bar", "/.*/", "BAZ")'], - ['=SUBSTITUTE("foo\\sbar", "\\s", "BAZ")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('fooBAZbar') - expect(engine.getCellValue(adr('A2'))).toEqual('fooBAZbar') - expect(engine.getCellValue(adr('A3'))).toEqual('fooBAZbar') - expect(engine.getCellValue(adr('A4'))).toEqual('fooBAZbar') - expect(engine.getCellValue(adr('A5'))).toEqual('fooBAZbar') - expect(engine.getCellValue(adr('A6'))).toEqual('fooBAZbar') - }) - - it('should coerce', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBSTITUTE("foobar", "o", TRUE(), 1)'], - ['=SUBSTITUTE("fooTRUE", TRUE(), 5, 1)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('fTRUEobar') - expect(engine.getCellValue(adr('A2'))).toEqual('foo5') - }) - - it('should return value when last argument is less than one', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBSTITUTE("foobar", "o", "f", 0)'], - ['=SUBSTITUTE("foobar", "o", "OO", -1)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - }) - - it('should return value when arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBSTITUTE("foobar", "o", "f", "bar")'], - ['=SUBSTITUTE(B1:C1, "o", "f", 3)'], - ['=SUBSTITUTE("foobar", B1:C1, "f", 3)'], - ['=SUBSTITUTE("foobar", "o", B1:C1, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) -}) diff --git a/test/unit/interpreter/function-subtotal.spec.ts b/test/unit/interpreter/function-subtotal.spec.ts deleted file mode 100644 index f99b78dd19..0000000000 --- a/test/unit/interpreter/function-subtotal.spec.ts +++ /dev/null @@ -1,173 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SUBTOTAL', () => { - it('should calculate AVERAGE', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBTOTAL(1, A2:A4, A5)', '=SUBTOTAL(101, A2:A4, A5)'], - [2], - [3], - [4], - [5] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3.5) - expect(engine.getCellValue(adr('B1'))).toEqual(3.5) - }) - - it('should calculate COUNT', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBTOTAL(2, A2:A4, A5)', '=SUBTOTAL(102, A2:A4, A5)'], - [2], - ['foo'], - [4], - [5] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - expect(engine.getCellValue(adr('B1'))).toEqual(3) - }) - - it('should calculate COUNTA', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBTOTAL(3, A2:A4, A5)', '=SUBTOTAL(103, A2:A4, A5)'], - [2], - ['foo'], - [4], - [5] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - expect(engine.getCellValue(adr('B1'))).toEqual(4) - }) - - it('should calcuate MAX', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBTOTAL(4, A2:A4, A5)', '=SUBTOTAL(104, A2:A4, A5)'], - [3], - [5], - [2], - [4] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(5) - expect(engine.getCellValue(adr('B1'))).toEqual(5) - }) - - it('should calculate MIN', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBTOTAL(5, A2:A4, A5)', '=SUBTOTAL(105, A2:A4, A5)'], - [3], - [5], - [2], - [4] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - expect(engine.getCellValue(adr('B1'))).toEqual(2) - }) - - it('should calculate PRODUCT', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBTOTAL(6, A2:A4, A5)', '=SUBTOTAL(106, A2:A4, A5)'], - [3], - [5], - [2], - [4] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(120) - expect(engine.getCellValue(adr('B1'))).toEqual(120) - }) - - it('should calculate STDEV.S', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBTOTAL(7, A2:A4, A5)', '=SUBTOTAL(107, A2:A4, A5)'], - [3], - [5], - [2], - [4] - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.29099444873581, 6) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(1.29099444873581, 6) - }) - - it('should calculate STDEV.P', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBTOTAL(8, A2:A4, A5)', '=SUBTOTAL(108, A2:A4, A5)'], - [3], - [5], - [2], - [4] - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.11803398875, 6) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(1.11803398875, 6) - }) - - it('should calculate SUM', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBTOTAL(9, A2:A4, A5)', '=SUBTOTAL(109, A2:A4, A5)'], - [3], - [5], - [2], - [4] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(14) - expect(engine.getCellValue(adr('B1'))).toEqual(14) - }) - - it('should calculate VAR.S', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBTOTAL(10, A2:A4, A5)', '=SUBTOTAL(110, A2:A4, A5)'], - [3], - [5], - [2], - [4] - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(5 / 3, 6) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(5 / 3, 6) - }) - - it('should calculate VAR.P', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBTOTAL(11, A2:A4, A5)', '=SUBTOTAL(111, A2:A4, A5)'], - [3], - [5], - [2], - [4] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(5 / 4) - expect(engine.getCellValue(adr('B1'))).toEqual(5 / 4) - }) - - it('should return correct error', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBTOTAL(12345, A2:A4, A5)'], - [3], - [5], - [2], - [4] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.BadMode)) - }) - - /** - * Inconsistency with ODFF standard. - */ - it('does not ignore other SUBTOTALS', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUBTOTAL(9, A2:A4)'], - ['=SUBTOTAL(9, B2:C2)', 1, 1], - ['=SUBTOTAL(9, B3:C3)', 1, 1], - ['=SUBTOTAL(9, B4:C4)', 1, 1], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(6) - }) -}) diff --git a/test/unit/interpreter/function-sum.spec.ts b/test/unit/interpreter/function-sum.spec.ts deleted file mode 100644 index 5435b42a52..0000000000 --- a/test/unit/interpreter/function-sum.spec.ts +++ /dev/null @@ -1,254 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('SUM', () => { - it('SUM without args', () => { - const engine = HyperFormula.buildFromArray([['=SUM()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('SUM with args', () => { - const engine = HyperFormula.buildFromArray([['=SUM(1, B1)', '3.14']]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(4.14) - }) - - it('SUM with range args', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '5'], - ['3', '4', '=SUM(A1:B2)'] - ]) - expect(engine.getCellValue(adr('C2'))).toEqual(10) - }) - - it('SUM with column range args', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '5'], - ['3', '4', '=SUM(A:B)'] - ]) - expect(engine.getCellValue(adr('C2'))).toEqual(10) - }) - - it('SUM with using previously cached value', () => { - const engine = HyperFormula.buildFromArray([ - ['3', '=SUM(A1:A1)'], - ['4', '=SUM(A1:A2)'], - ]) - expect(engine.getCellValue(adr('B2'))).toEqual(7) - }) - - it('doesnt do coercions', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['foo'], - ['=TRUE()'], - ['=CONCATENATE("1","0")'], - ['=SUM(A1:A5)'], - ['=SUM(A3)'], - ['=SUM(A4)'], - ['=SUM(A5)'], - ]) - - expect(engine.getCellValue(adr('A6'))).toEqual(3) - expect(engine.getCellValue(adr('A7'))).toEqual(0) - expect(engine.getCellValue(adr('A8'))).toEqual(0) - expect(engine.getCellValue(adr('A9'))).toEqual(0) - }) - - it('works when precision (default setting)', () => { - const engine = HyperFormula.buildFromArray([ - ['1.00000000000005', '-1'], - ['=SUM(A1:B1)'] - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) - - it('explicitly called does coercions', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(2,TRUE())'], - ['=SUM(2,"foo",TRUE())'], - ['=SUM(TRUE())'], - ['=SUM("10")'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(3) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(10) - }) - - it('doesnt take value from range if it does not store cached value for that function', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['=MAX(A1:A2)'], - ['=SUM(A1:A3)'], - ]) - expect(engine.getCellValue(adr('A4'))).toEqual(5) - }) - - it('range only with empty value', () => { - const engine = HyperFormula.buildFromArray([['', '=SUM(A1:A1)']]) - expect(engine.getCellValue(adr('B1'))).toEqual(0) - }) - - it('range only with some empty values', () => { - const engine = HyperFormula.buildFromArray([['42', '', '13', '=SUM(A1:C1)']]) - expect(engine.getCellValue(adr('D1'))).toEqual(55) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=SUM(MMULT(A1:B2, A1:B2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(54) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=4/0'], - ['=FOOBAR()', '4'], - ['=SUM(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('works with a column range reference to an empty sheet', () => { - const hf = HyperFormula.buildFromSheets({ - table1: [], - table2: [['=SUM(table1!A:C)']], - }) - - expect(hf.getCellValue(adr('A1', 1))).toEqual(0) - }) - - it('accepts 100k parameters', () => { - const numOfParams = 100000 - const argsList = Array(numOfParams).fill('1').map((_, i) => `A${i+2}`).join(', ') - const formula = `=SUM(${argsList})` - - const engine = HyperFormula.buildFromArray([ - [formula], - ...Array(numOfParams).fill([1]) - ], { maxRows: numOfParams+2 }) - - expect(engine.getCellValue(adr('A1'))).toEqual(numOfParams) - }) - - describe('works with reversed cell ranges', () => { - it('simple case', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=SUM(A1:B2)'], - ['=SUM(A2:B1)'], - ['=SUM(B1:A2)'], - ['=SUM(B2:A1)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(10) - expect(engine.getCellValue(adr('A4'))).toEqual(10) - expect(engine.getCellValue(adr('A5'))).toEqual(10) - expect(engine.getCellValue(adr('A6'))).toEqual(10) - }) - - describe('that has the same R1C1 representation (may cause cache clash)', () => { - it('when 1st row address is absolute and one of the ranges is a single value', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(B$2:B1)', 1], // R1C[+1]:R[+0]C[+1] - [' ', 2], - [' ', 3], - ['=SUM(B$2:B4)', 4], // R1C[+1]:R[+0]C[+1] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqual(9) - }) - - it('when 2nd row address is absolute and one of the ranges is a single value', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(B1:B$2)', 1], // R[+0]C[+1]:R1C[+1] - [' ', 2], - [' ', 3], - ['=SUM(B4:B$2)', 4], // R[+0]C[+1]:R1C[+1] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqual(9) - }) - - it('when 1st row address is absolute and one of the ranges is a single value (2)', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(B$2:B1)', 1], // R1C[+1]:R[+0]C[+1] - ['=SUM(B$2:B2)', 2], // R1C[+1]:R[+0]C[+1] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - }) - - it('when 2nd row address is absolute and one of the ranges is a single value (2)', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(B1:B$1)', 1], // R[+0]C[+1]:R0C[+1] - ['=SUM(B2:B$1)', 2], // R[+0]C[+1]:R0C[+1] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(3) - }) - }) - }) - - describe('works with reversed column ranges', () => { - it('when 1st address is absolute', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM($C:C)', '=SUM($C:D)', 1, 2], // C2:C[+2] // C2:C[+2] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('B1'))).toEqual(3) - }) - - it('when 2nd address is absolute', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(C:$C)', '=SUM(D:$C)', 1, 2], // C[+2]:C2 // C[+2]:C2 - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('B1'))).toEqual(3) - }) - }) - - describe('works with reversed row ranges', () => { - it('when 1st address is absolute', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM($4:3)'], // R3:R[+2] - ['=SUM($4:4)'], // R3:R[+2] - [1], - [2], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - }) - - it('when 2nd address is absolute', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(3:$3)'], // R[+2]:R2 - ['=SUM(4:$3)'], // R[+2]:R2 - [1], - [2], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(3) - }) - }) -}) diff --git a/test/unit/interpreter/function-sumif.spec.ts b/test/unit/interpreter/function-sumif.spec.ts deleted file mode 100644 index 120e569dca..0000000000 --- a/test/unit/interpreter/function-sumif.spec.ts +++ /dev/null @@ -1,773 +0,0 @@ -import {CellValueDetailedType, HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {plPL} from '../../../src/i18n/languages' -import {StatType} from '../../../src/statistics' -import {adr, detailedError, expectArrayWithSameContent} from '../testUtils' - -describe('Function SUMIF - argument validations and combinations', () => { - it('requires 2 or 3 arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMIF(C1)'], - ['=SUMIF(C1, ">0", C1, C1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works when 2nd arg is an integer', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMIF(C1:C2, 1, B1:B2)', 2, 1], - [null, 3, true], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('works when 2nd arg is a string', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMIF(C1:C2, "a", B1:B2)', 2, 'a'], - [null, 3, 'b'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('works when 2nd arg is an inline array', () => { - const engine = HyperFormula.buildFromArray([ - [2, 'a'], - [3, 'b'], - ['=SUMIF(B1:B2, {"a", "b"}, A1:A2)'] - ], { useArrayArithmetic: true }) - - expect(engine.getCellValue(adr('A3'))).toEqual(2) - expect(engine.getCellValue(adr('B3'))).toEqual(3) - }) - - it('works with an array as a second argument', () => { - const engine = HyperFormula.buildFromArray([ - [null, 2, 1], - [null, 3, 2], - ['=SUMIF(C1:C2, { 1, 2 }, B1:B2)'] - ], { useArrayArithmetic: true }) - - expect(engine.getCellValue(adr('A3'))).toEqual(2) - expect(engine.getCellValue(adr('B3'))).toEqual(3) - }) - - it('works when 2nd arg is a boolean', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMIF(C1:C2, TRUE(), B1:B2)', 2, 1], - [null, 3, true], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('works when 2nd arg is a string "true"', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMIF(C1:C2, "=TRUE", B1:B2)', 2, 1], - [null, 3, true], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('works when 2nd arg is a string "true" in different language', () => { - HyperFormula.registerLanguage('plPL', plPL) - const engine = HyperFormula.buildFromArray([ - ['=SUMIF(C1:C2, "=PRAWDA", B1:B2)', 2, 1], - [null, 3, true], - ], {language: 'plPL'}) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('error when criterion unparsable', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMIF(B1:B2, "> { - const engine = HyperFormula.buildFromArray([ - ['=SUMIF(B1:C1, ">0", B2:D2)'], - ['=SUMIF(B1, ">0", B2:D2)'], - ['=SUMIF(B1:D1, ">0", B2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - }) - - it('error when different height dimension of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMIF(B1:B2, ">0", C1:C3)'], - ['=SUMIF(B1, ">0", C1:C2)'], - ['=SUMIF(B1:B2, ">0", C1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - }) - - it('error when number of elements match but dimensions doesnt', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMIF(B1:B2, ">0", B1:C1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - }) - - it('scalars are treated like singular arrays', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMIF(10, ">1", 42)'], - ['=SUMIF(0, ">1", 42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(42) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) - - it('error propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMIF(4/0, ">1", 42)'], - ['=SUMIF(0, 4/0, 42)'], - ['=SUMIF(0, ">1", 4/0)'], - ['=SUMIF(0, 4/0, FOOBAR())'], - ['=SUMIF(4/0, FOOBAR(), 42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('works when arguments are just references', () => { - const engine = HyperFormula.buildFromArray([ - ['2', '3'], - ['=SUMIF(A1, ">1", B1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(3) - }) - - it('works with range values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1', '3', '5'], - ['1', '1', '7', '9'], - ['=SUMIF(MMULT(A1:B2, A1:B2), "=2", MMULT(C1:D2, C1:D2))'], - ['=SUMIF(A1:B2, "=1", MMULT(C1:D2, C1:D2))'], - ['=SUMIF(MMULT(A1:B2, A1:B2), "=2", C1:D2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(304) - expect(engine.getCellValue(adr('A4'))).toEqual(304) - expect(engine.getCellValue(adr('A5'))).toEqual(24) - }) - - it('works for mixed reference/range arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['2', '3'], - ['=SUMIF(A1:A1, ">1", B1)'], - ['=SUMIF(A1, ">1", B1:B1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(3) - expect(engine.getCellValue(adr('A3'))).toEqual(3) - }) - - it('works for 2 arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['10', '20', '30'], - ['=SUMIF(A1:C1, ">15")'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(50) - }) - - it('works for matrices', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=TRANSPOSE(A1:B1)'], - [], - ['=SUMIF(A2:A3, ">0", A2:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(3) - }) -}) - -describe('Function SUMIF(S) - calculations and optimizations', () => { - it('no coercion when sum', () => { - const engine = HyperFormula.buildFromArray([ - ['2', '="3"'], - ['=SUMIF(A1, ">1", B1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) - - it('empty coercions', () => { - const engine = HyperFormula.buildFromArray([ - [1, null], - [2, 8], - [3, 9], - ['=SUMIF(B1:B3,"=",A1:A3)'], - ['=SUMIF(B1:B3,">=",A1:A3)'], - ['=SUMIF(B1:B3,"<=",A1:A3)'], - ['=SUMIF(B1:B3,"<>",A1:A3)'], - ]) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - expect(engine.getCellValue(adr('A6'))).toEqual(0) - expect(engine.getCellValue(adr('A7'))).toEqual(5) - }) - - it('works for subranges with different conditions', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1', '=SUMIF(A1:A1,"="&A1,B1:B1)'], - ['2', '1', '=SUMIF(A1:A2,"="&A2,B1:B2)'], - ['1', '1', '=SUMIF(A1:A3,"="&A3,B1:B3)'], - ['3', '1', '=SUMIF(A1:A4,"="&A4,B1:B4)'], - ['1', '1', '=SUMIF(A1:A5,"="&A5,B1:B5)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(1) - expect(engine.getCellValue(adr('C2'))).toEqual(1) - expect(engine.getCellValue(adr('C3'))).toEqual(2) - expect(engine.getCellValue(adr('C4'))).toEqual(1) - expect(engine.getCellValue(adr('C5'))).toEqual(3) - }) - - it('works for subranges with inequality', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1', '=SUMIF(A1:A1,">2",B1:B1)'], - ['2', '1', '=SUMIF(A1:A2,">2",B1:B2)'], - ['3', '1', '=SUMIF(A1:A3,">2",B1:B3)'], - ['4', '1', '=SUMIF(A1:A4,">2",B1:B4)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(0) - expect(engine.getCellValue(adr('C2'))).toEqual(0) - expect(engine.getCellValue(adr('C3'))).toEqual(1) - expect(engine.getCellValue(adr('C4'))).toEqual(2) - }) - - it('works for subranges with more interesting criterions', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1', '=SUMIF(A1:A1,"=1",B1:B1)'], - ['2', '1', '=SUMIF(A1:A2,"<=2",B1:B2)'], - ['1', '1', '=SUMIF(A1:A3,"<2",B1:B3)'], - ['1', '1', '=SUMIF(A1:A4,">4",B1:B4)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(1) - expect(engine.getCellValue(adr('C2'))).toEqual(2) - expect(engine.getCellValue(adr('C3'))).toEqual(2) - expect(engine.getCellValue(adr('C4'))).toEqual(0) - }) - - it('discontinuous sumif range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1', '=SUMIF(A1:A1,"="&A1,B1:B1)'], - ['2', '1', '=SUMIF(A1:A2,"="&A2,B1:B2)'], - ['1', '1', '=SUMIF(A1:A3,"="&A3,B1:B3)'], - ['0', '0', '0'], - ['1', '1', '=SUMIF(A1:A5,"="&A5,B1:B5)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(1) - expect(engine.getCellValue(adr('C2'))).toEqual(1) - expect(engine.getCellValue(adr('C3'))).toEqual(2) - expect(engine.getCellValue(adr('C5'))).toEqual(3) - }) - - it('using full cache', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '3'], - ['1', '5'], - ['2', '7'], - ['=SUMIF(A1:A3, "=1", B1:B3)'], - ['=SUMIF(A1:A3, "=1", B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(5) - expect(engine.getCellValue(adr('A5'))).toEqual(5) - expect(engine.getStats().get(StatType.CRITERION_FUNCTION_FULL_CACHE_USED)).toEqual(1) - }) - - it('works with different sheets', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [ - ['0', '3'], - ['1', '5'], - ['2', '7'], - ['=SUMIF(A1:A3, "=1", B1:B3)'], - ['=SUMIF(Sheet2!A1:A3, "=1", B1:B3)'], - ], - Sheet2: [ - ['0', '30'], - ['0', '50'], - ['1', '70'], - ['=SUMIF(A1:A3, "=1", B1:B3)'], - ], - }) - - expect(engine.getCellValue(adr('A4', 0))).toEqual(5) - expect(engine.getCellValue(adr('A5', 0))).toEqual(7) - expect(engine.getCellValue(adr('A4', 1))).toEqual(70) - expect(engine.getStats().get(StatType.CRITERION_FUNCTION_FULL_CACHE_USED)).toEqual(0) - }) - - it('works when precision sensitive (default setting)', () => { - const engine = HyperFormula.buildFromArray([ - ['1.0000000001', '1'], - ['1.00000000000005', '2'], - ['1.00000000000005', '4'], - ['=SUMIF(A1:A3, "=1", B1:B3)'] - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(6) - }) - - it('criterions are not accent sensitive', () => { - const engine = HyperFormula.buildFromArray([ - ['abcd', '1'], - ['ABCD', '2'], - ['abc', '4'], - ['=SUMIF(A1:A3, "=ąbcd", B1:B3)'] - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(3) - }) - - it('criterions are accent sensitive if specified', () => { - const engine = HyperFormula.buildFromArray([ - ['abcd', '1'], - ['ABCD', '2'], - ['abc', '4'], - ['=SUMIF(A1:A3, "=ąbcd", B1:B3)'] - ], {accentSensitive: true}) - - expect(engine.getCellValue(adr('A4'))).toEqual(0) - }) - - it('criterions are not case sensitive', () => { - const engine = HyperFormula.buildFromArray([ - ['abcd', '1'], - ['ABCD', '2'], - ['abc', '4'], - ['=SUMIF(A1:A3, "<>abcd", B1:B3)'] - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(4) - }) - - it('criterions are not case sensitive 2', () => { - const engine = HyperFormula.buildFromArray([ - ['abcd', '1'], - ['ABCD', '2'], - ['abc', '4'], - ['=SUMIF(A1:A3, "=abcd", B1:B3)'] - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(3) - }) - - it('criterions are case sensitive if specified', () => { - const engine = HyperFormula.buildFromArray([ - ['abcd', '1'], - ['ABCD', '2'], - ['abc', '4'], - ['=SUMIF(A1:A3, "<>abcd", B1:B3)'] - ], {caseSensitive: true}) - - expect(engine.getCellValue(adr('A4'))).toEqual(6) - }) - - it('usage of wildcards', () => { - const engine = HyperFormula.buildFromArray([ - ['abcd', '1'], - ['ABCD', '2'], - ['abc', '4'], - [0, 8], - ['=SUMIF(A1:A4, "=a?c*", B1:B4)'] - ]) - - expect(engine.getCellValue(adr('A5'))).toEqual(7) - }) - - it('wildcards instead of regexps', () => { - const engine = HyperFormula.buildFromArray([ - ['a+?*', '1'], - ['a?*', '2'], - ['aa~?~*', '4'], - [0, 8], - ['=SUMIF(A1:A4, "=a+~?~*", B1:B4)'] - ]) - - expect(engine.getCellValue(adr('A5'))).toEqual(1) - }) - - it('regexp', () => { - const engine = HyperFormula.buildFromArray([ - ['abcd', '1'], - ['abd', '2'], - ['.*c.*', '4'], - [0, 8], - ['=SUMIF(A1:A4, ".*c.*", B1:B4)'] - ], {useRegularExpressions: true}) - - expect(engine.getCellValue(adr('A5'))).toEqual(5) - }) - - it('regexp with not-equal operator', () => { - const engine = HyperFormula.buildFromArray([ - ['abcd', '1'], - ['abd', '2'], - ['.*c.*', '4'], - [0, 8], - ['=SUMIF(A1:A4, "<>.*c.*", B1:B4)'] - ], {useRegularExpressions: true}) - - expect(engine.getCellValue(adr('A5'))).toEqual(10) - }) - - it('incorrect regexp', () => { - const engine = HyperFormula.buildFromArray([ - ['abcd', '1'], - ['abd', '2'], - ['.*c.*', '4'], - [0, 8], - ['=SUMIF(A1:A4, "=)", B1:B4)'] - ], {useRegularExpressions: true}) - - expect(engine.getCellValue(adr('A5'))).toEqual(0) - }) - - it('ignore errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '3'], - ['=4/0', '5'], - ['2', '=4/0'], - ['1', '10'], - ['=SUMIF(A1:A4, "=1", B1:B4)'], - ]) - - expect(engine.getCellValue(adr('A5'))).toEqual(13) - }) - - it('coerces dates as numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '9160250011660588', '43469', '25000'], - ['2', '9160250011689568', '43631', '15000'], - ['=SUMIF(C2:C11,">31/05/2019",D2:D11)'] - ], {dateFormats: ['DD/MM/YYYY']}) - expect(engine.getCellValue(adr('A3'))).toEqual(15000) - }) - - it('works when criterion arg is a date', () => { - const engine = HyperFormula.buildFromArray([ - ['31/05/2019', '1'], - ['=DATEVALUE("31/05/2019")', '1'], - ['=DATE(2019, 5, 31)', '1'], - ['=A1', '1'], - ['43616', '1'], - ['=SUMIF(A1:A5, "31/05/2019", B1:B5)'] - ], {dateFormats: ['DD/MM/YYYY']}) - - expect(engine.getCellValue(adr('A6'))).toEqual(5) - }) - - it('works when criterion arg is a time value', () => { - const engine = HyperFormula.buildFromArray([ - ['12:13', '1'], - ['=SUMIF(A1:A1, "12:13", B1:B1)'] - ]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - }) - - it('works when criterion arg is a datetime value', () => { - const engine = HyperFormula.buildFromArray([ - ['31/05/2019 9:15', '1'], - ['=SUMIF(A1:A1, "31/05/2019 9:15", B1:B1)'] - ]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - }) - - it('works when criterion arg is a currency value', () => { - const engine = HyperFormula.buildFromArray([ - ['$1', '1'], - ['1$', '1'], - ['=SUMIF(A1:A2, "$1", B1:B2)'], - ['=SUMIF(A1:A2, "1$", B1:B2)'] - ]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValue(adr('A3'))).toEqual(2) - expect(engine.getCellValue(adr('A4'))).toEqual(2) - }) - - it('works when criterion arg is a percent value', () => { - const engine = HyperFormula.buildFromArray([ - ['20%', '1'], - ['=SUMIF(A1:A1, "20%", B1:B1)'] - ]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - }) - - it('ignores whitespace characters in criterion argument', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['=SUMIF(A1:A1, "$1")'], - ['=SUMIF(A1:A1, " $1")'], - ['=SUMIF(A1:A1, "$ 1")'], - ['=SUMIF(A1:A1, "$1 ")'], - ['=SUMIF(A1:A1, "100%")'], - ['=SUMIF(A1:A1, " 100%")'], - ['=SUMIF(A1:A1, "100 %")'], - ['=SUMIF(A1:A1, "100% ")'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('A4'))).toEqual(1) - expect(engine.getCellValue(adr('A5'))).toEqual(1) - expect(engine.getCellValue(adr('A6'))).toEqual(1) - expect(engine.getCellValue(adr('A7'))).toEqual(1) - expect(engine.getCellValue(adr('A8'))).toEqual(1) - expect(engine.getCellValue(adr('A9'))).toEqual(1) - }) -}) - -describe('Function SUMIFS - argument validations and combinations', () => { - it('requires odd number of arguments, but at least 3', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMIFS(C1, ">0")'], - ['=SUMIFS(C1, ">0", B1, B1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('error when criterion unparsable', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMIFS(B1:B2, C1:C2, "> { - const engine = HyperFormula.buildFromArray([ - ['=SUMIFS(B1:C1, B2:D2, ">0")'], - ['=SUMIFS(B1, B2:D2, ">0")'], - ['=SUMIFS(B1:D1, B2, ">0")'], - ['=SUMIFS(B1:D1, B2:D2, ">0", B2:E2, ">0")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - }) - - it('error when different height dimension of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMIFS(B1:B2, C1:C3, ">0")'], - ['=SUMIFS(B1, C1:C2, ">0")'], - ['=SUMIFS(B1:B2, C1, ">0")'], - ['=SUMIFS(B1:B2, C1:C2, ">0", C1:C3, ">0")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - }) - - it('scalars are treated like singular arrays', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMIFS(42, 10, ">1")'], - ['=SUMIFS(42, 0, ">1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(42) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) - - it('error propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMIFS(4/0, 42, ">1")'], - ['=SUMIFS(0, 4/0, ">1")'], - ['=SUMIFS(0, 42, 4/0)'], - ['=SUMIFS(0, 4/0, FOOBAR())'], - ['=SUMIFS(4/0, FOOBAR(), ">1")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('works when arguments are just references', () => { - const engine = HyperFormula.buildFromArray([ - ['2', '3'], - ['=SUMIFS(B1, A1, ">1")'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(3) - }) - - it('works with range values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1', '3', '5'], - ['1', '1', '7', '9'], - ['=SUMIFS(MMULT(C1:D2, C1:D2), MMULT(A1:B2, A1:B2), "=2")'], - ['=SUMIFS(MMULT(C1:D2, C1:D2), A1:B2, "=1")'], - ['=SUMIFS(C1:D2, MMULT(A1:B2, A1:B2), "=2")'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(304) - expect(engine.getCellValue(adr('A4'))).toEqual(304) - expect(engine.getCellValue(adr('A5'))).toEqual(24) - }) - - it('works for mixed reference/range arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['2', '3'], - ['=SUMIFS(B1, A1:A1, ">1")'], - ['4', '5'], - ['=SUMIFS(B3:B3, A3, ">1")'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(3) - expect(engine.getCellValue(adr('A4'))).toEqual(5) - }) - - it('works when criterion arg is an inline array', () => { - const engine = HyperFormula.buildFromArray([ - [2, 'a'], - [3, 'b'], - ['=SUMIFS(A1:A2, B1:B2, {"a", "b"})'] - ], { useArrayArithmetic: true }) - - expect(engine.getCellValue(adr('A3'))).toEqual(2) - expect(engine.getCellValue(adr('B3'))).toEqual(3) - }) -}) - -describe('Function SUMIFS - calcultions on more than one criteria', () => { - it('works for more than one criterion/range pair', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '100', '3'], - ['1', '101', '5'], - ['2', '102', '7'], - ['=SUMIFS(C1:C3, A1:A3, ">=1", B1:B3, "<102")'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(5) - }) -}) - -describe('Function SUMIF - cache recalculation after cruds', () => { - it('recalculates SUMIF if changes in summed range', () => { - const sheet = [ - ['10', '10'], - ['5', '6'], - ['=SUMIF(A2:B2, ">=1", A1:B1)', '0'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - const changes = engine.setCellContents(adr('A1'), [['1', '3']]) - - expect(engine.getCellValue(adr('A3'))).toEqual(4) - expect(changes.length).toEqual(3) - expectArrayWithSameContent(changes.map((change) => change.newValue), [1, 3, 4]) - }) - - it('recalculates SUMIF if changes in tested range', () => { - const sheet = [ - ['10', '10'], - ['5', '6'], - ['0', '=SUMIF(A1:B1, ">=2", A2:B2)'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - const changes = engine.setCellContents(adr('A1'), [['1', '3']]) - - expect(engine.getCellValue(adr('B3'))).toEqual(6) - expect(changes.length).toEqual(3) - expectArrayWithSameContent(changes.map((change) => change.newValue), [1, 3, 6]) - }) - - it('recalculates SUMIF if summed range same as tested range', () => { - const sheet = [ - ['10', '10'], - ['0', '=SUMIF(A1:B1, ">=2", A1:B1)'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - expect(engine.getCellValue(adr('B2'))).toEqual(20) - - const changes = engine.setCellContents(adr('A1'), [['1', '3']]) - - expect(engine.getCellValue(adr('B2'))).toEqual(3) - expect(changes.length).toEqual(3) - expectArrayWithSameContent(changes.map((change) => change.newValue), [1, 3, 3]) - }) -}) - -describe('Function SUMIFS - cache recalculation after cruds', () => { - it('recalculates SUMIFS if changes in summed range', () => { - const sheet = [ - ['10', '10'], - ['5', '6'], - ['7', '8'], - ['=SUMIFS(A1:B1, A2:B2, ">=5", A3:B3, ">=7")'], - ] - const engine = HyperFormula.buildFromArray(sheet) - - const changes = engine.setCellContents(adr('A1'), [['1', '3']]) - - expect(engine.getCellValue(adr('A4'))).toEqual(4) - expect(changes.length).toEqual(3) - expectArrayWithSameContent(changes.map((change) => change.newValue), [1, 3, 4]) - }) - - it('recalculates SUMIFS if changes in one of the tested range', () => { - const sheet = [ - ['10', '10'], - ['5', '6'], - ['7', '8'], - ['=SUMIFS(A1:B1, A2:B2, ">=5", A3:B3, ">=7")'], - ] - const engine = HyperFormula.buildFromArray(sheet) - expect(engine.getCellValue(adr('A4'))).toEqual(20) - - const changes = engine.setCellContents(adr('A3'), [['1', '7']]) - - expect(engine.getCellValue(adr('A4'))).toEqual(10) - expect(changes.length).toEqual(3) - expectArrayWithSameContent(changes.map((change) => change.newValue), [1, 7, 10]) - }) -}) diff --git a/test/unit/interpreter/function-sumproduct.spec.ts b/test/unit/interpreter/function-sumproduct.spec.ts deleted file mode 100644 index 08dfe4d43f..0000000000 --- a/test/unit/interpreter/function-sumproduct.spec.ts +++ /dev/null @@ -1,206 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SUMPRODUCT', () => { - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMPRODUCT()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1'], - ['2', '2'], - ['3', '3'], - ['=SUMPRODUCT(A1:A3,B1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(14) - }) - - it('works for more args', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1'], - ['2', '2'], - ['3', '3'], - ['=SUMPRODUCT(A1:A3, B1:B3, A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(36) - }) - - it('works for less args', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1'], - ['2', '2'], - ['3', '3'], - ['=SUMPRODUCT(A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(6) - }) - - it('works with wider ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '3', '1', '3'], - ['2', '4', '2', '4'], - ['=SUMPRODUCT(A1:B2,C1:D2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(30) - }) - - it('works with cached smaller range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1', '=SUMPRODUCT(A1:A1, B1:B1)'], - ['2', '2', '=SUMPRODUCT(A1:A2, B1:B2)'], - ['3', '3', '=SUMPRODUCT(A1:A3, B1:B3)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(1) - expect(engine.getCellValue(adr('C2'))).toEqual(5) - expect(engine.getCellValue(adr('C3'))).toEqual(14) - }) - - it('sumproduct from scalars', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMPRODUCT(42, 78)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(3276) - }) - - it('use cached value if the same formula used', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1'], - ['2', '2'], - ['=SUMPRODUCT(A1:A2,B1:B2)'], - ['=SUMPRODUCT(A1:A2,B1:B2)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqual(5) - }) - - it('makes a coercion from other values', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRUE()', '42'], - ['=SUMPRODUCT(A1,B1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(42) - }) - - it('if coercion unsuccessful, it ignores it', () => { - const engine = HyperFormula.buildFromArray([ - ['"foobar"', '42'], - ['=SUMPRODUCT(A1,B1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) - - it('works even if some string in data', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1'], - ['asdf', 'fdsafdsa'], - ['=SUMPRODUCT(A1:A2,B1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(1) - }) - - it('works even if both strings passed', () => { - const engine = HyperFormula.buildFromArray([ - ['asdf', 'fdsafdsa'], - ['=SUMPRODUCT(A1,B1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) - - it('works even if both booleans passed', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRUE()', '=FALSE()'], - ['=SUMPRODUCT(A1,B1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) - - it('error if error is somewhere in right value', () => { - const engine = HyperFormula.buildFromArray([ - ['42', '78'], - ['13', '=4/0'], - ['=SUMPRODUCT(A1:A2,B1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('error if error is somewhere in left value', () => { - const engine = HyperFormula.buildFromArray([ - ['42', '78'], - ['=3/0', '13'], - ['=SUMPRODUCT(A1:A2,B1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('error in left has precedence over error in right', () => { - const engine = HyperFormula.buildFromArray([ - ['42', '78'], - ['=UNKNOWNFUNCTION()', '=3/0'], - ['=SUMPRODUCT(A1:A2,B1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('UNKNOWNFUNCTION'))) - }) - - it('error when different size', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '3', '1', '3'], - ['2', '4', '2', '4'], - ['=SUMPRODUCT(A1:B2,C1:C2)'], - ['=SUMPRODUCT(A1:B2,C1:D1)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - }) - - it('works with matrices', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3'], - ['=SUMPRODUCT(A1:B1, TRANSPOSE(A1:A2))'], - ]) - expect(engine.getCellValue(adr('A3'))).toEqual(7) - }) - - it('error if mismatched range shape', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ['2'], - ['3'], - ['=SUMPRODUCT(A1:C1,A1:A3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EqualLength)) - }) - - // Inconsistency with Product 1 - it('order of errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '', '=FOOBAR()', '12'], - ['3', '=4/0', '', '13', '14'], - ['=SUMPRODUCT(A1:B2, D1:E2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - }) -}) diff --git a/test/unit/interpreter/function-sumsq.spec.ts b/test/unit/interpreter/function-sumsq.spec.ts deleted file mode 100644 index a78a996019..0000000000 --- a/test/unit/interpreter/function-sumsq.spec.ts +++ /dev/null @@ -1,85 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('SUMSQ', () => { - it('SUMSQ without args', () => { - const engine = HyperFormula.buildFromArray([['=SUMSQ()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('SUMSQ with args', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMSQ(1, B1)', '2'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(5) - }) - - it('SUMSQ with range args', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '5'], - ['3', '4', '=SUMSQ(A1:B2)'], - ]) - expect(engine.getCellValue(adr('C2'))).toEqual(30) - }) - - it('SUMSQ with using previously cached value', () => { - const engine = HyperFormula.buildFromArray([ - ['3', '=SUMSQ(A1:A1)'], - ['4', '=SUMSQ(A1:A2)'], - ]) - expect(engine.getCellValue(adr('B2'))).toEqual(25) - }) - - it('doesnt do coercions', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['foo'], - ['=TRUE()'], - ['=CONCATENATE("1","0")'], - ['=SUMSQ(A1:A5)'], - ]) - - expect(engine.getCellValue(adr('A6'))).toEqual(5) - }) - - it('range only with empty value', () => { - const engine = HyperFormula.buildFromArray([ - ['', '=SUMSQ(A1:A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(0) - }) - - it('range only with some empty values', () => { - const engine = HyperFormula.buildFromArray([ - ['42', '', '13', '=SUMSQ(A1:C1)'], - ]) - - expect(engine.getCellValue(adr('D1'))).toEqual(1933) - }) - - it('over a range value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=SUMSQ(MMULT(A1:B2, A1:B2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(858) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=4/0'], - ['=FOOBAR()', '4'], - ['=SUMSQ(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-sumx2my2.spec.ts b/test/unit/interpreter/function-sumx2my2.spec.ts deleted file mode 100644 index 980cd08bbc..0000000000 --- a/test/unit/interpreter/function-sumx2my2.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SUMX2MY2', () => { - it('should validate number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMX2MY2(1)'], - ['=SUMX2MY2(1,2,3)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return correct output', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMX2MY2(A2:D2, A3:D3)'], - [1, 2, 3, 4], - [5, 4, 2, 1], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(-16) - }) - - it('should validate that ranges are of equal length', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMX2MY2(A2:F2, A3:E3)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.EqualLength)) - }) - - it('should propagate errors (from array 1)', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMX2MY2(A2:E2, A3:E3)'], - [1, 2, 3, '=NA()', 5, 6], - [5, 4, 2, 1, 5, 10], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('should propagate errors (from array 2)', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMX2MY2(A2:E2, A3:E3)'], - [5, 4, 2, 1, 5, 10], - [1, 2, 3, '=NA()', 5, 6], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('should ignore non-number inputs', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMX2MY2(A2:D2, A3:D3)'], - [null, 2, '\'1', 4], - [5, '\'abcd', 2, true], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) -}) diff --git a/test/unit/interpreter/function-sumx2py2.spec.ts b/test/unit/interpreter/function-sumx2py2.spec.ts deleted file mode 100644 index 703d6c6e9f..0000000000 --- a/test/unit/interpreter/function-sumx2py2.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SUMX2PY2', () => { - it('should validate number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMX2PY2(1)'], - ['=SUMX2PY2(1,2,3)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return correct output', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMX2PY2(A2:D2, A3:D3)'], - [1, 2, 3, 4], - [5, 4, 2, 1], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(76) - }) - - it('should validate that ranges are of equal length', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMX2PY2(A2:F2, A3:E3)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.EqualLength)) - }) - - it('should propagate errors (from array 1)', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMX2PY2(A2:E2, A3:E3)'], - [1, 2, 3, '=NA()', 5, 6], - [5, 4, 2, 1, 5, 10], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('should propagate errors (from array 2)', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMX2PY2(A2:E2, A3:E3)'], - [5, 4, 2, 1, 5, 10], - [1, 2, 3, '=NA()', 5, 6], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('should ignore non-number inputs', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMX2PY2(A2:D2, A3:D3)'], - [null, 2, '\'1', 4], - [5, '\'abcd', 2, true], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) -}) diff --git a/test/unit/interpreter/function-sumxmy2.spec.ts b/test/unit/interpreter/function-sumxmy2.spec.ts deleted file mode 100644 index 564fd0c090..0000000000 --- a/test/unit/interpreter/function-sumxmy2.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SUMXMY2', () => { - it('should validate number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMXMY2(1)'], - ['=SUMXMY2(1,2,3)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return correct output', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMXMY2(A2:D2, A3:D3)'], - [1, 2, 3, 4], - [5, 4, 2, 1], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(30) - }) - - it('should validate that ranges are of equal length', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMXMY2(A2:F2, A3:E3)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.EqualLength)) - }) - - it('should propagate errors (from array 1)', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMXMY2(A2:E2, A3:E3)'], - [1, 2, 3, '=NA()', 5, 6], - [5, 4, 2, 1, 5, 10], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('should propagate errors (from array 2)', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMXMY2(A2:E2, A3:E3)'], - [5, 4, 2, 1, 5, 10], - [1, 2, 3, '=NA()', 5, 6], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('should ignore non-number inputs', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUMXMY2(A2:D2, A3:D3)'], - [null, 2, '\'1', 4], - [5, '\'abcd', 2, true], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) -}) diff --git a/test/unit/interpreter/function-switch.spec.ts b/test/unit/interpreter/function-switch.spec.ts deleted file mode 100644 index ce2ae50f5c..0000000000 --- a/test/unit/interpreter/function-switch.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueDetailedType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Interpreter - SWITCH function', () => { - it('Should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SWITCH(1)', '=SWITCH(2,3)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('Should work with more arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SWITCH(1,2,3)', '=SWITCH(1,2,3,4)', '=SWITCH(1,2,3,4,5)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.NoDefault)) - expect(engine.getCellValue(adr('B1'))).toEqual(4) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.NoDefault)) - }) - - it('Should work with precision', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1.0000000001', '3', '1.0000000000001', '5'], - ['=SWITCH(A1,B1,C1,D1,E1)'] - ]) - expect(engine.getCellValue(adr('A2'))).toEqual(5) - }) - - it('Should work with strings', () => { - const engine = HyperFormula.buildFromArray([ - ['abc', '1', '3', 'ABC', '5'], - ['=SWITCH(A1,B1,C1,D1,E1)'] - ], {caseSensitive: false}) - expect(engine.getCellValue(adr('A2'))).toEqual(5) - }) - it('Should fail with error in first argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=SWITCH(1/0,1/0,3,4,5)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - it('Should not fail with error in other arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SWITCH(4,1/0,3,4,5)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(5) - }) - it('Should pass errors as normal values', () => { - const engine = HyperFormula.buildFromArray([ - ['=SWITCH(4,2,3,4,1/0)', '=SWITCH(1,2,3,4,1/0,5)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('B1'))).toEqual(5) - }) - it('Should fail with range', () => { - const engine = HyperFormula.buildFromArray([ - ['=SWITCH(1,2,A2:A3,4,5)'] - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - it('passes subtypes', () => { - const engine = HyperFormula.buildFromArray([['=SWITCH(1,1,B1)', '1%']]) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - }) -}) diff --git a/test/unit/interpreter/function-syd.spec.ts b/test/unit/interpreter/function-syd.spec.ts deleted file mode 100644 index d2ad44eeb3..0000000000 --- a/test/unit/interpreter/function-syd.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function SYD', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=SYD(1,1,1)', '=SYD(1, 1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=SYD(100, 1, 2.1, 2)', '=SYD(100, 1, 2.1, 2.1)', '=SYD(100, 1, 2, 2.1)', '=SYD(100, 1, 2, 2)', ], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(33.4562211981567) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(30.4147465437788) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NUM)) - expect(engine.getCellValue(adr('D1'))).toBeCloseTo(33) - }) -}) diff --git a/test/unit/interpreter/function-t.dist.2t.spec.ts b/test/unit/interpreter/function-t.dist.2t.spec.ts deleted file mode 100644 index 8aa3818214..0000000000 --- a/test/unit/interpreter/function-t.dist.2t.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function T.DIST.2T', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST.2T(1)'], - ['=T.DIST.2T(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST.2T("foo", 2)'], - ['=T.DIST.2T(1, "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST.2T(1, 1)'], - ['=T.DIST.2T(3, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.5, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0954659662667092, 6) - }) - - it('should truncate input', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST.2T(1, 1.9)'], - ['=T.DIST.2T(3, 2.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.5, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0954659662667092, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST.2T(0, 1)'], - ['=T.DIST.2T(-0.01, 1)'], - ['=T.DIST.2T(1, 0.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1, 6) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-t.dist.rt.spec.ts b/test/unit/interpreter/function-t.dist.rt.spec.ts deleted file mode 100644 index 476d91b46d..0000000000 --- a/test/unit/interpreter/function-t.dist.rt.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function T.DIST.RT', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST.RT(1)'], - ['=T.DIST.RT(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST.RT("foo", 2)'], - ['=T.DIST.RT(1, "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST.RT(1, 1)'], - ['=T.DIST.RT(3, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.25, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0477329831333546, 6) - }) - - it('should truncate input', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST.RT(1, 1.9)'], - ['=T.DIST.RT(3, 2.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.25, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0477329831333546, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST.RT(0, 1)'], - ['=T.DIST.RT(-0.01, 1)'], - ['=T.DIST.RT(1, 0.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.5, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.50318300828035, 6) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-t.dist.spec.ts b/test/unit/interpreter/function-t.dist.spec.ts deleted file mode 100644 index 347023a421..0000000000 --- a/test/unit/interpreter/function-t.dist.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function T.DIST', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST(1, 2)'], - ['=T.DIST(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST("foo", 2, TRUE())'], - ['=T.DIST(1, "baz", TRUE())'], - ['=T.DIST(1, 2, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST(1, 1, TRUE())'], - ['=T.DIST(3, 2, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.75, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.952267016866645, 6) - }) - - it('should work as pdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST(1, 1, FALSE())'], - ['=T.DIST(3, 2, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.159154942198517, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0274101222343421, 6) - }) - - it('should truncate input', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST(1, 1.9, TRUE())'], - ['=T.DIST(3, 2.9, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.75, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.952267016866645, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.DIST(-1, 1, FALSE())'], - ['=T.DIST(1, 0.9, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.159154942198517, 11) - //product #2 returns different error - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-t.inv.2t.spec.ts b/test/unit/interpreter/function-t.inv.2t.spec.ts deleted file mode 100644 index 224bb28a5b..0000000000 --- a/test/unit/interpreter/function-t.inv.2t.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function T.INV.2T', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.INV.2T(1)'], - ['=T.INV.2T(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.INV.2T("foo", 2)'], - ['=T.INV.2T(0.5, "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.INV.2T(0.1, 1)'], - ['=T.INV.2T(0.9, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(6.31375151467394, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.142133810903748, 6) - }) - - it('should truncate input', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.INV.2T(0.1, 1.9)'], - ['=T.INV.2T(0.9, 2.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(6.31375151467394, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.142133810903748, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.INV.2T(0.01, 1)'], - ['=T.INV.2T(0, 1)'], - ['=T.INV.2T(1, 1)'], - ['=T.INV.2T(1.01, 1)'], - ['=T.INV.2T(0.5, 0.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(63.656741162866, 6) - //product #2 returns different error - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqual(0) - //product #2 returns value - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-t.inv.spec.ts b/test/unit/interpreter/function-t.inv.spec.ts deleted file mode 100644 index 4352299e20..0000000000 --- a/test/unit/interpreter/function-t.inv.spec.ts +++ /dev/null @@ -1,63 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function T.INV', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.INV(1)'], - ['=T.INV(1, 2, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.INV("foo", 2)'], - ['=T.INV(0.5, "baz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.INV(0.1, 1)'], - ['=T.INV(0.9, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-3.07768353592299, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(1.88561804936468, 6) - }) - - it('should truncate input', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.INV(0.1, 1.9)'], - ['=T.INV(0.9, 2.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-3.07768353592299, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(1.88561804936468, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.INV(0.01, 1)'], - ['=T.INV(0, 1)'], - ['=T.INV(0.99, 1)'], - ['=T.INV(1, 1)'], - ['=T.INV(0.5, 0.9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(-31.820515953771, 6) - //product #2 returns different error - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(31.820515953771, 6) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueLarge)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-t.spec.ts b/test/unit/interpreter/function-t.spec.ts deleted file mode 100644 index 5e42b79dfa..0000000000 --- a/test/unit/interpreter/function-t.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function T', () => { - it('should take one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=T()'], - ['=T("foo", "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return given text', () => { - const engine = HyperFormula.buildFromArray([ - ['=T("foo")'], - ['=T(B2)', 'bar'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('foo') - expect(engine.getCellValue(adr('A2'))).toEqual('bar') - }) - - it('should return empty string if given value is not a text', () => { - const engine = HyperFormula.buildFromArray([ - ['=T(B1)', '=TRUE()'], - ['=T(B2)', 42], - ['=T(B3)', null], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('') - expect(engine.getCellValue(adr('A2'))).toEqual('') - expect(engine.getCellValue(adr('A3'))).toEqual('') - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=T(B1)', '=1/0'], - ['=T(B2)', '=FOO()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOO'))) - }) -}) diff --git a/test/unit/interpreter/function-t.test.spec.ts b/test/unit/interpreter/function-t.test.spec.ts deleted file mode 100644 index 6f0e60dbd3..0000000000 --- a/test/unit/interpreter/function-t.test.spec.ts +++ /dev/null @@ -1,154 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('T.TEST', () => { - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.TEST(1, 2, 3)'], - ['=T.TEST(1, 2, 3, 4, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works for mode 1', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10], - [2, 5], - ['=T.TEST(A1:A2, B1:B2, 1, 1)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.1475836177, 6) - }) - - it('works for mode 2', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10], - [2, 5], - ['=T.TEST(A1:A2, B1:B2, 1, 2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.07142857143, 6) - }) - - it('works for mode 3', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10], - [2, 5], - ['=T.TEST(A1:A2, B1:B2, 1, 3)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.1203798536, 6) - }) - - it('works for larger ranges for mode 1', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10, 1, 1, 3, 7], - [2, 5, 1, 1, 4, 8], - ['=T.TEST(A1:F1, A2:F2, 2, 1)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.741153821738662, 9) - }) - - it('works for larger ranges for mode 2', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10, 1, 1, 3, 7], - [2, 5, 1, 1, 4, 8], - ['=T.TEST(A1:F1, A2:F2, 2, 2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.8654794555, 9) - }) - - it('works for larger ranges for mode 3', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10, 1, 1, 3, 7], - [2, 5, 1, 1, 4, 8], - ['=T.TEST(A1:F1, A2:F2, 2, 3)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.8658288672, 9) - }) - - it('validates range length for mode 1', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10, 1, 1, 3], - [2, 5, 1, 1, 4, 8], - ['=T.TEST(A1:E1, A2:F2, 1, 1)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.EqualLength)) - }) - - it('works for distinct length ranges for mode 2', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10, 1, 1, 3], - [2, 5, 1, 1, 4, 8], - ['=T.TEST(A1:E1, A2:F2, 1, 2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.442070764, 6) - }) - - it('works for distinct length ranges for mode 3', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10, 1, 1, 3], - [2, 5, 1, 1, 4, 8], - ['=T.TEST(A1:E1, A2:F2, 1, 3)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.4444544032, 6) - }) - - it('doesnt do coercions, nonnumeric values are skipped for mode 1', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10, 1, 1, 3], - [2, 5, null, 1, 4, 8], - ['=T.TEST(A1:E1, A2:F2, 1, 1)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.3298741207, 9) - }) - - it('doesnt do coercions, nonnumeric values are skipped for mode 2', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10, 1, 1, 3, 7], - [2, 5, null, 1, 4, 8], - ['=T.TEST(A1:F1, A2:F2, 1, 2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.4684423056, 9) - }) - - it('doesnt do coercions, nonnumeric values are skipped for mode 3', () => { - const engine = HyperFormula.buildFromArray([ - [1, 10, 1, 1, 3, 7], - [2, 5, null, 1, 4, 8], - ['=T.TEST(A1:F1, A2:F2, 1, 3)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.4674248166, 9) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['=NA()', '50'], - ['3', '30'], - ['=T.TEST(A1:A3, B1:B3, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NA)) - }) - - it('error when not enough data', () => { - const engine = HyperFormula.buildFromArray([ - ['=T.TEST(1, 2, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.TwoValues)) - }) -}) diff --git a/test/unit/interpreter/function-tan.spec.ts b/test/unit/interpreter/function-tan.spec.ts deleted file mode 100644 index 2ac77bed86..0000000000 --- a/test/unit/interpreter/function-tan.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function TAN', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=TAN(0)', '=TAN(0.5)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.546302489843791) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=TAN("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=TAN()', '=TAN(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=TAN(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-1.5574077246549) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=TAN(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-tanh.spec.ts b/test/unit/interpreter/function-tanh.spec.ts deleted file mode 100644 index 59396f8579..0000000000 --- a/test/unit/interpreter/function-tanh.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function TANH', () => { - it('happy path', () => { - const engine = HyperFormula.buildFromArray([['=TANH(0)', '=TANH(0.5)']]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(0.46211715726001) - }) - - it('when value not numeric', () => { - const engine = HyperFormula.buildFromArray([['=TANH("foo")']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([['=TANH()', '=TANH(1,-1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('use number coercion', () => { - const engine = HyperFormula.buildFromArray([ - ['="-1"', '=TANH(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(-0.761594155955765) - }) - - it('errors propagation', () => { - const engine = HyperFormula.buildFromArray([ - ['=TANH(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-tbilleq.spec.ts b/test/unit/interpreter/function-tbilleq.spec.ts deleted file mode 100644 index b613d6032b..0000000000 --- a/test/unit/interpreter/function-tbilleq.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function TBILLEQ', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=TBILLEQ(1,1)', '=TBILLEQ(1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=TBILLEQ(0, 100, 0.1)'], - ['=TBILLEQ(0, 360, 0.1)', '=TBILLEQ(0, 183, 0.1)', ], - ['=TBILLEQ(0, 180, 1.9)', '=TBILLEQ(0, 180, 2)', '=TBILLEQ(0, 180, 2.1)', ], - ['=TBILLEQ("1/2/2000", "31/1/2001", 0.1)', '=TBILLEQ(0, 365, 0.1)', ], - ['=TBILLEQ("28/2/2003", "29/2/2004", 0.1)'], - ['=TBILLEQ(2, 2.1, 0.1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.104285714285714, 6) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - //inconsistency with products #1 & #2 - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.112654320987654, 6) - //inconsistency with products #1 & #2 - expect(engine.getCellValue(adr('B2'))).toBeCloseTo(0.106818846941762, 6) - //inconsistency with product #1 (returns #NUM!) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(38.5277777777778, 6) - //inconsistency with product #1 (returns #NUM!) - expect(engine.getCellValue(adr('B3'))).toEqual(0) - expect(engine.getCellValue(adr('C3'))).toEqualError(detailedError(ErrorType.NUM)) - //inconsistency with products #1 & #2 - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(0.112828438948995, 6) - //inconsistency with products #1 & #2 - expect(engine.getCellValue(adr('B4'))).toBeCloseTo(0.112828438948995, 6) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NUM)) - }) -}) diff --git a/test/unit/interpreter/function-tbillprice.spec.ts b/test/unit/interpreter/function-tbillprice.spec.ts deleted file mode 100644 index 08b0d49109..0000000000 --- a/test/unit/interpreter/function-tbillprice.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function TBILLPRICE', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=TBILLPRICE(1,1)', '=TBILLPRICE(1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=TBILLPRICE(0, 100, 0.1)'], - ['=TBILLPRICE(0, 360, 0.1)', '=TBILLPRICE(0, 183, 0.1)', ], - ['=TBILLPRICE(0, 180, 1.9)', '=TBILLPRICE(0, 180, 2)', '=TBILLPRICE(0, 180, 2.1)', ], - ['=TBILLPRICE("1/2/2000", "31/1/2001", 0.1)', '=TBILLPRICE(0, 365, 0.1)', ], - ['=TBILLPRICE("28/2/2003", "29/2/2004", 0.1)'], - ['=TBILLPRICE(2, 2.1, 0.1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(97.2222222222222, 6) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValue(adr('A2'))).toEqual(90) - expect(engine.getCellValue(adr('B2'))).toBeCloseTo(94.9166666666667, 6) - //inconsistency with product #1 (returns #NUM!) - expect(engine.getCellValue(adr('A3'))).toEqual(5) - //inconsistency with product #1 (returns #NUM!) - expect(engine.getCellValue(adr('B3'))).toEqual(0) - expect(engine.getCellValue(adr('C3'))).toEqualError(detailedError(ErrorType.NUM)) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(89.8611111111111, 6) - expect(engine.getCellValue(adr('B4'))).toBeCloseTo(89.8611111111111, 6) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NUM)) - }) -}) diff --git a/test/unit/interpreter/function-tbillyield.spec.ts b/test/unit/interpreter/function-tbillyield.spec.ts deleted file mode 100644 index 2b3b781cd1..0000000000 --- a/test/unit/interpreter/function-tbillyield.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellValueDetailedType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function TBILLYIELD', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=TBILLYIELD(1,1)', '=TBILLYIELD(1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate the correct value with correct arguments and defaults', () => { - const engine = HyperFormula.buildFromArray([ - ['=TBILLYIELD(0, 100, 10)'], - ['=TBILLYIELD(0, 360, 10)', '=TBILLYIELD(0, 183, 10)', ], - ['=TBILLYIELD(0, 180, 10)', '=TBILLYIELD(0, 180, 100)', '=TBILLYIELD(0, 180, 110)', ], - ['=TBILLYIELD("1/2/2000", "31/1/2001", 10)', '=TBILLYIELD(0, 365, 10)', ], - ['=TBILLYIELD("28/2/2003", "29/2/2004", 10)'], - ['=TBILLYIELD(2, 2.1, 10)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(32.4, 6) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValue(adr('A2'))).toEqual(9) - expect(engine.getCellValue(adr('B2'))).toBeCloseTo(17.7049180327869, 6) - expect(engine.getCellValue(adr('A3'))).toEqual(18) - expect(engine.getCellValue(adr('B3'))).toEqual(0) - expect(engine.getCellValue(adr('C3'))).toBeCloseTo(-0.181818181818182, 6) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(8.87671232876712, 6) - expect(engine.getCellValue(adr('B4'))).toBeCloseTo(8.87671232876712, 6) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NUM)) - }) -}) diff --git a/test/unit/interpreter/function-tdist.spec.ts b/test/unit/interpreter/function-tdist.spec.ts deleted file mode 100644 index 7abe1193cd..0000000000 --- a/test/unit/interpreter/function-tdist.spec.ts +++ /dev/null @@ -1,79 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function TDIST', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=TDIST(1, 1)'], - ['=TDIST(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=TDIST("foo", 2, 3)'], - ['=TDIST(1, "baz", 3)'], - ['=TDIST(1, 2, "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work as T.DIST.RT', () => { - const engine = HyperFormula.buildFromArray([ - ['=TDIST(1, 1, 1)'], - ['=TDIST(3, 2, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.25, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0477329831333546, 6) - }) - - it('should work as T.DIST.2T', () => { - const engine = HyperFormula.buildFromArray([ - ['=TDIST(1, 1, 2)'], - ['=TDIST(3, 2, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.5, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0954659662667092, 6) - }) - - it('should truncate input', () => { - const engine = HyperFormula.buildFromArray([ - ['=TDIST(1, 1.9, 2)'], - ['=TDIST(3, 2.9, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.5, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0954659662667092, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=TDIST(0, 1, 1)'], - ['=TDIST(0, 1, 2)'], - ['=TDIST(-0.01, 1, 1)'], - ['=TDIST(-0.01, 1, 2)'], - ['=TDIST(1, 0.9, 1)'], - ['=TDIST(1, 0.9, 2)'], - ['=TDIST(0, 1, 1.5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.5, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(1, 6) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - //product #2 returns value - expect(engine.getCellValue(adr('A7'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.IntegerExpected)) - }) -}) diff --git a/test/unit/interpreter/function-text.spec.ts b/test/unit/interpreter/function-text.spec.ts deleted file mode 100644 index 63719cbd7b..0000000000 --- a/test/unit/interpreter/function-text.spec.ts +++ /dev/null @@ -1,305 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src' -import {SimpleDateTime} from '../../../src/DateTimeHelper' -import {ErrorMessage} from '../../../src/error-message' -import {defaultStringifyDateTime} from '../../../src/format/format' -import {Maybe} from '../../../src/Maybe' -import {adr, detailedError} from '../testUtils' - -describe('TEXT()', () => { - it('works', () => { - const engine = HyperFormula.buildFromArray([[ - '2', - '=TEXT(A1, "mm/dd/yyyy")', - ]]) - - expect(engine.getCellValue(adr('B1'))).toEqual('01/01/1900') - }) - - it('wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=TEXT(42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('wrong format argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=TEXT(2, 42)'], - ['=TEXT(2, 0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('42') - expect(engine.getCellValue(adr('A2'))).toEqual('2') - }) - - it('wrong date argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=TEXT(TRUE(), "mm/dd/yyyy")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('12/31/1899') - }) - - it('day formats', () => { - const engine = HyperFormula.buildFromArray([[ - '=DATE(2018, 8, 8)', - '=TEXT(A1, "d d")', - '=TEXT(A1, "dd DD")', - ]]) - - expect(engine.getCellValue(adr('B1'))).toEqual('8 8') - expect(engine.getCellValue(adr('C1'))).toEqual('08 08') - }) - - it('month formats', () => { - const engine = HyperFormula.buildFromArray([[ - '=DATE(2018, 8, 8)', - '=TEXT(A1, "m M")', - '=TEXT(A1, "mm MM")', - ]]) - - expect(engine.getCellValue(adr('B1'))).toEqual('8 0') //heuristic - repeated month is minutes - expect(engine.getCellValue(adr('C1'))).toEqual('08 00') //heuristic - repeated month is minutes - }) - - it('year formats', () => { - const engine = HyperFormula.buildFromArray([[ - '=DATE(2018, 8, 8)', - '=TEXT(A1, "yy YY")', - '=TEXT(A1, "yyyy YYYY")', - ]]) - - expect(engine.getCellValue(adr('B1'))).toEqual('18 18') - expect(engine.getCellValue(adr('C1'))).toEqual('2018 2018') - }) - - it('12 hours', () => { - const engine = HyperFormula.buildFromArray([ - [ - '8/8/2018 14:00', - '=TEXT(A1, "hh:mm A/P")', - ], - [ - '8/8/2018 00:30', - '=TEXT(A2, "hh:mm AM/PM")', - ], - ['8/8/2018 00:30', '=TEXT(A3, "hh:mm am/pm")'], - [ - '8/8/2018 00:30', - '=TEXT(A4, "hh:mm a/p")', - ] - ]) - expect(engine.getCellValue(adr('B1'))).toEqual('02:00 P') - expect(engine.getCellValue(adr('B2'))).toEqual('12:30 AM') - expect(engine.getCellValue(adr('B3'))).toEqual('12:30 am') - expect(engine.getCellValue(adr('B4'))).toEqual('12:30 a') - }) - - it('24 hours', () => { - const engine = HyperFormula.buildFromArray([ - [ - '8/8/2018 13:59', - '=TEXT(A1, "HH:mm")', - ] - ]) - expect(engine.getCellValue(adr('B1'))).toEqual('13:59') - }) - - it('padding', () => { - const engine = HyperFormula.buildFromArray([ - [ - '8/8/2018 01:01:01', '=TEXT(A1, "H:m:s")', - ], - [ - '8/8/2018 01:11:11', '=TEXT(A2, "H:m:s")', - ] - ]) - expect(engine.getCellValue(adr('B1'))).toEqual('1:1:1') - expect(engine.getCellValue(adr('B2'))).toEqual('1:11:11') - }) - - it('fractions of seconds', () => { - const engine = HyperFormula.buildFromArray([ - [ - '0.0000011574074074074074', '=TEXT(A1, "hh:mm:ss.ss")', - ], - ['0.000001', '=TEXT(A2, "hh:mm:ss.sss")'] - ]) - expect(engine.getCellValue(adr('B1'))).toEqual('00:00:00.1') - expect(engine.getCellValue(adr('B2'))).toEqual('00:00:00.086') - }) - - it('distinguishes between months and minutes - not supported', () => { - const engine = HyperFormula.buildFromArray([[ - '=DATE(2018, 8, 8)', - '=TEXT(A1, "mm")', - '=TEXT(A1, "HH:mm")', - '=TEXT(A1, "H:m")', - ]]) - expect(engine.getCellValue(adr('B1'))).toEqual('08') - expect(engine.getCellValue(adr('C1'))).toEqual('00:00') - expect(engine.getCellValue(adr('D1'))).toEqual('0:0') - }) - - it('works for number format', () => { - const engine = HyperFormula.buildFromArray([ - ['12.45'], - ['=TEXT(A1, "###.###")'], - ['=TEXT(A1, "000.000")'], - ['=TEXT(A1, "$000.00")'], - ['=TEXT(A1, "$#.000")'], - ['=TEXT(A1, "$###.000")'], - ['=TEXT(A1, "000.00.00$")'], - ['=TEXT(A1, "###.##.##$")'], - ['=TEXT(A1, "$###,##0.00")'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual('12.45') - expect(engine.getCellValue(adr('A3'))).toEqual('012.450') - expect(engine.getCellValue(adr('A4'))).toEqual('$012.45') - expect(engine.getCellValue(adr('A5'))).toEqual('$12.450') - expect(engine.getCellValue(adr('A6'))).toEqual('$12.450') - expect(engine.getCellValue(adr('A7'))).toEqual('012.45.00$') - expect(engine.getCellValue(adr('A8'))).toEqual('12.45.##$') - expect(engine.getCellValue(adr('A9'))).toEqual('$12,##0.00') - }) - - it('works with currency format "$#.00"', () => { - const engine = HyperFormula.buildFromArray([ - ['=TEXT(0.5, "$#.00")'], - ['=TEXT(10, "$#.00")'], - ['=TEXT(100, "$#.00")'], - ['=TEXT(1000, "$#.00")'], - ['=TEXT(10000, "$#.00")'], - ['=TEXT(100000, "$#.00")'], - ['=TEXT(1000000, "$#.00")'], - ['=TEXT(10000000, "$#.00")'], - ['=TEXT(10000000.99, "$#.00")'], - ['=TEXT(10000000.99999, "$#.00")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('$0.50') - expect(engine.getCellValue(adr('A2'))).toEqual('$10.00') - expect(engine.getCellValue(adr('A3'))).toEqual('$100.00') - expect(engine.getCellValue(adr('A4'))).toEqual('$1000.00') - expect(engine.getCellValue(adr('A5'))).toEqual('$10000.00') - expect(engine.getCellValue(adr('A6'))).toEqual('$100000.00') - expect(engine.getCellValue(adr('A7'))).toEqual('$1000000.00') - expect(engine.getCellValue(adr('A8'))).toEqual('$10000000.00') - expect(engine.getCellValue(adr('A9'))).toEqual('$10000000.99') - expect(engine.getCellValue(adr('A10'))).toEqual('$10000001.00') - }) - - it('date and time format', () => { - const engine = HyperFormula.buildFromArray([ - ['1.100', '=TEXT(A1, "yyyy-mm-dd hh:mm:ss")'], - ['1.222', '=TEXT(A2, "yyyy-mm-dd hh:mm:ss")'], - ['0.99999', '=TEXT(A3, "yyyy-mm-dd hh:mm:ss")'], - ['0.999999', '=TEXT(A4, "yyyy-mm-dd hh:mm:ss")'], - ['0.9999999', '=TEXT(A5, "yyyy-mm-dd hh:mm:ss")'], - ['0.99999999', '=TEXT(A6, "yyyy-mm-dd hh:mm:ss")'], - ['0.999999999', '=TEXT(A7, "yyyy-mm-dd hh:mm:ss")'], - ['0.9999999999', '=TEXT(A8, "yyyy-mm-dd hh:mm:ss")'], - ['0.99999999999', '=TEXT(A9, "yyyy-mm-dd hh:mm:ss")'], - ['0.999999999999', '=TEXT(A10, "yyyy-mm-dd hh:mm:ss")'] - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual('1899-12-31 02:24:00') - expect(engine.getCellValue(adr('B2'))).toEqual('1899-12-31 05:19:40') - expect(engine.getCellValue(adr('B3'))).toEqual('1899-12-30 23:59:59') - expect(engine.getCellValue(adr('B4'))).toEqual('1899-12-30 23:59:59') - expect(engine.getCellValue(adr('B5'))).toEqual('1899-12-30 23:59:59') - expect(engine.getCellValue(adr('B6'))).toEqual('1899-12-30 23:59:59') - expect(engine.getCellValue(adr('B7'))).toEqual('1899-12-30 23:59:59') - expect(engine.getCellValue(adr('B8'))).toEqual('1899-12-30 23:59:59') - expect(engine.getCellValue(adr('B9'))).toEqual('1899-12-31 00:00:00') - expect(engine.getCellValue(adr('B10'))).toEqual('1899-12-31 00:00:00') - }) - - it('correct rounding', () => { - const engine = HyperFormula.buildFromArray([ - ['0.9999999999', '=TEXT(A1, "yyyy-mm-dd hh:mm:ss.sssss")'], - ['0.9999999999', '=TEXT(A2, "yyyy-mm-dd hh:mm:ss.ssss")'], - ['0.9999999999', '=TEXT(A3, "yyyy-mm-dd hh:mm:ss.sss")'], - ['0.9999999999', '=TEXT(A4, "yyyy-mm-dd hh:mm:ss.ss")'], - ['0.9999999999', '=TEXT(A5, "yyyy-mm-dd hh:mm:ss.s")'], - ['0.9999999999', '=TEXT(A6, "yyyy-mm-dd hh:mm:ss")'], - ['0.9999999999', '=TEXT(A7, "yyyy-mm-dd hh:mm")'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual('1899-12-30 23:59:59.99999') - expect(engine.getCellValue(adr('B2'))).toEqual('1899-12-30 23:59:59.9999') - expect(engine.getCellValue(adr('B3'))).toEqual('1899-12-30 23:59:59.999') - expect(engine.getCellValue(adr('B4'))).toEqual('1899-12-30 23:59:59.99') - expect(engine.getCellValue(adr('B5'))).toEqual('1899-12-30 23:59:59.9') - expect(engine.getCellValue(adr('B6'))).toEqual('1899-12-30 23:59:59') - expect(engine.getCellValue(adr('B7'))).toEqual('1899-12-30 23:59') - }) -}) - -describe('time duration', () => { - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['0.1', '=TEXT(A1, "[hh]:mm:ss")'], - ['1.1', '=TEXT(A2, "[hh]:mm:ss")', ], - ['0.1', '=TEXT(A3, "[mm]:ss")', ], - ['1.1', '=TEXT(A4, "[mm]:ss")', ], - ['1.1', '=TEXT(A5, "[hh]:m:ss")', ], - ['0.1111', '=TEXT(A6, "[mm]:ss.ss")', ], - ['0.1111', '=TEXT(A7, "[mm]:ss.00")', ], - ['0.1111', '=TEXT(A8, "hh:[mm]:s")', ], - ['0.1111', '=TEXT(A9, "h:[mm]")', ], - ['0.1111', '=TEXT(A10, "abc")', ], - ]) - expect(engine.getCellValue(adr('B1'))).toEqual('02:24:00') - expect(engine.getCellValue(adr('B2'))).toEqual('26:24:00') - expect(engine.getCellValue(adr('B3'))).toEqual('144:00') - expect(engine.getCellValue(adr('B4'))).toEqual('1584:00') - expect(engine.getCellValue(adr('B5'))).toEqual('26:24:00') - expect(engine.getCellValue(adr('B6'))).toEqual('159:59.04') - expect(engine.getCellValue(adr('B7'))).toEqual('159:59.04') - expect(engine.getCellValue(adr('B8'))).toEqual('02:39:59') - expect(engine.getCellValue(adr('B9'))).toEqual('2:39') - expect(engine.getCellValue(adr('B10'))).toEqual('abc') - }) -}) - -describe('Custom date printing', () => { - function customPrintDate(date: SimpleDateTime, dateFormat: string): Maybe { - const str = defaultStringifyDateTime(date, dateFormat) - if (str === undefined) { - return undefined - } else { - return 'fancy ' + str + ' fancy' - } - } - - it('works', () => { - const engine = HyperFormula.buildFromArray([[ - '2', - '=TEXT(A1, "mm/dd/yyyy")', - ]], {stringifyDateTime: customPrintDate}) - - expect(engine.getCellValue(adr('B1'))).toEqual('fancy 01/01/1900 fancy') - }) - - it('no effect for number format', () => { - const engine = HyperFormula.buildFromArray([[ - '12.45', - '=TEXT(A1, "###.###")', - '=TEXT(A1, "000.000")', - ]], {stringifyDateTime: customPrintDate}) - - expect(engine.getCellValue(adr('B1'))).toEqual('12.45') - expect(engine.getCellValue(adr('C1'))).toEqual('012.450') - }) - - it('date printing, month and minutes', () => { - const engine = HyperFormula.buildFromArray([['1.1', '=TEXT(A1, "mm-dd mm:ss.sssss")'], - ['1.222', '=TEXT(A2, "mm-dd mm:ss.sssss")']]) - expect(engine.getCellValue(adr('B1'))).toEqual('12-31 24:00') - expect(engine.getCellValue(adr('B2'))).toEqual('12-31 19:40.79999') - }) -}) diff --git a/test/unit/interpreter/function-time.spec.ts b/test/unit/interpreter/function-time.spec.ts deleted file mode 100644 index 0a7340d6f0..0000000000 --- a/test/unit/interpreter/function-time.spec.ts +++ /dev/null @@ -1,114 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueDetailedType, ErrorType} from '../../../src/Cell' -import {Config} from '../../../src/Config' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError, timeNumberToString} from '../testUtils' - -describe('Function TIME', () => { - it('with 3 numerical arguments', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=TIME(0, 0, 0)', '=TIME(21, 0, 54)', '=TIME(3, 10, 24)'], - ], config) - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(timeNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('00:00:00') - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValue(adr('B1'))).toEqual(0.875625) - expect(timeNumberToString(engine.getCellValue(adr('B1')), config)).toEqual('21:00:54') - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(0.132222222222222) - expect(timeNumberToString(engine.getCellValue(adr('C1')), config)).toEqual('03:10:24') - }) - - it('truncation', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=TIME(0.9, 0, 0)', '=TIME(21, 0.5, 54)', '=TIME(3, 10, 24.99)'], - ], config) - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(timeNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('00:00:00') - expect(engine.getCellValue(adr('B1'))).toEqual(0.875625) - expect(timeNumberToString(engine.getCellValue(adr('B1')), config)).toEqual('21:00:54') - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(0.132222222222222) - expect(timeNumberToString(engine.getCellValue(adr('C1')), config)).toEqual('03:10:24') - }) - - it('rollover', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=TIME(24, 0, 0)', '=TIME(19, 120, 54)', '=TIME(0, 189, 84)'], - ], config) - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(timeNumberToString(engine.getCellValue(adr('A1')), config)).toEqual('00:00:00') - expect(engine.getCellValue(adr('B1'))).toEqual(0.875625) - expect(timeNumberToString(engine.getCellValue(adr('B1')), config)).toEqual('21:00:54') - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(0.132222222222222) - expect(timeNumberToString(engine.getCellValue(adr('C1')), config)).toEqual('03:10:24') - }) - - it('negative', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=TIME(-1, 59, 0)', '=TIME(0, -1, 59)', '=TIME(0, 1, -61)'], - ], config) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NegativeTime)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NegativeTime)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NegativeTime)) - }) - - it('fractions', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=TIME(0, 0.9, 0)', '=TIME(0, 0, -0.9)', '=TIME(0.9, 0, 0)'], - ], config) - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('B1'))).toEqual(0) - expect(engine.getCellValue(adr('C1'))).toEqual(0) - }) - - it('number of arguments', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=TIME(0, 1)'], - ['=TIME(0, 1, 1, 1)'], - ], config) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('with incoercible argument', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=TIME("foo", 1, 1)'], - ['=TIME(0, "foo", 1)'], - ['=TIME(0, 1, "foo")'], - ], config) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('with coercible argument', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['="0"', '=TRUE()'], - ['=TIME(A1, 1, 1)'], - ['=TIME(0, B1, 1)'], - ['=TIME(0, 1, B1)'], - ], config) - expect(timeNumberToString(engine.getCellValue(adr('A2')), config)).toEqual('00:01:01') - expect(timeNumberToString(engine.getCellValue(adr('A3')), config)).toEqual('00:01:01') - expect(timeNumberToString(engine.getCellValue(adr('A4')), config)).toEqual('00:01:01') - }) - - it('precedence of errors', () => { - const config = new Config() - const engine = HyperFormula.buildFromArray([ - ['=TIME(FOOBAR(), 4/0, 1)'], - ['=TIME(0, FOOBAR(), 4/0)'], - ['=TIME(0, 1, FOOBAR())'], - ], config) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - }) -}) diff --git a/test/unit/interpreter/function-timevalue.spec.ts b/test/unit/interpreter/function-timevalue.spec.ts deleted file mode 100644 index 856e4374cf..0000000000 --- a/test/unit/interpreter/function-timevalue.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueDetailedType, ErrorType} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function TIMEVALUE', () => { - it('with wrong arguments', () => { - const engine = HyperFormula.buildFromArray([['=TIMEVALUE("foo")', '=TIMEVALUE(1)', '=TIMEVALUE(1, 2)', '=TIMEVALUE()']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.IncorrectDateTime)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.IncorrectDateTime)) - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('with string arguments', () => { - const engine = HyperFormula.buildFromArray([['=TIMEVALUE("3:00pm")', '=TIMEVALUE("15:00")', '=TIMEVALUE("21:00:00")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.625) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValue(adr('B1'))).toEqual(0.625) - expect(engine.getCellValue(adr('C1'))).toEqual(0.875) - }) - - it('ignores date', () => { - const engine = HyperFormula.buildFromArray([['=TIMEVALUE("3:00pm")', '=TIMEVALUE("31/12/2018 3:00pm")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.625) - expect(engine.getCellValue(adr('B1'))).toEqual(0.625) - }) - - it('rollover', () => { - const engine = HyperFormula.buildFromArray([['=TIMEVALUE("24:00")', '=TIMEVALUE("31/12/2018 24:00")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('B1'))).toEqual(0) - }) - - it('propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=TIMEVALUE(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should return NUMBER_TIME', () => { - const engine = HyperFormula.buildFromArray([ - ['=TIMEVALUE("14:31")'], - ]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.NUMBER_TIME) - }) -}) diff --git a/test/unit/interpreter/function-today.spec.ts b/test/unit/interpreter/function-today.spec.ts deleted file mode 100644 index 6ac6e10df2..0000000000 --- a/test/unit/interpreter/function-today.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {HyperFormula} from '../../../src' -import {CellValueDetailedType, ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Interpreter - function TODAY', () => { - let originalNow: () => number - - beforeEach(() => { - originalNow = Date.now - let cnt = 20 - Date.now = () => { - cnt += 1 - return Date.parse(`1985-08-16T03:45:${cnt}`) - } - }) - - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['=TODAY()'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(31275) - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_DATE) - }) - - it('works #2', () => { - const engine = HyperFormula.buildFromArray([ - ['=YEAR(TODAY())'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(1985) - }) - - it('works #3', () => { - const engine = HyperFormula.buildFromArray([ - ['=MONTH(TODAY())'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(8) - }) - - it('works #4', () => { - const engine = HyperFormula.buildFromArray([ - ['=DAY(TODAY())'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(16) - }) - - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=TODAY(42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - afterEach(() => { - Date.now = originalNow - }) -}) diff --git a/test/unit/interpreter/function-trim.spec.ts b/test/unit/interpreter/function-trim.spec.ts deleted file mode 100644 index e49c0e45dd..0000000000 --- a/test/unit/interpreter/function-trim.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function TRIM', () => { - it('should return N/A when number of arguments is incorrect', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRIM()'], - ['=TRIM("foo", "bar")'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRIM(" foo")'], - ['=TRIM("foo ")'], - ['=TRIM(" foo ")'], - ['=TRIM(" f o o ")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('foo') - expect(engine.getCellValue(adr('A2'))).toEqual('foo') - expect(engine.getCellValue(adr('A3'))).toEqual('foo') - expect(engine.getCellValue(adr('A4'))).toEqual('f o o') - }) - - it('should coerce other types to string', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRIM(1)'], - ['=TRIM(5+5)'], - ['=TRIM(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - expect(engine.getCellValue(adr('A2'))).toEqual('10') - expect(engine.getCellValue(adr('A3'))).toEqual('TRUE') - }) -}) diff --git a/test/unit/interpreter/function-true.spec.ts b/test/unit/interpreter/function-true.spec.ts deleted file mode 100644 index 2c3b35d6c2..0000000000 --- a/test/unit/interpreter/function-true.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function TRUE', () => { - it('works', () => { - const engine = HyperFormula.buildFromArray([['=TRUE()']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(true) - }) - - it('is 0-arity', () => { - const engine = HyperFormula.buildFromArray([['=TRUE(1)']]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) -}) diff --git a/test/unit/interpreter/function-unichar.spec.ts b/test/unit/interpreter/function-unichar.spec.ts deleted file mode 100644 index 098e72129e..0000000000 --- a/test/unit/interpreter/function-unichar.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function UNICHAR', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=UNICHAR()'], - ['=UNICHAR(1, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for wrong type of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=UNICHAR("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['=UNICHAR(1)'], - ['=UNICHAR(33)'], - ['=UNICHAR(65)'], - ['=UNICHAR(90)'], - ['=UNICHAR(209)'], - ['=UNICHAR(255)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('') - expect(engine.getCellValue(adr('A2'))).toEqual('!') - expect(engine.getCellValue(adr('A3'))).toEqual('A') - expect(engine.getCellValue(adr('A4'))).toEqual('Z') - expect(engine.getCellValue(adr('A5'))).toEqual('Ñ') - expect(engine.getCellValue(adr('A6'))).toEqual('ÿ') - }) - - it('should round down floats', () => { - const engine = HyperFormula.buildFromArray([ - ['=UNICHAR(42)'], - ['=UNICHAR(42.2)'], - ['=UNICHAR(42.8)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('*') - expect(engine.getCellValue(adr('A2'))).toEqual('*') - expect(engine.getCellValue(adr('A3'))).toEqual('*') - }) - - it('should work only for values from 1 to 1114111 truncating decimal part', () => { - const engine = HyperFormula.buildFromArray([ - ['=UNICHAR(0)'], - ['=UNICHAR(0.5)'], - ['=UNICHAR(1)'], - ['=UNICHAR(256)'], - ['=UNICHAR(1114111)'], - ['=UNICHAR(1114111.5)'], - ['=UNICHAR(1114112)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.CharacterCodeBounds)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.CharacterCodeBounds)) - expect(engine.getCellValue(adr('A3'))).toEqual('') - expect(engine.getCellValue(adr('A4'))).toEqual('Ā') - expect(engine.getCellValue(adr('A5'))).toEqual('􏿿') - expect(engine.getCellValue(adr('A6'))).toEqual('􏿿') - expect(engine.getCellValue(adr('A7'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.CharacterCodeBounds)) - }) -}) diff --git a/test/unit/interpreter/function-unicode.spec.ts b/test/unit/interpreter/function-unicode.spec.ts deleted file mode 100644 index 73ab450df2..0000000000 --- a/test/unit/interpreter/function-unicode.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ -import {CellValueType, ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function UNICODE', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=UNICODE()'], - ['=UNICODE("foo", "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for empty strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=UNICODE("")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.EmptyString)) - }) - - it('should work for single chars', () => { - const engine = HyperFormula.buildFromArray([ - ['=UNICODE("")'], - ['=UNICODE("!")'], - ['=UNICODE("A")'], - ['=UNICODE("Z")'], - ['=UNICODE("Ñ")'], - ['=UNICODE("ÿ")'], - ['=UNICODE(TRUE())'], - ['=UNICODE("€")'], - ['=UNICODE("􏿿")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(33) - expect(engine.getCellValue(adr('A3'))).toEqual(65) - expect(engine.getCellValue(adr('A4'))).toEqual(90) - expect(engine.getCellValue(adr('A5'))).toEqual(209) - expect(engine.getCellValue(adr('A6'))).toEqual(255) - expect(engine.getCellValue(adr('A7'))).toEqual(84) - expect(engine.getCellValue(adr('A8'))).toEqual(8364) - expect(engine.getCellValue(adr('A9'))).toEqual(1114111) - }) - - it('should return code of first character', () => { - const engine = HyperFormula.buildFromArray([ - ['=UNICODE("Abar")'], - ['=UNICODE("Ñbaz")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(65) - expect(engine.getCellValue(adr('A2'))).toEqual(209) - }) - - it('should return number', () => { - const engine = HyperFormula.buildFromArray([ - ['=UNICODE("foo")'] - ]) - - expect(engine.getCellValueType(adr('A1'))).toEqual(CellValueType.NUMBER) - }) - - it('should be identity when composed with UNICHAR', () => { - const engine = HyperFormula.buildFromArray([ - ['=UNICODE(UNICHAR(1))'], - ['=UNICODE(UNICHAR(128))'], - ['=UNICODE(UNICHAR(256))'], - ['=UNICODE(UNICHAR(8364))'], - ['=UNICODE(UNICHAR(1114111))'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqual(128) - expect(engine.getCellValue(adr('A3'))).toEqual(256) - expect(engine.getCellValue(adr('A4'))).toEqual(8364) - expect(engine.getCellValue(adr('A5'))).toEqual(1114111) - }) -}) diff --git a/test/unit/interpreter/function-upper.spec.ts b/test/unit/interpreter/function-upper.spec.ts deleted file mode 100644 index 7bd1191bdb..0000000000 --- a/test/unit/interpreter/function-upper.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function UPPER', () => { - it('should take one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=UPPER()'], - ['=UPPER("foo", "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should convert text to uppercase', () => { - const engine = HyperFormula.buildFromArray([ - ['=UPPER("")'], - ['=UPPER(B1)'], - ['=UPPER("FOO")'], - ['=UPPER("foo")'], - ['=UPPER("bAr")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('') - expect(engine.getCellValue(adr('A2'))).toEqual('') - expect(engine.getCellValue(adr('A3'))).toEqual('FOO') - expect(engine.getCellValue(adr('A4'))).toEqual('FOO') - expect(engine.getCellValue(adr('A5'))).toEqual('BAR') - }) - - it('should coerce', () => { - const engine = HyperFormula.buildFromArray([ - ['=UPPER(TRUE())'], - ['=UPPER(0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('TRUE') - expect(engine.getCellValue(adr('A2'))).toEqual('0') - }) -}) diff --git a/test/unit/interpreter/function-value.spec.ts b/test/unit/interpreter/function-value.spec.ts deleted file mode 100644 index 0687f3c706..0000000000 --- a/test/unit/interpreter/function-value.spec.ts +++ /dev/null @@ -1,451 +0,0 @@ -import {CellValueDetailedType, ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function VALUE', () => { - describe('argument validation', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE()'], - ['=VALUE("1", "2")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - }) - - describe('basic numeric string conversion', () => { - it('should convert integer string', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("123")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(123) - }) - - it('should convert decimal string', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("123.45")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(123.45) - }) - - it('should convert negative number string', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("-123")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(-123) - }) - - it('should convert string with plus sign', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("+123")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(123) - }) - - it('should trim leading and trailing spaces', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE(" 123 ")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(123) - }) - - it('should convert string with leading zeros', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("00123")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(123) - }) - - it('should convert scientific notation (uppercase E)', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("1.23E3")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(1230) - }) - - it('should convert scientific notation (lowercase e, negative exponent)', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("1.5e-2")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0.015) - }) - - it('should convert string with thousand separator', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("1,234")'], - ], {thousandSeparator: ',', functionArgSeparator: ';'}) - - expect(engine.getCellValue(adr('A1'))).toBe(1234) - }) - - it('should convert parentheses notation as negative number', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("(123)")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(-123) - }) - - it('should return VALUE error for nested parentheses', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("((123))")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return VALUE error for unbalanced parentheses', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("(123")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - }) - - describe('percentage strings', () => { - it('should convert percentage string to decimal', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("50%")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0.5) - }) - }) - - describe('currency strings', () => { - it('should convert currency string', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("$123")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(123) - }) - - it('should convert currency string with thousand separator and decimal', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("$1,234.56")'], - ], {thousandSeparator: ',', functionArgSeparator: ';'}) - - expect(engine.getCellValue(adr('A1'))).toBe(1234.56) - }) - }) - - describe('date strings', () => { - it('should convert date string to serial number', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("01/13/2026")'], - ], {dateFormats: ['MM/DD/YYYY']}) - - expect(engine.getCellValue(adr('A1'))).toBe(46035) - }) - }) - - describe('time strings', () => { - it('should convert time string to fraction of day', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("14:30")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.60416667, 6) - }) - - it('should convert time string with seconds', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("12:30:45")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.52135417, 6) - }) - - it('should handle time greater than 24 hours', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("25:00")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.04166667, 6) - }) - }) - - describe('datetime strings', () => { - it('should convert datetime string to serial number with time fraction', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("01/13/2026 14:30")'], - ], {dateFormats: ['MM/DD/YYYY']}) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(46035.60417, 4) - }) - }) - - describe('error cases', () => { - it('should return VALUE error for empty string', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return VALUE error for non-numeric string', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("abc")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return VALUE error for string with trailing text', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("123abc")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should return VALUE error for European decimal format in default locale', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("123,45")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - }) - - describe('12-hour time format', () => { - it('should parse 12-hour time format with am/pm', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("3:00pm")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.625, 6) - }) - }) - - describe('type coercion', () => { - it('should return VALUE error for boolean input', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE(TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should pass through number input unchanged', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE(123)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(123) - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE(1/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should convert cell reference with string value', () => { - const engine = HyperFormula.buildFromArray([ - ["'123", '=VALUE(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBe(123) - }) - - it('should pass through cell reference with numeric value', () => { - const engine = HyperFormula.buildFromArray([ - [123, '=VALUE(A1)'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBe(123) - }) - }) - - describe('locale-specific behavior', () => { - it('should respect decimal separator config', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("123,45")'], - ], {decimalSeparator: ',', thousandSeparator: ' ', functionArgSeparator: ';'}) - - expect(engine.getCellValue(adr('A1'))).toBe(123.45) - }) - - it('should respect thousand separator config', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("1 234,56")'], - ], {decimalSeparator: ',', thousandSeparator: ' ', functionArgSeparator: ';'}) - - expect(engine.getCellValue(adr('A1'))).toBe(1234.56) - }) - - it('should respect custom currency symbol', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("€123")'], - ], {currencySymbol: ['€']}) - - expect(engine.getCellValue(adr('A1'))).toBe(123) - }) - - it('should handle currency symbol at end', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("123€")'], - ], {currencySymbol: ['€']}) - - expect(engine.getCellValue(adr('A1'))).toBe(123) - }) - }) - - describe('custom parseDateTime', () => { - it('should use custom parseDateTime function for date parsing', () => { - const customParseDateTime = jasmine.createSpy().and.callFake((dateString: string) => { - const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(dateString) - if (match) { - return { - year: parseInt(match[1], 10), - month: parseInt(match[2], 10), - day: parseInt(match[3], 10), - } - } - return undefined - }) - - const engine = HyperFormula.buildFromArray([ - ['=VALUE("2026-01-13")'], - ], { - parseDateTime: customParseDateTime, - dateFormats: ['YYYY-MM-DD'], - }) - - expect(customParseDateTime).toHaveBeenCalledWith('2026-01-13', 'YYYY-MM-DD', 'hh:mm') - expect(engine.getCellValue(adr('A1'))).toBe(46035) - }) - - it('should use custom parseDateTime function for time parsing', () => { - const customParseDateTime = jasmine.createSpy().and.callFake((dateString: string) => { - const match = /^(\d{1,2})h(\d{2})m$/.exec(dateString) - if (match) { - return { - hours: parseInt(match[1], 10), - minutes: parseInt(match[2], 10), - seconds: 0, - } - } - return undefined - }) - - const engine = HyperFormula.buildFromArray([ - ['=VALUE("14h30m")'], - ], { - parseDateTime: customParseDateTime, - timeFormats: ['hh:mm'], - }) - - expect(customParseDateTime).toHaveBeenCalledWith('14h30m', 'DD/MM/YYYY', 'hh:mm') - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.60416667, 6) - }) - - it('should use custom parseDateTime function for datetime parsing', () => { - const customParseDateTime = jasmine.createSpy().and.callFake((dateString: string) => { - const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})$/.exec(dateString) - if (match) { - return { - year: parseInt(match[1], 10), - month: parseInt(match[2], 10), - day: parseInt(match[3], 10), - hours: parseInt(match[4], 10), - minutes: parseInt(match[5], 10), - seconds: 0, - } - } - return undefined - }) - - const engine = HyperFormula.buildFromArray([ - ['=VALUE("2026-01-13T14:30")'], - ], { - parseDateTime: customParseDateTime, - dateFormats: ['YYYY-MM-DD'], - timeFormats: ['hh:mm'], - }) - - expect(customParseDateTime).toHaveBeenCalledWith('2026-01-13T14:30', 'YYYY-MM-DD', 'hh:mm') - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(46035.60417, 4) - }) - - it('should return VALUE error when custom parseDateTime returns undefined', () => { - const customParseDateTime = jasmine.createSpy().and.returnValue(undefined) - - const engine = HyperFormula.buildFromArray([ - ['=VALUE("invalid-format")'], - ], { - parseDateTime: customParseDateTime, - }) - - expect(customParseDateTime).toHaveBeenCalledWith('invalid-format', 'DD/MM/YYYY', 'hh:mm') - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - }) - - describe('return type', () => { - it('should return NUMBER_RAW for numeric string', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("123")'], - ]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_RAW) - }) - - it('should return NUMBER_DATE for date string', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("01/13/2026")'], - ], {dateFormats: ['MM/DD/YYYY']}) - - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_DATE) - }) - - it('should return NUMBER_TIME for time string', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("14:30")'], - ]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_TIME) - }) - - it('should return NUMBER_DATETIME for datetime string', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("01/13/2026 14:30")'], - ], {dateFormats: ['MM/DD/YYYY']}) - - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - }) - - it('should return NUMBER_PERCENT for percentage string', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("50%")'], - ]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - }) - - it('should return NUMBER_CURRENCY for currency string', () => { - const engine = HyperFormula.buildFromArray([ - ['=VALUE("$123")'], - ]) - - expect(engine.getCellValueDetailedType(adr('A1'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - }) - }) -}) diff --git a/test/unit/interpreter/function-var.p.spec.ts b/test/unit/interpreter/function-var.p.spec.ts deleted file mode 100644 index b02eb0c04c..0000000000 --- a/test/unit/interpreter/function-var.p.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function VAR.P', () => { - it('should take at least one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=VAR.P()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate variance (population)', () => { - const engine = HyperFormula.buildFromArray([ - ['=VAR.P(2, 3)'], - ['=VAR.P(1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(0.25) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) - - it('should coerce explicit argument to numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=VAR.P(2, 3, 4, TRUE(), FALSE(), "1",)'], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.95918367346939, 6) //inconsistency with product #1 - }) - - it('should ignore non-numeric values in ranges, including ignoring logical values and text representation of numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=VAR.P(B1:I1)', 2, 3, 4, true, false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.666666666666667, 6) - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=VAR.P(B1:I1)', 2, 3, 4, '=NA()', false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-var.s.spec.ts b/test/unit/interpreter/function-var.s.spec.ts deleted file mode 100644 index 5f61666f76..0000000000 --- a/test/unit/interpreter/function-var.s.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function VAR.S', () => { - it('should take at least two arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=VAR.S()'], - ['=VAR.S(1)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should calculate variance (sample)', () => { - const engine = HyperFormula.buildFromArray([ - ['=VAR.S(2, 3)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(0.5) - }) - - it('should coerce explicit argument to numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=VAR.S(2, 3, 4, TRUE(), FALSE(), "1",)'], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(2.28571428571429, 6) //inconsistency with product #1 - }) - - it('should ignore non-numeric values in ranges, including ignoring logical values and text representation of numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=VAR.S(B1:I1)', 2, 3, 4, true, false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=VAR.S(B1:I1)', 2, 3, 4, '=NA()', false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-vara.spec.ts b/test/unit/interpreter/function-vara.spec.ts deleted file mode 100644 index e338cec370..0000000000 --- a/test/unit/interpreter/function-vara.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function VARA', () => { - it('should take at least two arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=VARA()'], - ['=VARA(1)'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('should calculate variance (sample)', () => { - const engine = HyperFormula.buildFromArray([ - ['=VARA(2, 3)'], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.5, 6) - }) - - it('should coerce explicit argument to numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=VARA(2, 3, 4, TRUE(), FALSE(), "1",)'], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(2.28571428571429) - }) - - it('should evaluate TRUE to 1, FALSE to 0 and text to 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=VARA(B1:I1)', 2, 3, 4, true, false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(2.61904761904762) - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=VARA(B1:I1)', 2, 3, 4, '=NA()', false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-varpa.spec.ts b/test/unit/interpreter/function-varpa.spec.ts deleted file mode 100644 index 77b8d3d734..0000000000 --- a/test/unit/interpreter/function-varpa.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function VARPA', () => { - it('should take at least one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=VARPA()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should calculate variance (population)', () => { - const engine = HyperFormula.buildFromArray([ - ['=VARPA(2, 3)'], - ['=VARPA(1)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(0.25) - expect(engine.getCellValue(adr('A2'))).toEqual(0) - }) - - it('should coerce explicit argument to numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=VARPA(2, 3, 4, TRUE(), FALSE(), "1",)'], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1.95918367346939, 6) - }) - - it('should evaluate TRUE to 1, FALSE to 0 and text to 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=VARPA(B1:I1)', 2, 3, 4, true, false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(2.24489795918367, 6) - }) - - it('should propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=VARPA(B1:I1)', 2, 3, 4, '=NA()', false, 'a', '\'1', null], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-version.spec.ts b/test/unit/interpreter/function-version.spec.ts deleted file mode 100644 index 754eb4b575..0000000000 --- a/test/unit/interpreter/function-version.spec.ts +++ /dev/null @@ -1,98 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ProtectedFunctionError} from '../../../src/errors' -import {InterpreterValue} from '../../../src/interpreter/InterpreterValue' -import {FunctionPlugin, FunctionPluginTypecheck} from '../../../src/interpreter/plugin/FunctionPlugin' -import {adr} from '../testUtils' - -describe('Function VERSION', () => { - describe('getting version', () => { - it('GPL license key', () => { - const engine = HyperFormula.buildFromArray([ - ['=VERSION()'], - ], { - licenseKey: 'gpl-v3', - }) - - expect(engine.getCellValue(adr('A1'))).toEqual(`HyperFormula v${HyperFormula.version}, 1`) - }) - - it('missing license key', () => { - const engine = HyperFormula.buildFromArray([ - ['=VERSION()'], - ], { - licenseKey: '', - }) - - expect(engine.getCellValue(adr('A1'))).toEqual(`HyperFormula v${HyperFormula.version}, 2`) - }) - - it('invalid license key', () => { - const engine = HyperFormula.buildFromArray([ - ['=VERSION()'], - ], { - licenseKey: '11111-11111-11111-11111-11111', - }) - - expect(engine.getCellValue(adr('A1'))).toEqual(`HyperFormula v${HyperFormula.version}, 3`) - }) - - it('expired license key', () => { - const engine = HyperFormula.buildFromArray([ - ['=VERSION()'], - ], { - licenseKey: '80584-cc272-2e7c4-06f16-4db00', - }) - - expect(engine.getCellValue(adr('A1'))).toEqual(`HyperFormula v${HyperFormula.version}, 4`) - }) - - it('correct license key', () => { - const engine = HyperFormula.buildFromArray([ - ['=VERSION()'], - ], { - licenseKey: 'internal-use-in-handsontable', - }) - - expect(engine.getCellValue(adr('A1'))).toEqual(`HyperFormula v${HyperFormula.version}, table`) - }) - }) - - describe('registering', () => { - class VersionExtra extends FunctionPlugin implements FunctionPluginTypecheck { - public static implementedFunctions = { - 'VERSION': { - method: 'version', - } - } - - public version(): InterpreterValue { - return 'version' - } - } - - it('should not allow registering VERSION formula', () => { - expect(() => { - HyperFormula.buildFromArray([ - ['=VERSION()'], - ], { - licenseKey: 'gpl-v3', - functionPlugins: [VersionExtra] - }) - }).toThrow(ProtectedFunctionError.cannotRegisterFunctionWithId('VERSION')) - }) - - it('should be available even if anyone unregistered', () => { - expect(() => { - HyperFormula.unregisterFunction('VERSION') - }).toThrow(ProtectedFunctionError.cannotUnregisterFunctionWithId('VERSION')) - - const engine = HyperFormula.buildFromArray([ - ['=VERSION()'], - ], { - licenseKey: 'gpl-v3', - }) - - expect(engine.getCellValue(adr('A1'))).toEqual(`HyperFormula v${HyperFormula.version}, 1`) - }) - }) -}) diff --git a/test/unit/interpreter/function-vlookup.spec.ts b/test/unit/interpreter/function-vlookup.spec.ts deleted file mode 100644 index 23b72e30ae..0000000000 --- a/test/unit/interpreter/function-vlookup.spec.ts +++ /dev/null @@ -1,826 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('ColumnIndex strategy', () => { - describe('VLOOKUP - args validation', () => { - it('not enough parameters', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1, A2:B3)'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('too many parameters', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1, A2:B3, 2, TRUE(), "foo")'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('wrong type of first argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(D1:E1, A2:B3, 2, TRUE())'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('wrong type of second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1, "foo", 2, TRUE())'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('wrong type of third argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1, A2:B3, "foo", TRUE())'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong type of fourth argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1, A2:B3, 2, "bar")'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('should return error when index argument greater that range width', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1, A2:B3, 3)'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.IndexLarge)) - }) - - it('should return error when index is less than one', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1, C2:D3, 0)'], - ['=VLOOKUP(1, C2:D3, -1)'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - }) - - it('should return #VALUE error when the found value is a range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', '=A1:B1'], - ['3', 'c'], - ['4', 'd'], - ['5', 'e'], - ['=VLOOKUP(2, A1:B5, 2)'], - ]) - - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('should propagate errors properly', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1/0, B1:B1, 1)'], - ['=VLOOKUP(1, B1:B1, 1/0)'], - ['=VLOOKUP(1, A10:A11, 1, NA())'] - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA)) - }) - }) - - describe('VLOOKUP', () => { - it('should find value in sorted range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['3', 'c'], - ['4', 'd'], - ['5', 'e'], - ['=VLOOKUP(2, A1:B5, 2)'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A6'))).toEqual('b') - }) - - it('should find value in sorted range using linearSearch', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['3', 'c'], - ['4', 'd'], - ['5', 'e'], - ['=VLOOKUP(2, A1:B5, 2, FALSE())'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A6'))).toEqual('b') - }) - - it('should return a single value even if there are more matching values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['2', 'c'], - ['2', 'd'], - ['2', 'e'], - ['=VLOOKUP(2, A1:B5, 2, FALSE())'] - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A6'))).toEqual('b') - expect(engine.getCellValue(adr('B6'))).toEqual(null) - expect(engine.getCellValue(adr('A7'))).toEqual(null) - }) - - it('should return the first matching value if RangeLookup = FALSE', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['2', 'c'], - ['2', 'd'], - ['2', 'e'], - ['=VLOOKUP(2, A1:B5, 2, FALSE())'] - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A6'))).toEqual('b') - }) - - it('should return the last matching value if RangeLookup = TRUE', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['2', 'c'], - ['2', 'd'], - ['2', 'e'], - ['=VLOOKUP(2, A1:B5, 2, TRUE())'] - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A6'))).toEqual('e') - }) - - it('works with wildcards', () => { - const engine = HyperFormula.buildFromArray([ - ['abd', 'a'], - [1, 'b'], - ['aaaa', 'c'], - ['ddaa', 'd'], - ['abcd', 'e'], - ['=VLOOKUP("*c*", A1:B5, 2, FALSE())'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A6'))).toEqual('e') - }) - - it('on sorted data ignores wildcards', () => { - const engine = HyperFormula.buildFromArray([ - ['abd', 'a'], - [1, 'b'], - ['*c*', 'c'], - ['ddaa', 'd'], - ['abcd', 'e'], - ['=VLOOKUP("*c*", A1:B5, 2, TRUE())'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A6'))).toEqual('c') - }) - - it('should find value in unsorted range using linearSearch', () => { - const engine = HyperFormula.buildFromArray([ - ['5', 'a'], - ['4', 'b'], - ['3', 'c'], - ['2', 'd'], - ['1', 'e'], - ['=VLOOKUP(2, A1:B5, 2, FALSE())'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A6'))).toEqual('d') - }) - - it('should find value in sorted range with different types', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['3', 'c'], - ['=TRUE()', 'd'], - ['foo', 'e'], - ['=VLOOKUP(TRUE(), A1:B5, 2, FALSE())'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A6'))).toEqual('d') - }) - - it('should find value in unsorted range with different types', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRUE()', 'a'], - ['4', 'b'], - ['foo', 'c'], - ['2', 'd'], - ['bar', 'e'], - ['=VLOOKUP(2, A1:B5, 2, FALSE())'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A6'))).toEqual('d') - }) - - it('should return the lower bound for sorted values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['8', 'c'], - ['=VLOOKUP(4, A1:B3, 2, TRUE())'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A4'))).toEqual('b') - }) - - it('should return the lower bound for sorted values if all are smaller than the search value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['3', 'c'], - ['=VLOOKUP(4, A1:B3, 2, TRUE())'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A4'))).toEqual('c') - }) - - it('should return error when all values are greater', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['3', 'c'], - ['=VLOOKUP(0, A1:B3, 2, TRUE())'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('should return error when value not present using linear search', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['3', 'c'], - ['=VLOOKUP(4, A1:B3, 2, FALSE())'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('should return #NA when searching in an empty range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['3', 'c'], - ['4', 'd'], - ['5', 'e'], - ['=VLOOKUP(42, X10:Y20, 2)'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('should find value if index build during evaluation', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['=A1', 'b'], - ['2', 'c'], - ['=VLOOKUP(1, A1:B3, 2, TRUE())'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A4'))).toEqual('b') - }) - - it('should properly calculate absolute row index', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(3, A3:A5, 1, TRUE())'], - ['foo'], - ['1'], - ['2'], - ['3'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('should work for standard matrices', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(3, A4:B6, 2, TRUE())'], - ['1', '2', '3'], - ['4', '5', '6'], - ['=TRANSPOSE(A2:C3)'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(6) - }) - - it('should work after updating standard matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(4, A4:B6, 2, TRUE())'], - ['1', '2', '3'], - ['4', '5', '6'], - ['=TRANSPOSE(A2:C3)'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(6) - - engine.setCellContents(adr('C2'), '5') - - expect(engine.getCellValue(adr('A1'))).toEqual(5) - }) - - it('should coerce empty arg to 0', () => { - const engine = HyperFormula.buildFromArray([ - ['0', 'a'], - ['2', 'b'], - ['3', 'c'], - ['4', 'd'], - ['5', 'e'], - ['=VLOOKUP(C3, A1:B5, 2)'], - ['=VLOOKUP(, A1:B5, 2)'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A6'))).toEqual('a') - expect(engine.getCellValue(adr('A7'))).toEqual('a') - }) - }) -}) - -describe('BinarySearchStrategy', () => { - describe('VLOOKUP - args validation', () => { - it('not enough parameters', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1, A2:B3)'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('too many parameters', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1, A2:B3, 2, TRUE(), "foo")'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('wrong type of first argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(D1:E1, A2:B3, 2, TRUE())'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('wrong type of second argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1, "foo", 2, TRUE())'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('wrong type of third argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1, A2:B3, "foo", TRUE())'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('wrong type of fourth argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1, A2:B3, 2, "bar")'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('should return error when index argument greater that range width', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1, A2:B3, 3)'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.IndexLarge)) - }) - - it('should return error when index is less than one', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1, C2:D3, 0)'], - ['=VLOOKUP(1, C2:D3, -1)'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.LessThanOne)) - }) - - it('should propagate errors properly', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(1/0, B1:B1, 1)'], - ['=VLOOKUP(1, B1:B1, 1/0)'], - ['=VLOOKUP(1, A10:A11, 1, NA())'] - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA)) - }) - }) - - describe('VLOOKUP', () => { - it('should find value in sorted range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['3', 'c'], - ['4', 'd'], - ['5', 'e'], - ['=VLOOKUP(2, A1:B5, 2)'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A6'))).toEqual('b') - }) - - it('should find value in sorted range using linearSearch', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['3', 'c'], - ['4', 'd'], - ['5', 'e'], - ['=VLOOKUP(2, A1:B5, 2, FALSE())'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A6'))).toEqual('b') - }) - - it('should return a single value even if there are more matching values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['2', 'c'], - ['2', 'd'], - ['2', 'e'], - ['=VLOOKUP(2, A1:B5, 2, FALSE())'] - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A6'))).toEqual('b') - expect(engine.getCellValue(adr('B6'))).toEqual(null) - expect(engine.getCellValue(adr('A7'))).toEqual(null) - }) - - it('should return the first matching value if RangeLookup = FALSE', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['2', 'c'], - ['2', 'd'], - ['2', 'e'], - ['=VLOOKUP(2, A1:B5, 2, FALSE())'] - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A6'))).toEqual('b') - }) - - it('should return the last matching value if RangeLookup = TRUE', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['2', 'c'], - ['2', 'd'], - ['2', 'e'], - ['=VLOOKUP(2, A1:B5, 2, TRUE())'] - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A6'))).toEqual('e') - }) - - it('works with wildcards', () => { - const engine = HyperFormula.buildFromArray([ - ['abd', 'a'], - [1, 'b'], - ['aaaa', 'c'], - ['ddaa', 'd'], - ['abcd', 'e'], - ['=VLOOKUP("*c*", A1:B5, 2, FALSE())'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A6'))).toEqual('e') - }) - - it('returns error when there is no matching value for the wildcard pattern', () => { - const engine = HyperFormula.buildFromArray([ - ['abd', 'a'], - [1, 'b'], - ['aaaa', 'c'], - ['ddaa', 'd'], - ['abbd', 'e'], - ['=VLOOKUP("*c*", A1:B5, 2, FALSE())'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('on sorted data ignores wildcards', () => { - const engine = HyperFormula.buildFromArray([ - ['abd', 'a'], - [1, 'b'], - ['*c*', 'c'], - ['ddaa', 'd'], - ['abcd', 'e'], - ['=VLOOKUP("*c*", A1:B5, 2, TRUE())'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A6'))).toEqual('c') - }) - - it('should find value in unsorted range using linearSearch', () => { - const engine = HyperFormula.buildFromArray([ - ['5', 'a'], - ['4', 'b'], - ['3', 'c'], - ['2', 'd'], - ['1', 'e'], - ['=VLOOKUP(2, A1:B5, 2, FALSE())'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A6'))).toEqual('d') - }) - - it('should find value in sorted range with different types', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['3', 'c'], - ['=TRUE()', 'd'], - ['foo', 'e'], - ['=VLOOKUP(TRUE(), A1:B5, 2, FALSE())'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A6'))).toEqual('d') - }) - - it('should find value in unsorted range with different types', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRUE()', 'a'], - ['4', 'b'], - ['foo', 'c'], - ['2', 'd'], - ['bar', 'e'], - ['=VLOOKUP(2, A1:B5, 2, FALSE())'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A6'))).toEqual('d') - }) - - it('should return the lower bound for sorted values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['8', 'c'], - ['=VLOOKUP(4, A1:B3, 2, TRUE())'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A4'))).toEqual('b') - }) - - it('should return the lower bound for sorted values if all are smaller than the search value', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['3', 'c'], - ['=VLOOKUP(4, A1:B3, 2, TRUE())'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A4'))).toEqual('c') - }) - - it('should return error when all values are greater', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['3', 'c'], - ['=VLOOKUP(0, A1:B3, 2, TRUE())'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('should return #NA when searching in an empty range', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['3', 'c'], - ['4', 'd'], - ['5', 'e'], - ['=VLOOKUP(42, X10:Y20, 2)'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('should return error when value not present using linear search', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['2', 'b'], - ['3', 'c'], - ['=VLOOKUP(4, A1:B3, 2, FALSE())'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('should find value if index build during evaluation', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'a'], - ['=A1', 'b'], - ['2', 'c'], - ['=VLOOKUP(1, A1:B3, 2, TRUE())'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A4'))).toEqual('b') - }) - - it('should properly calculate absolute row index', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(3, A3:A5, 1, TRUE())'], - ['foo'], - ['1'], - ['2'], - ['3'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('should work for standard matrices', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(3, A4:B6, 2, TRUE())'], - ['1', '2', '3'], - ['4', '5', '6'], - ['=TRANSPOSE(A2:C3)'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(6) - }) - - it('should work after updating standard matrix', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(4, A4:B6, 2, TRUE())'], - ['1', '2', '3'], - ['4', '5', '6'], - ['=TRANSPOSE(A2:C3)'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(6) - - engine.setCellContents(adr('C2'), '5') - - expect(engine.getCellValue(adr('A1'))).toEqual(5) - }) - - it('should coerce empty arg to 0', () => { - const engine = HyperFormula.buildFromArray([ - ['0', 'a'], - ['2', 'b'], - ['3', 'c'], - ['4', 'd'], - ['5', 'e'], - ['=VLOOKUP(C3, A1:B5, 2)'], - ['=VLOOKUP(, A1:B5, 2)'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A6'))).toEqual('a') - expect(engine.getCellValue(adr('A7'))).toEqual('a') - }) - }) - - it('should calculate indexes properly when using binary search', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(4, A5:A10, 1, TRUE())'], - [], - [], - [], - ['1'], - ['2'], - ['3'], - ['4'], - ['5'], - ], {useColumnIndex: false}) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('should calculate indexes properly when using naive approach', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(4, A5:A10, 1, FALSE())'], - [], - [], - [], - ['1'], - ['2'], - ['3'], - ['4'], - ['5'], - ], {useColumnIndex: false}) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('should coerce null to zero when using naive approach', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(, A2:A4, 1, FALSE())'], - [1], - [3], - [0], - ], {useColumnIndex: false}) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - }) - - it('should work on column ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP(2,B:C,2)', 1, 'a'], - [null, 2, 'b'], - [null, 3, 'c'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual('b') - }) - - it('works for strings, is not case sensitive', () => { - const engine = HyperFormula.buildFromArray([ - ['a', '1'], - ['b', '2'], - ['c', '3'], - ['A', '4'], - ['B', '5'], - ['=VLOOKUP("A", A1:B5, 2, FALSE())'] - ], {caseSensitive: false}) - - expect(engine.getCellValue(adr('A6'))).toEqual(1) - }) - - it('works for strings, is not case sensitive even if config defines case sensitivity', () => { - const engine = HyperFormula.buildFromArray([ - ['a', '1'], - ['b', '2'], - ['c', '3'], - ['A', '4'], - ['B', '5'], - ['=VLOOKUP("A", A1:B5, 2, FALSE())'] - ], {useColumnIndex: false, caseSensitive: true}) - - expect(engine.getCellValue(adr('A6'))).toEqual(1) - }) - - it('should find value in sorted range', () => { - const engine = HyperFormula.buildFromArray([ - ['a', '1'], - ['B', '2'], - ['c', '3'], - ['d', '4'], - ['e', '5'], - ['=VLOOKUP("b", A1:B5, 2)'], - ], {useColumnIndex: false, caseSensitive: false}) - expect(engine.getCellValue(adr('A6'))).toEqual(2) - }) - - it('should properly report no match', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP("0", A2:A5, 1)'], - [1], - [2], - [3], - ['\'1'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('should properly report approximate matching', () => { - const engine = HyperFormula.buildFromArray([ - ['=VLOOKUP("2", A2:A5, 1)'], - [1], - [2], - [3], - ['\'1'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('1') - }) - - it('works with a column range reference to an empty sheet', () => { - const hf = HyperFormula.buildFromSheets({ - table1: [], - table2: [['=VLOOKUP("42", table1!A:C, 1)']], - }) - - expect(hf.getCellValue(adr('A1', 1))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-weekday.spec.ts b/test/unit/interpreter/function-weekday.spec.ts deleted file mode 100644 index 8ad7b5a555..0000000000 --- a/test/unit/interpreter/function-weekday.spec.ts +++ /dev/null @@ -1,131 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function WEEKDAY', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKDAY(1, 2, 3)'], - ['=WEEKDAY()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for wrong type of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKDAY("foo", 1)'], - ['=WEEKDAY(2, "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should not work for wrong value of args', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKDAY(-1, 1)'], - ['=WEEKDAY(2, 9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.BadMode)) - }) - - it('should work for strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKDAY("31/07/2020")'], - ['=WEEKDAY("31/07/2020", "1")'], - ['=WEEKDAY("31/07/2020", "2")'], - ['=WEEKDAY("31/07/2020", "3")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(6) - expect(engine.getCellValue(adr('A2'))).toEqual(6) - expect(engine.getCellValue(adr('A3'))).toEqual(5) - expect(engine.getCellValue(adr('A4'))).toEqual(4) - }) - - it('should work for numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKDAY(0)'], - ['=WEEKDAY(0, 1)'], - ['=WEEKDAY(0, 2)'], - ['=WEEKDAY(0, 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(7) - expect(engine.getCellValue(adr('A2'))).toEqual(7) - expect(engine.getCellValue(adr('A3'))).toEqual(6) - expect(engine.getCellValue(adr('A4'))).toEqual(5) - }) - - it('should work for strings with different nullDate', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKDAY("31/07/2020")'], - ['=WEEKDAY("31/07/2020", "1")'], - ['=WEEKDAY("31/07/2020", "2")'], - ['=WEEKDAY("31/07/2020", "3")'], - ], {nullDate: {day: 20, month: 10, year: 1920}}) - - expect(engine.getCellValue(adr('A1'))).toEqual(6) - expect(engine.getCellValue(adr('A2'))).toEqual(6) - expect(engine.getCellValue(adr('A3'))).toEqual(5) - expect(engine.getCellValue(adr('A4'))).toEqual(4) - }) - - it('should work for strings with compatibility mode', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKDAY("31/07/2020")'], - ['=WEEKDAY("31/07/2020", "1")'], - ['=WEEKDAY("31/07/2020", "2")'], - ['=WEEKDAY("31/07/2020", "3")'], - ], {leapYear1900: true}) - - expect(engine.getCellValue(adr('A1'))).toEqual(6) - expect(engine.getCellValue(adr('A2'))).toEqual(6) - expect(engine.getCellValue(adr('A3'))).toEqual(5) - expect(engine.getCellValue(adr('A4'))).toEqual(4) - }) - - it('should work for strings with compatibility mode and different nullDate', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKDAY("31/07/2020")'], - ['=WEEKDAY("31/07/2020", "1")'], - ['=WEEKDAY("31/07/2020", "2")'], - ['=WEEKDAY("31/07/2020", "3")'], - ], {leapYear1900: true, nullDate: {day: 20, month: 10, year: 1920}}) - - expect(engine.getCellValue(adr('A1'))).toEqual(6) - expect(engine.getCellValue(adr('A2'))).toEqual(6) - expect(engine.getCellValue(adr('A3'))).toEqual(5) - expect(engine.getCellValue(adr('A4'))).toEqual(4) - }) - - it('big test', () => { - const args = [1, 2, 3, 11, 12, 13, 14, 15, 16, 17] - const dates = ['13/08/2020', '14/08/2020', '15/08/2020', '16/08/2020', '17/08/2020', '18/08/2020', '19/08/2020'] - const arrs = [] - for (const arg of args) { - const arr = [] - for (const date of dates) { - arr.push(`=WEEKDAY("${date}", ${arg})`) - } - arrs.push(arr) - } - const engine = HyperFormula.buildFromArray(arrs) - expect(engine.getSheetValues(0)).toEqual( - [[5, 6, 7, 1, 2, 3, 4], - [4, 5, 6, 7, 1, 2, 3], - [3, 4, 5, 6, 0, 1, 2], - [4, 5, 6, 7, 1, 2, 3], - [3, 4, 5, 6, 7, 1, 2], - [2, 3, 4, 5, 6, 7, 1], - [1, 2, 3, 4, 5, 6, 7], - [7, 1, 2, 3, 4, 5, 6], - [6, 7, 1, 2, 3, 4, 5], - [5, 6, 7, 1, 2, 3, 4]]) - }) -}) diff --git a/test/unit/interpreter/function-weeknum.spec.ts b/test/unit/interpreter/function-weeknum.spec.ts deleted file mode 100644 index 90016abc97..0000000000 --- a/test/unit/interpreter/function-weeknum.spec.ts +++ /dev/null @@ -1,145 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function WEEKNUM', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKNUM(1, 2, 3)'], - ['=WEEKNUM()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for wrong type of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKNUM("foo", 1)'], - ['=WEEKNUM(2, "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should not work for wrong value of args', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKNUM(-1, 1)'], - ['=WEEKNUM(2, 9)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.BadMode)) - }) - - it('should work for strings', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKNUM("02/08/2020")'], - ['=WEEKNUM("02/08/2020", "1")'], - ['=WEEKNUM("02/08/2020", "2")'], - ['=WEEKNUM("02/08/2020", "21")'], - ['=WEEKNUM("02/08/2017", "2")'], - ['=WEEKNUM("02/08/2017", "21")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(32) - expect(engine.getCellValue(adr('A2'))).toEqual(32) - expect(engine.getCellValue(adr('A3'))).toEqual(31) - expect(engine.getCellValue(adr('A4'))).toEqual(31) - expect(engine.getCellValue(adr('A5'))).toEqual(32) - expect(engine.getCellValue(adr('A6'))).toEqual(31) - }) - - it('should work for numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKNUM(0)'], - ['=WEEKNUM(0, 1)'], - ['=WEEKNUM(0, 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(52) - expect(engine.getCellValue(adr('A2'))).toEqual(52) - expect(engine.getCellValue(adr('A3'))).toEqual(53) - }) - - it('should work for strings with different nullDate', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKNUM("02/08/2020")'], - ['=WEEKNUM("02/08/2020", "1")'], - ['=WEEKNUM("02/08/2020", "2")'], - ['=WEEKNUM("02/08/2020", "21")'], - ['=WEEKNUM("02/08/2017", "2")'], - ['=WEEKNUM("02/08/2017", "21")'], - ], {nullDate: {day: 20, month: 10, year: 1920}}) - - expect(engine.getCellValue(adr('A1'))).toEqual(32) - expect(engine.getCellValue(adr('A2'))).toEqual(32) - expect(engine.getCellValue(adr('A3'))).toEqual(31) - expect(engine.getCellValue(adr('A4'))).toEqual(31) - expect(engine.getCellValue(adr('A5'))).toEqual(32) - expect(engine.getCellValue(adr('A6'))).toEqual(31) - }) - - it('should work for strings with compatibility mode', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKNUM("02/08/2020")'], - ['=WEEKNUM("02/08/2020", "1")'], - ['=WEEKNUM("02/08/2020", "2")'], - ['=WEEKNUM("02/08/2020", "21")'], - ['=WEEKNUM("02/08/2017", "2")'], - ['=WEEKNUM("02/08/2017", "21")'], - ], {leapYear1900: true}) - - expect(engine.getCellValue(adr('A1'))).toEqual(32) - expect(engine.getCellValue(adr('A2'))).toEqual(32) - expect(engine.getCellValue(adr('A3'))).toEqual(31) - expect(engine.getCellValue(adr('A4'))).toEqual(31) - expect(engine.getCellValue(adr('A5'))).toEqual(32) - expect(engine.getCellValue(adr('A6'))).toEqual(31) - }) - it('should work for strings with compatibility mode and different nullDate', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEEKNUM("02/08/2020")'], - ['=WEEKNUM("02/08/2020", "1")'], - ['=WEEKNUM("02/08/2020", "2")'], - ['=WEEKNUM("02/08/2020", "21")'], - ['=WEEKNUM("02/08/2017", "2")'], - ['=WEEKNUM("02/08/2017", "21")'], - ], {leapYear1900: true, nullDate: {day: 20, month: 10, year: 1920}}) - - expect(engine.getCellValue(adr('A1'))).toEqual(32) - expect(engine.getCellValue(adr('A2'))).toEqual(32) - expect(engine.getCellValue(adr('A3'))).toEqual(31) - expect(engine.getCellValue(adr('A4'))).toEqual(31) - expect(engine.getCellValue(adr('A5'))).toEqual(32) - expect(engine.getCellValue(adr('A6'))).toEqual(31) - }) - - it('big test', () => { - const args = [1, 2, 11, 12, 13, 14, 15, 16, 17, 21] - const dates = ['13/08/2020', '14/08/2020', '15/08/2020', '16/08/2020', '17/08/2020', '18/08/2020', '19/08/2020'] - const arrs = [] - for (const arg of args) { - const arr = [] - for (const date of dates) { - arr.push(`=WEEKNUM("${date}", ${arg})`) - } - arrs.push(arr) - } - const engine = HyperFormula.buildFromArray(arrs) - expect(engine.getSheetValues(0)).toEqual( - [[33, 33, 33, 34, 34, 34, 34], - [33, 33, 33, 33, 34, 34, 34], - [33, 33, 33, 33, 34, 34, 34], - [33, 33, 33, 33, 33, 34, 34], - [33, 33, 33, 33, 33, 33, 34], - [34, 34, 34, 34, 34, 34, 34], - [33, 34, 34, 34, 34, 34, 34], - [33, 33, 34, 34, 34, 34, 34], - [33, 33, 33, 34, 34, 34, 34], - [33, 33, 33, 33, 34, 34, 34], - ]) - }) -}) diff --git a/test/unit/interpreter/function-weibull.dist.spec.ts b/test/unit/interpreter/function-weibull.dist.spec.ts deleted file mode 100644 index a11905d89e..0000000000 --- a/test/unit/interpreter/function-weibull.dist.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function WEIBULL.DIST', () => { - it('should return error for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEIBULL.DIST(1, 2, 3)'], - ['=WEIBULL.DIST(1, 2, 3, 4, 5)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should return error for arguments of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEIBULL.DIST("foo", 2, 3, TRUE())'], - ['=WEIBULL.DIST(1, "baz", 3, TRUE())'], - ['=WEIBULL.DIST(1, 2, "baz", TRUE())'], - ['=WEIBULL.DIST(1, 2, 3, "abcd")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('should work as cdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEIBULL.DIST(0.1, 1, 2, TRUE())'], - ['=WEIBULL.DIST(0.5, 2, 4, TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.048770575499286, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0155035629945915, 6) - }) - - it('should work as pdf', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEIBULL.DIST(0.1, 1, 2, FALSE())'], - ['=WEIBULL.DIST(0.5, 2, 4, FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.475614712250357, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.061531027312838, 6) - }) - - it('checks bounds', () => { - const engine = HyperFormula.buildFromArray([ - ['=WEIBULL.DIST(0, 1, 1, FALSE())'], - ['=WEIBULL.DIST(-0.01, 0.01, 0.01, FALSE())'], - ['=WEIBULL.DIST(0, 0, 0.01, FALSE())'], - ['=WEIBULL.DIST(0, 0.01, 0, FALSE())'], - ]) - - //product #2 returns different value - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-workday.intl.spec.ts b/test/unit/interpreter/function-workday.intl.spec.ts deleted file mode 100644 index d41ce23395..0000000000 --- a/test/unit/interpreter/function-workday.intl.spec.ts +++ /dev/null @@ -1,140 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function WORKDAY.INTL', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=WORKDAY.INTL(1)', '=WORKDAY.INTL(1, 1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should check for types or value of third argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=WORKDAY.INTL(0, 1, TRUE())'], - ['=WORKDAY.INTL(0, 1, "1")'], - ['=WORKDAY.INTL(0, 1, "1010102")'], - ['=WORKDAY.INTL(0, 1, -1)'], - ['=WORKDAY.INTL(0, 1, "1111111")'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.WeekendString)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.WeekendString)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.BadMode)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.WeekendString)) - }) - - it('works correctly for first two arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=WORKDAY.INTL(1000, 1)'], - ['=WORKDAY.INTL(1000.9, 1.9)'], - ['=WORKDAY.INTL(1000.9, -1)'], - ['=WORKDAY.INTL(1000, -1.9)'], - ['=WORKDAY.INTL(1000, 0)'], - ['=WORKDAY.INTL(1000, 0.9)'], - ['=WORKDAY.INTL(1000, -0.9)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(1003) - expect(engine.getCellValue(adr('A2'))).toEqual(1003) - expect(engine.getCellValue(adr('A3'))).toEqual(999) - expect(engine.getCellValue(adr('A4'))).toEqual(999) - expect(engine.getCellValue(adr('A5'))).toEqual(1000) - expect(engine.getCellValue(adr('A6'))).toEqual(1000) - expect(engine.getCellValue(adr('A7'))).toEqual(1000) - }) - - it('today plus 1', () => { - const engine = HyperFormula.buildFromArray([ - ['=WORKDAY.INTL("29/09/2020", 1)'], - ['=WORKDAY.INTL("29/09/2020", 1, 3)'], - ['=WORKDAY.INTL("29/09/2020", 1, 4)'], - ['=WORKDAY.INTL("29/09/2020", 1, 5)'], - ['=WORKDAY.INTL("29/09/2020", 1, 6)'], - ['=WORKDAY.INTL("29/09/2020", 1, 13)'], - ['=WORKDAY.INTL("29/09/2020", 1, 14)'], - ['=WORKDAY.INTL("29/09/2020", 1, 15)'], - ['=WORKDAY.INTL("29/09/2020", 1, "1011111")'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(44104) - expect(engine.getCellValue(adr('A2'))).toEqual(44104) - expect(engine.getCellValue(adr('A3'))).toEqual(44105) - expect(engine.getCellValue(adr('A4'))).toEqual(44106) - expect(engine.getCellValue(adr('A5'))).toEqual(44104) - expect(engine.getCellValue(adr('A6'))).toEqual(44104) - expect(engine.getCellValue(adr('A7'))).toEqual(44105) - expect(engine.getCellValue(adr('A8'))).toEqual(44104) - expect(engine.getCellValue(adr('A9'))).toEqual(44110) - }) - - it('today minus 1', () => { - const engine = HyperFormula.buildFromArray([ - ['=WORKDAY.INTL("29/09/2020", -1)'], - ['=WORKDAY.INTL("29/09/2020", -1, 2)'], - ['=WORKDAY.INTL("29/09/2020", -1, 3)'], - ['=WORKDAY.INTL("29/09/2020", -1, 4)'], - ['=WORKDAY.INTL("29/09/2020", -1, 5)'], - ['=WORKDAY.INTL("29/09/2020", -1, 12)'], - ['=WORKDAY.INTL("29/09/2020", -1, 13)'], - ['=WORKDAY.INTL("29/09/2020", -1, 14)'], - ['=WORKDAY.INTL("29/09/2020", -1, "1011111")'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(44102) - expect(engine.getCellValue(adr('A2'))).toEqual(44100) - expect(engine.getCellValue(adr('A3'))).toEqual(44101) - expect(engine.getCellValue(adr('A4'))).toEqual(44102) - expect(engine.getCellValue(adr('A5'))).toEqual(44102) - expect(engine.getCellValue(adr('A6'))).toEqual(44101) - expect(engine.getCellValue(adr('A7'))).toEqual(44102) - expect(engine.getCellValue(adr('A8'))).toEqual(44102) - expect(engine.getCellValue(adr('A9'))).toEqual(44096) - }) - - it('this year', () => { - const engine = HyperFormula.buildFromArray([ - ['29/09/2020', '=A1+0.1', '31/12/2019', '01/01/2021', '27/09/2020'], - ['=WORKDAY.INTL("01/01/2020", 262, 1)'], - ['=WORKDAY.INTL("01/01/2020", 262, 1, A1:A1)'], - ['=WORKDAY.INTL("01/01/2020", 262, 1, A1:B1)'], - ['=WORKDAY.INTL("01/01/2020", 262, 1, A1:D1)'], - ['=WORKDAY.INTL("01/01/2020", 262, 1, A1:E1)'], - ]) - expect(engine.getCellValue(adr('A2'))).toEqual(44197) - expect(engine.getCellValue(adr('A3'))).toEqual(44200) - expect(engine.getCellValue(adr('A4'))).toEqual(44200) - expect(engine.getCellValue(adr('A5'))).toEqual(44201) - expect(engine.getCellValue(adr('A6'))).toEqual(44201) - }) - - it('should output correct values', () => { - const engine = HyperFormula.buildFromArray([ - ['01/01/2020', '=A1+5', '=A1+8', '=A1+9', '=A1+15', '=A1+18', '=A1+19', '=A1+32', '=A1+54', '=A1+55'], - ['=WORKDAY.INTL(A1, 91, "0000000", A1:J1)'], - ['=WORKDAY.INTL(A1+7, 9, "0000000", A1:J1)'], - ['=WORKDAY.INTL(A1+7, 86, "0000000", A1:J1)'], - ['=WORKDAY.INTL(A1+13, 34, "0000000", A1:J1)'], - ['=WORKDAY.INTL(A1+50, 5, "0000000", A1:J1)'], - ]) - expect(engine.getCellValue(adr('A2'))).toEqual(43931) - expect(engine.getCellValue(adr('A3'))).toEqual(43852) - expect(engine.getCellValue(adr('A4'))).toEqual(43932) - expect(engine.getCellValue(adr('A5'))).toEqual(43882) - expect(engine.getCellValue(adr('A6'))).toEqual(43888) - }) - - it('checks types in last argument', () => { - const engine = HyperFormula.buildFromArray([ - [true, '\'1', null, '=NA()'], - ['=WORKDAY.INTL(1000, 1, 1, A1:A1)'], - ['=WORKDAY.INTL(1000, 1, 1, B1:B1)'], - ['=WORKDAY.INTL(1000, 1, 1, C1:C1)'], - ['=WORKDAY.INTL(1000, 1, 1, A1:D1)'], - ]) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A4'))).toEqual(1003) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-workday.spec.ts b/test/unit/interpreter/function-workday.spec.ts deleted file mode 100644 index b7e556bccb..0000000000 --- a/test/unit/interpreter/function-workday.spec.ts +++ /dev/null @@ -1,79 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function WORKDAY', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=WORKDAY(1)', '=WORKDAY(1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works correctly for first two arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=WORKDAY(1000, 1)'], - ['=WORKDAY(1000.9, 1.9)'], - ['=WORKDAY(1000.9, -1)'], - ['=WORKDAY(1000, -1.9)'], - ['=WORKDAY(1000, 0)'], - ['=WORKDAY(1000, 0.9)'], - ['=WORKDAY(1000, -0.9)'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(1003) - expect(engine.getCellValue(adr('A2'))).toEqual(1003) - expect(engine.getCellValue(adr('A3'))).toEqual(999) - expect(engine.getCellValue(adr('A4'))).toEqual(999) - expect(engine.getCellValue(adr('A5'))).toEqual(1000) - expect(engine.getCellValue(adr('A6'))).toEqual(1000) - expect(engine.getCellValue(adr('A7'))).toEqual(1000) - }) - - it('this year', () => { - const engine = HyperFormula.buildFromArray([ - ['29/09/2020', '=A1+0.1', '31/12/2019', '01/01/2021', '27/09/2020'], - ['=WORKDAY("01/01/2020", 262)'], - ['=WORKDAY("01/01/2020", 262, A1:A1)'], - ['=WORKDAY("01/01/2020", 262, A1:B1)'], - ['=WORKDAY("01/01/2020", 262, A1:D1)'], - ['=WORKDAY("01/01/2020", 262, A1:E1)'], - ]) - expect(engine.getCellValue(adr('A2'))).toEqual(44197) - expect(engine.getCellValue(adr('A3'))).toEqual(44200) - expect(engine.getCellValue(adr('A4'))).toEqual(44200) - expect(engine.getCellValue(adr('A5'))).toEqual(44201) - expect(engine.getCellValue(adr('A6'))).toEqual(44201) - }) - - it('should output correct values', () => { - const engine = HyperFormula.buildFromArray([ - ['01/01/2020', '=A1+5', '=A1+8', '=A1+9', '=A1+15', '=A1+18', '=A1+19', '=A1+32', '=A1+54', '=A1+55'], - ['=WORKDAY(A1, 65, A1:J1)'], - ['=WORKDAY(A1+7, 6, A1:J1)'], - ['=WORKDAY(A1+7, 62, A1:J1)'], - ['=WORKDAY(A1+13, 26, A1:J1)'], - ['=WORKDAY(A1+50, 3, A1:J1)'], - ]) - expect(engine.getCellValue(adr('A2'))).toEqual(43931) - expect(engine.getCellValue(adr('A3'))).toEqual(43852) - expect(engine.getCellValue(adr('A4'))).toEqual(43934) - expect(engine.getCellValue(adr('A5'))).toEqual(43882) - expect(engine.getCellValue(adr('A6'))).toEqual(43888) - }) - - it('checks types in last argument', () => { - const engine = HyperFormula.buildFromArray([ - [true, '\'1', null, '=NA()'], - ['=WORKDAY(1000, 1, A1:A1)'], - ['=WORKDAY(1000, 1, B1:B1)'], - ['=WORKDAY(1000, 1, C1:C1)'], - ['=WORKDAY(1000, 1, A1:D1)'], - ]) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - expect(engine.getCellValue(adr('A4'))).toEqual(1003) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/function-xlookup.spec.ts b/test/unit/interpreter/function-xlookup.spec.ts deleted file mode 100644 index fc3f4fe1bf..0000000000 --- a/test/unit/interpreter/function-xlookup.spec.ts +++ /dev/null @@ -1,1276 +0,0 @@ -import { HyperFormula, ErrorType } from '../../../src' -import { ErrorMessage } from '../../../src/error-message' -import { adr, detailedError } from '../testUtils' -import { AbsoluteCellRange } from '../../../src/AbsoluteCellRange' - -describe('Function XLOOKUP', () => { - describe('validates arguments', () => { - it('returns error when less than 3 arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1, A2:B3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('returns error when more than 5 arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1, A2:A3, B2:B3, "foo", 0, 1, 42)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('returns error when shapes of lookupArray and returnArray are incompatible', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1, B1:B10, C1:C9)'], // returnArray too short - ['=XLOOKUP(1, B1:B10, C1:C11)'], // returnArray too long - ['=XLOOKUP(1, B1:B10, C1:D5)'], // returnArray too short - ['=XLOOKUP(1, B1:E1, B2:D2)'], // returnArray too short - ['=XLOOKUP(1, B1:E1, B2:F2)'], // returnArray too long - ['=XLOOKUP(1, B1:E1, B2:C3)'], // returnArray too short - ['=XLOOKUP(1, B1:B3, C1:E1)'], // transposed - ['=XLOOKUP(1, C1:E1, B1:B3)'], // transposed - ['=XLOOKUP(1, B1:C2, D3:E4)'], // lookupArray: 2d range - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongDimension)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongDimension)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongDimension)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongDimension)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongDimension)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongDimension)) - expect(engine.getCellValue(adr('A7'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongDimension)) - expect(engine.getCellValue(adr('A8'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongDimension)) - expect(engine.getCellValue(adr('A9'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongDimension)) - }) - - it('returns error when matchMode is of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1, B1:B2, C1:C2, 0, -2)'], - ['=XLOOKUP(1, B1:B2, C1:C2, 0, 3)'], - ['=XLOOKUP(1, B1:B2, C1:C2, 0, 0.5)'], - ['=XLOOKUP(1, B1:B2, C1:C2, 0, "string")'], - ['=XLOOKUP(1, B1:B2, C1:C2, 0, B1:B2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.BadMode)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.BadMode)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.BadMode)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('returns error when searchMode is of wrong type', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1, B1:B2, C1:C2, 0, 0, -3)'], - ['=XLOOKUP(1, B1:B2, C1:C2, 0, 0, 3)'], - ['=XLOOKUP(1, B1:B2, C1:C2, 0, 0, 0)'], - ['=XLOOKUP(1, B1:B2, C1:C2, 0, 0, 0.5)'], - ['=XLOOKUP(1, B1:B2, C1:C2, 0, 0, "string")'], - ['=XLOOKUP(1, B1:B2, C1:C2, 0, 0, D1:D2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.BadMode)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.BadMode)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.BadMode)) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.BadMode)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A6'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('propagates errors properly', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1/0, B1:B1, 1)'], - ['=XLOOKUP(1, B1:B1, 1/0)'], - ['=XLOOKUP(1, A10:A11, NA())'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.NA)) - }) - }) - - describe('with default matchMode and searchMode', () => { - it('finds value in a sorted row', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(2, B1:D1, B1:D1)', 1, 2, 3], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('finds value in an unsorted row', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(2, B1:D1, B1:D1)', 4, 2, 3], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('finds value in a sorted column', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(2, B1:B3, B1:B3)', 1], - ['', 2], - ['', 3], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('finds value in an unsorted column', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(2, B1:B3, B1:B3)', 4], - ['', 2], - ['', 3], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('when key is not found, returns ifNotFound value or NA error', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(2, B1:B3, B1:B3)'], - ['=XLOOKUP(2, B1:D1, B1:D1)'], - ['=XLOOKUP(2, B1:B3, B1:B3, "not found")'], - ['=XLOOKUP(2, B1:D1, B1:D1, "not found")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - expect(engine.getCellValue(adr('A3'))).toEqual('not found') - expect(engine.getCellValue(adr('A4'))).toEqual('not found') - }) - - it('works when returnArray is shifted (vertical search)', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(2, B1:B3, C11:C13)', 1], - ['', 2], - ['', 3], - [], - [], - [], - [], - [], - [], - [], - ['', '', 'a'], - ['', '', 'b'], - ['', '', 'c'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('b') - }) - - it('works when returnArray is shifted (horizontal search)', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(2, B1:D1, C2:E2)', '1', '2', '3'], - ['', '', 'a', 'b', 'c'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('b') - }) - - it('should not perform the wildcard match unless matchMode=2', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:E2, A2:E2)'], - ['a', 'axxb', 'a1b111', 'a2b222', 'x'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - it('should not perform the wildcard match unless matchMode=2 (ColumnIndex)', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:E2, A2:E2)'], - ['a', 'axxb', 'a1b111', 'a2b222', 'x'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.ValueNotFound)) - }) - - describe('when lookupArray is a single-cell range', () => { - it('returns single cell, when returnArray is also a single-cell range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1, B1:B1, C1:C1)', 1, 'a'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('a') - }) - - it('returns a vertical range, when returnArray is a vertical range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1, B1:B1, A3:A4)', 1], - [], - ['b'], - ['c'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('b') - expect(engine.getCellValue(adr('A2'))).toEqual('c') - }) - - it('returns a horizontal range, when returnArray is a horizontal range', () => { - const engine = HyperFormula.buildFromArray([ - [1, 'b', 'c'], - ['=XLOOKUP(1, A1:A1, B1:C1)'], - ]) - - expect(engine.getCellValue(adr('A2'))).toEqual('b') - expect(engine.getCellValue(adr('B2'))).toEqual('c') - }) - }) - - it('finds an empty cell', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("", B1:D1, B2:D2)', 1, 2, ''], - ['', 'a', 'b', 'c'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('c') - }) - }) - - describe('with BinarySearch column search strategy, when provided with searchMode =', () => { - it('1, finds the first match in unsorted horizontal range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1, A2:E2, A3:E3, "NotFound", 0, 1)'], - [2, 1, 3, 1, 4], - [1, 2, 3, 4, 5], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('1, finds the first match in unsorted vertical range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1, A2:A6, B2:B6, "NotFound", 0, 1)'], - [2, 1], - [1, 2], - [3, 3], - [1, 4], - [4, 5] - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('1, returns "NotFound" if there is no match', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(5, A2:A6, B2:B6, "NotFound", 0, 1)'], - [2, 1], - [1, 2], - [3, 3], - [1, 4], - [4, 5] - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - - it('-1, finds the last match in unsorted horizontal range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1, A2:E2, A3:E3, "NotFound", 0, -1)'], - [2, 1, 3, 1, 4], - [1, 2, 3, 4, 5], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('-1, finds the last match in unsorted vertical range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1, A2:A6, B2:B6, "NotFound", 0, -1)'], - [2, 1], - [1, 2], - [3, 3], - [1, 4], - [4, 5] - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('-1, returns "NotFound" if there is no match', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(5, A2:A6, B2:B6, "NotFound", 0, -1)'], - [2, 1], - [1, 2], - [3, 3], - [1, 4], - [4, 5] - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - - it('2, finds the value in horizontal range sorted ascending', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(2, A2:E2, A2:E2, "NotFound", 0, 2)'], - [1, 2, 2, 5, 5], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('2, finds the value in vertical range sorted ascending', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(2, A2:A6, A2:A6, "NotFound", 0, 2)'], - [1], - [2], - [2], - [5], - [5], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('2, returns "NotFound" if there is no match in a range sorted ascending', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(3, A2:A6, A2:A6, "NotFound", 0, 2)'], - [1], - [2], - [2], - [5], - [5], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - - it('-2, finds the value in horizontal range sorted descending', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(2, A2:E2, A2:E2, "NotFound", 0, -2)'], - [5, 2, 2, 1, 1], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('-2, finds the value in vertical range sorted descending', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(2, A2:A6, A2:A6, "NotFound", 0, -2)'], - [5], - [2], - [2], - [1], - [1], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('-2, returns "NotFound" if there is no match in a range sorted descending', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(3, A2:A6, A2:A6, "NotFound", 0, -2)'], - [5], - [2], - [2], - [1], - [1], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - }) - - describe('with ColumnIndex column search strategy, when provided with searchMode =', () => { - it('1, finds the first match in unsorted horizontal range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1, A2:E2, A3:E3, "NotFound", 0, 1)'], - [2, 1, 3, 1, 4], - [1, 2, 3, 4, 5], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('1, finds the first match in unsorted vertical range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1, A2:A6, B2:B6, "NotFound", 0, 1)'], - [2, 1], - [1, 2], - [3, 3], - [1, 4], - [4, 5] - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('1, returns "NotFound" if there is no match', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(5, A2:A6, B2:B6, "NotFound", 0, 1)'], - [2, 1], - [1, 2], - [3, 3], - [1, 4], - [4, 5] - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - - it('-1, finds the last match in unsorted horizontal range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1, A2:E2, A3:E3, "NotFound", 0, -1)'], - [2, 1, 3, 1, 4], - [1, 2, 3, 4, 5], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('-1, finds the last match in unsorted vertical range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(1, A2:A6, B2:B6, "NotFound", 0, -1)'], - [2, 1], - [1, 2], - [3, 3], - [1, 4], - [4, 5] - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(4) - }) - - it('-1, returns "NotFound" if there is no match', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(5, A2:A6, B2:B6, "NotFound", 0, -1)'], - [2, 1], - [1, 2], - [3, 3], - [1, 4], - [4, 5] - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - - it('2, finds the value in horizontal range sorted ascending', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(2, A2:E2, A2:E2, "NotFound", 0, 2)'], - [1, 2, 2, 5, 5], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('2, finds the value in vertical range sorted ascending', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(2, A2:A6, A2:A6, "NotFound", 0, 2)'], - [1], - [2], - [2], - [5], - [5], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('2, returns "NotFound" if there is no match in a range sorted ascending', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(3, A2:A6, A2:A6, "NotFound", 0, 2)'], - [1], - [2], - [2], - [5], - [5], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - - it('-2, finds the value in horizontal range sorted descending', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(2, A2:E2, A2:E2, "NotFound", 0, -2)'], - [5, 2, 2, 1, 1], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('-2, finds the value in vertical range sorted descending', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(2, A2:A6, A2:A6, "NotFound", 0, -2)'], - [5], - [2], - [2], - [1], - [1], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) - - it('-2, returns "NotFound" if there is no match in a range sorted descending', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(3, A2:A6, A2:A6, "NotFound", 0, -2)'], - [5], - [2], - [2], - [1], - [1], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - }) - - describe('with BinarySearch column search strategy, when provided with matchMode =', () => { - describe('-1 (looking for a lower bound)', () => { - describe('in array ordered ascending', () => { - it('returns exact match if exists', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, 2)'], - [1, 2, 42, 50, 51], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(42) - }) - - it('returns a lower bound when there is no exact match', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, 2)'], - [1, 2, 40, 50, 51], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(40) - }) - - it('returns a lower bound when all elements are smaller than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, 2)'], - [1, 2, 3, 4, 5], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(5) - }) - - it('returns NotFound when all elements are greater than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, 2)'], - [43, 44, 45, 46, 47], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - }) - - describe('in array ordered descending', () => { - it('returns exact match if exists', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, -2)'], - [55, 54, 42, 2, 1], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(42) - }) - - it('returns a lower bound when there is no exact match', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, -2)'], - [55, 54, 40, 2, 1], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(40) - }) - - it('returns a lower bound when all elements are smaller than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, -2)'], - [5, 4, 3, 2, 1], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(5) - }) - - it('returns NotFound when all elements are greater than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, -2)'], - [100, 90, 80, 70, 60], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - }) - - it('returns a lower bound if there is no match in unsorted horizontal range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(3, A2:E2, A3:E3, "NotFound", -1, 1)'], - [2, 1, 4, 2, 5], - [1, 2, 3, 4, 5], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('returns a lower bound if there is no match in unsorted vertical range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(3, A2:A6, B2:B6, "NotFound", -1, 1)'], - [2, 1], - [1, 2], - [4, 3], - [2, 4], - [5, 5] - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - }) - - describe('1 (looking for a upper bound', () => { - describe('in array ordered ascending', () => { - it('returns exact match if exists', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, 2)'], - [1, 2, 42, 50, 51], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(42) - }) - - it('returns an upper bound when there is no exact match', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, 2)'], - [1, 2, 44, 50, 51], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(44) - }) - - it('returns NotFound when all elements are smaller than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, 2)'], - [1, 2, 3, 4, 5], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - - it('returns an upper bound when all elements are greater than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, 2)'], - [43, 44, 45, 46, 47], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(43) - }) - }) - - describe('in array ordered descending', () => { - it('returns exact match if exists', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, -2)'], - [55, 54, 42, 2, 1], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(42) - }) - - it('returns an upper bound when there is no exact match', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, -2)'], - [55, 54, 44, 2, 1], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(44) - }) - - it('returns NotFound when all elements are smaller than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, -2)'], - [5, 4, 3, 2, 1], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - - it('returns an upper bound when all elements are greater than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, -2)'], - [100, 90, 80, 70, 60], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(60) - }) - }) - - it('returns an upper bound if there is no match in unsorted horizontal range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(3, A2:E2, A3:E3, "NotFound", 1, 1)'], - [2, 1, 4, 2, 5], - [1, 2, 3, 4, 5], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('returns an upper bound if there is no match in unsorted vertical range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(3, A2:A6, B2:B6, "NotFound", 1, 1)'], - [2, 1], - [1, 2], - [4, 3], - [2, 4], - [5, 5] - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - }) - - describe('2 (wildcard match)', () => { - describe('for a horizontal range', () => { - it('when searchMode = 1, returns the first matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:E2, A2:E2, "NotFound", 2, 1)'], - ['a', 'axxb', 'a1b111', 'a2b222', 'x'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a1b111') - }) - - it('when searchMode = 2, returns the first matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:E2, A2:E2, "NotFound", 2, 2)'], - ['a', 'axxb', 'a1b111', 'a2b222', 'x'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a1b111') - }) - - it('when searchMode = -2, returns the first matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:E2, A2:E2, "NotFound", 2, -2)'], - ['a', 'axxb', 'a1b111', 'a2b222', 'x'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a1b111') - }) - - it('when searchMode = -1, returns the last matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:E2, A2:E2, "NotFound", 2, -1)'], - ['a', 'axxb', 'a1b111', 'a2b222', 'x'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a2b222') - }) - - it('when there are no matching items, returns NotFound (all searchModes)', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("t?b*", A5:E5, A5:E5, "NotFound", 2, 1)'], - ['=XLOOKUP("t?b*", A5:E5, A5:E5, "NotFound", 2, -1)'], - ['=XLOOKUP("t?b*", A5:E5, A5:E5, "NotFound", 2, 2)'], - ['=XLOOKUP("t?b*", A5:E5, A5:E5, "NotFound", 2, -2)'], - ['a', 'axxb', 'a1b111', 'a2b222', 'x'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - expect(engine.getCellValue(adr('A2'))).toEqual('NotFound') - expect(engine.getCellValue(adr('A3'))).toEqual('NotFound') - expect(engine.getCellValue(adr('A4'))).toEqual('NotFound') - }) - }) - - describe('for a vertical range', () => { - it('when searchMode = 1, returns the first matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:A6, A2:A6, "NotFound", 2, 1)'], - ['a'], - ['axxb'], - ['a1b111'], - ['a2b222'], - ['x'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a1b111') - }) - - it('when searchMode = 2, returns the first matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:A6, A2:A6, "NotFound", 2, 2)'], - ['a'], - ['axxb'], - ['a1b111'], - ['a2b222'], - ['x'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a1b111') - }) - - it('when searchMode = -2, returns the first matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:A6, A2:A6, "NotFound", 2, -2)'], - ['a'], - ['axxb'], - ['a1b111'], - ['a2b222'], - ['x'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a1b111') - }) - - it('when searchMode = -1, returns the last matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:A6, A2:A6, "NotFound", 2, -1)'], - ['a'], - ['axxb'], - ['a1b111'], - ['a2b222'], - ['x'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a2b222') - }) - - it('when there are no matching items, returns NotFound (all searchModes)', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("t?b*", A5:A9, A5:A9, "NotFound", 2, 1)'], - ['=XLOOKUP("t?b*", A5:A9, A5:A9, "NotFound", 2, -1)'], - ['=XLOOKUP("t?b*", A5:A9, A5:A9, "NotFound", 2, 2)'], - ['=XLOOKUP("t?b*", A5:A9, A5:A9, "NotFound", 2, -2)'], - ['a'], - ['axxb'], - ['a1b111'], - ['a2b222'], - ['x'], - ], { useColumnIndex: false }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - expect(engine.getCellValue(adr('A2'))).toEqual('NotFound') - expect(engine.getCellValue(adr('A3'))).toEqual('NotFound') - expect(engine.getCellValue(adr('A4'))).toEqual('NotFound') - }) - }) - }) - }) - - describe('with ColumnIndex column search strategy, when provided with matchMode =', () => { - describe('-1 (looking for a lower bound', () => { - describe('in array ordered ascending', () => { - it('returns exact match if exists', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, 2)'], - [1, 2, 42, 50, 51], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(42) - }) - - it('returns a lower bound when there is no exact match', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, 2)'], - [1, 2, 40, 50, 51], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(40) - }) - - it('returns a lower bound when all elements are smaller than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, 2)'], - [1, 2, 3, 4, 5], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(5) - }) - - it('returns NotFound when all elements are greater than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, 2)'], - [43, 44, 45, 46, 47], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - }) - - describe('in array ordered descending', () => { - it('returns exact match if exists', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, -2)'], - [55, 54, 42, 2, 1], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(42) - }) - - it('returns a lower bound when there is no exact match', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, -2)'], - [55, 54, 40, 2, 1], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(40) - }) - - it('returns a lower bound when all elements are smaller than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, -2)'], - [5, 4, 3, 2, 1], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(5) - }) - - it('returns NotFound when all elements are greater than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", -1, -2)'], - [100, 90, 80, 70, 60], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - }) - - it('returns a lower bound if there is no match in unsorted horizontal range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(3, A2:E2, A3:E3, "NotFound", -1, 1)'], - [2, 1, 4, 2, 5], - [1, 2, 3, 4, 5], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - - it('returns a lower bound if there is no match in unsorted vertical range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(3, A2:A6, B2:B6, "NotFound", -1, 1)'], - [2, 1], - [1, 2], - [4, 3], - [2, 4], - [5, 5] - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - }) - - describe('1 (looking for a upper bound', () => { - describe('in array ordered ascending', () => { - it('returns exact match if exists', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, 2)'], - [1, 2, 42, 50, 51], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(42) - }) - - it('returns an upper bound when there is no exact match', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, 2)'], - [1, 2, 44, 50, 51], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(44) - }) - - it('returns NotFound when all elements are smaller than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, 2)'], - [1, 2, 3, 4, 5], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - - it('returns an upper bound when all elements are greater than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, 2)'], - [43, 44, 45, 46, 47], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(43) - }) - }) - - describe('in array ordered descending', () => { - it('returns exact match if exists', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, -2)'], - [55, 54, 42, 2, 1], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(42) - }) - - it('returns an upper bound when there is no exact match', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, -2)'], - [55, 54, 44, 2, 1], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(44) - }) - - it('returns NotFound when all elements are smaller than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, -2)'], - [5, 4, 3, 2, 1], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - }) - - it('returns an upper bound when all elements are greater than the key', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(42, A2:E2, A2:E2, "NotFound", 1, -2)'], - [100, 90, 80, 70, 60], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(60) - }) - }) - - it('returns an upper bound if there is no match in unsorted horizontal range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(3, A2:E2, A3:E3, "NotFound", 1, 1)'], - [2, 1, 4, 2, 5], - [1, 2, 3, 4, 5], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('returns an upper bound if there is no match in unsorted vertical range', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP(3, A2:A6, B2:B6, "NotFound", 1, 1)'], - [2, 1], - [1, 2], - [4, 3], - [2, 4], - [5, 5] - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - }) - - describe('2 (wildcard match)', () => { - describe('for a horizontal range', () => { - it('when searchMode = 1, returns the first matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:E2, A2:E2, "NotFound", 2, 1)'], - ['a', 'axxb', 'a1b111', 'a2b222', 'x'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a1b111') - }) - - it('when searchMode = 2, returns the first matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:E2, A2:E2, "NotFound", 2, 2)'], - ['a', 'axxb', 'a1b111', 'a2b222', 'x'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a1b111') - }) - - it('when searchMode = -2, returns the first matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:E2, A2:E2, "NotFound", 2, -2)'], - ['a', 'axxb', 'a1b111', 'a2b222', 'x'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a1b111') - }) - - it('when searchMode = -1, returns the last matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:E2, A2:E2, "NotFound", 2, -1)'], - ['a', 'axxb', 'a1b111', 'a2b222', 'x'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a2b222') - }) - - it('when there are no matching items, returns NotFound (all searchModes)', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("t?b*", A5:E5, A5:E5, "NotFound", 2, 1)'], - ['=XLOOKUP("t?b*", A5:E5, A5:E5, "NotFound", 2, -1)'], - ['=XLOOKUP("t?b*", A5:E5, A5:E5, "NotFound", 2, 2)'], - ['=XLOOKUP("t?b*", A5:E5, A5:E5, "NotFound", 2, -2)'], - ['a', 'axxb', 'a1b111', 'a2b222', 'x'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - expect(engine.getCellValue(adr('A2'))).toEqual('NotFound') - expect(engine.getCellValue(adr('A3'))).toEqual('NotFound') - expect(engine.getCellValue(adr('A4'))).toEqual('NotFound') - }) - }) - - describe('for a vertical range', () => { - it('when searchMode = 1, returns the first matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:A6, A2:A6, "NotFound", 2, 1)'], - ['a'], - ['axxb'], - ['a1b111'], - ['a2b222'], - ['x'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a1b111') - }) - - it('when searchMode = 2, returns the first matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:A6, A2:A6, "NotFound", 2, 2)'], - ['a'], - ['axxb'], - ['a1b111'], - ['a2b222'], - ['x'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a1b111') - }) - - it('when searchMode = -2, returns the first matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:A6, A2:A6, "NotFound", 2, -2)'], - ['a'], - ['axxb'], - ['a1b111'], - ['a2b222'], - ['x'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a1b111') - }) - - it('when searchMode = -1, returns the last matching item', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("a?b*", A2:A6, A2:A6, "NotFound", 2, -1)'], - ['a'], - ['axxb'], - ['a1b111'], - ['a2b222'], - ['x'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('a2b222') - }) - - it('when there are no matching items, returns NotFound (all searchModes)', () => { - const engine = HyperFormula.buildFromArray([ - ['=XLOOKUP("t?b*", A5:A9, A5:A9, "NotFound", 2, 1)'], - ['=XLOOKUP("t?b*", A5:A9, A5:A9, "NotFound", 2, -1)'], - ['=XLOOKUP("t?b*", A5:A9, A5:A9, "NotFound", 2, 2)'], - ['=XLOOKUP("t?b*", A5:A9, A5:A9, "NotFound", 2, -2)'], - ['a'], - ['axxb'], - ['a1b111'], - ['a2b222'], - ['x'], - ], { useColumnIndex: true }) - - expect(engine.getCellValue(adr('A1'))).toEqual('NotFound') - expect(engine.getCellValue(adr('A2'))).toEqual('NotFound') - expect(engine.getCellValue(adr('A3'))).toEqual('NotFound') - expect(engine.getCellValue(adr('A4'))).toEqual('NotFound') - }) - }) - }) - }) - - describe('acts similar to Microsoft Excel', () => { - /** - * Examples from - * https://support.microsoft.com/en-us/office/xlookup-function-b7fd680e-6d10-43e6-84f9-88eae8bf5929 - */ - - it('should find value in simple column range (official example 1)', () => { - const engine = HyperFormula.buildFromArray([ - ['China', 'CN'], - ['India', 'IN'], - ['United States', 'US'], - ['Indonesia', 'ID'], - ['France', 'FR'], - ['=XLOOKUP("Indonesia", A1:A5, B1:B5)'], - ]) - - expect(engine.getCellValue(adr('A6'))).toEqual('ID') - }) - - it('should find row range in table (official example 2)', () => { - const engine = HyperFormula.buildFromArray([ - ['8389', 'Dianne Pugh', 'Finance'], - ['4390', 'Ned Lanning', 'Marketing'], - ['8604', 'Margo Hendrix', 'Sales'], - ['8389', 'Dianne Pugh', 'Finance'], - ['4937', 'Earlene McCarty', 'Accounting'], - ['=XLOOKUP(A1, A2:A5, B2:C5)'], - ]) - - expect(engine.getRangeValues(AbsoluteCellRange.spanFrom(adr('A6'), 2, 1))).toEqual([['Dianne Pugh', 'Finance']]) - }) - - it('should find column range in table (official example 2, transposed)', () => { - const engine = HyperFormula.buildFromArray([ - ['8389', '4390', '8604', '8389', '4937'], - ['Dianne Pugh', 'Ned Lanning', 'Margo Hendrix', 'Dianne Pugh', 'Earlene McCarty'], - ['Finance', 'Marketing', 'Sales', 'Finance', 'Accounting'], - ['=XLOOKUP(A1, B1:E1, B2:E3)'], - [] - ]) - - expect(engine.getRangeValues(AbsoluteCellRange.spanFrom(adr('A4'), 1, 2))).toEqual([['Dianne Pugh'], ['Finance']]) - }) - - it('should find use if_not_found argument if not found (official example 3)', () => { - const engine = HyperFormula.buildFromArray([ - ['1234', 'Dianne Pugh', 'Finance'], - ['4390', 'Ned Lanning', 'Marketing'], - ['8604', 'Margo Hendrix', 'Sales'], - ['8389', 'Dianne Pugh', 'Finance'], - ['4937', 'Earlene McCarty', 'Accounting'], - ['=XLOOKUP(A1, A2:A5, B2:B5, "ID not found")'], - ]) - - expect(engine.getCellValue(adr('A6'))).toEqual('ID not found') - }) - - it('example 4', () => { - const engine = HyperFormula.buildFromArray([ - ['10', 'a'], - ['20', 'b'], - ['30', 'c'], - ['40', 'd'], - ['50', 'e'], - ['=XLOOKUP(25, A1:A5, B1:B5, 0, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A6'))).toEqual('c') - }) - - it('nested xlookup function to perform both a vertical and horizontal match (official example 5)', () => { - const engine = HyperFormula.buildFromArray([ - ['Quarter', 'Gross profit', 'Net profit', 'Profit %'], - ['Qtr1', '=XLOOKUP(B1, $A4:$A12, XLOOKUP($A2, $B3:$F3, $B4:$F12))', '19342', '29.3'], - ['Income statement', 'Qtr1', 'Qtr2', 'Qtr3', 'Qtr4', 'Total'], - ['Total sales', '50000', '78200', '89500', '91200', '308950'], - ['Cost of sales', '25000', '42050', '59450', '60450', '186950'], - ['Gross profit', '25000', '36150', '30050', '30800', '122000'], - ['Depreciation', '899', '791', '202', '412', '2304'], - ['Interest', '513', '853', '150', '956', '2472'], - ['Earnings before tax', '23588', '34506', '29698', '29432', '117224'], - ['Tax', '4246', '6211', '5346', '5298', '21100'], - ['Net profit', '19342', '28295', '24352', '24134', '96124'], - ['Profit %', '29.3', '27.8', '23.4', '27.6', '26.9'], - ]) - - expect(engine.getCellValue(adr('B2'))).toEqual(25000) - }) - }) -}) diff --git a/test/unit/interpreter/function-xnpv.spec.ts b/test/unit/interpreter/function-xnpv.spec.ts deleted file mode 100644 index 4f04753350..0000000000 --- a/test/unit/interpreter/function-xnpv.spec.ts +++ /dev/null @@ -1,94 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function XNPV', () => { - it('should return #NA! error with the wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=XNPV(1,1)', '=XNPV(1, 1, 1, 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - /** - * Product #2 implements only Rate>0 (even though states in the documentation that Rate>-1). - */ - it('should accept Rate values that are greater than -1.', () => { - const engine = HyperFormula.buildFromArray([ - ['=XNPV(-1, A2:D2, A3:D3)', '=XNPV(2, A2:D2, A3:D3)', '=XNPV(-0.9, A2:D2, A3:D3)'], - [1, 2, 3, 4], - [1, 2, 3, 4], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - expect(engine.getCellValue(adr('B1'))).toBeCloseTo(9.94002794561453, 6) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(10.1271695921145, 6) - }) - - it('should calculate the correct value', () => { - const engine = HyperFormula.buildFromArray([ - ['=XNPV(2%, 1, 1)'], - ['=XNPV(1, B2:C2, D2:E2)', 1, 2, 3, 4], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(2.99620553730319, 6) - }) - - it('should round dates', () => { - const engine = HyperFormula.buildFromArray([ - ['=XNPV(1, B1:C1, D1:E1)', 1, 2, 3.1, 4], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(2.99620553730319, 6) - }) - - it('only first date needs to be earliest', () => { - const engine = HyperFormula.buildFromArray([ - ['=XNPV(1, B1:E1, F1:I1)', 1, 2, 3, 4, 1, 4, 3, 2], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(9.9696766801485, 6) - }) - - it('should evaluate to #NUM! if values in range are not numbers', () => { - const engine = HyperFormula.buildFromArray([ - ['=XNPV(1, B1:C1, D1:E1)', 1, null, 3, null], - ['=XNPV(1, B2:C2, D2:E2)', 1, 2, 3.1, true], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberExpected)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberExpected)) - }) - - /** - * Product #1 tries to match the values in ranges. - */ - it('should evaluate to #NUM! if ranges are of different length', () => { - const engine = HyperFormula.buildFromArray([ - ['=XNPV(1, B1:C1, D1:F1)', 1, 2, 3, 4, 5], - ['=XNPV(1, B2:D2, E2:F2)', 1, 2, 3, 4, 5], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.EqualLength)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.EqualLength)) - }) - - it('should evaluate to #NUM! if dates are in wrong order', () => { - const engine = HyperFormula.buildFromArray([ - ['=XNPV(1, B1:C1, D1:E1)', 1, 2, 4, 3], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('should evaluate to #NUM! if dates are too small', () => { - const engine = HyperFormula.buildFromArray([ - ['=XNPV(1, B1:C1, D1:E1)', 1, 2, -1, 4], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) -}) diff --git a/test/unit/interpreter/function-xor.spec.ts b/test/unit/interpreter/function-xor.spec.ts deleted file mode 100644 index 6b9b325f3a..0000000000 --- a/test/unit/interpreter/function-xor.spec.ts +++ /dev/null @@ -1,123 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function XOR', () => { - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['=XOR(TRUE(), TRUE())'], - ['=XOR(TRUE(), FALSE())'], - ['=XOR(FALSE(), TRUE())'], - ['=XOR(FALSE(), FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('A2'))).toBe(true) - expect(engine.getCellValue(adr('A3'))).toBe(true) - expect(engine.getCellValue(adr('A4'))).toBe(false) - }) - - it('at least one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=XOR()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('for one argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=XOR(TRUE())'], - ['=XOR(FALSE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('A2'))).toBe(false) - }) - - it('use coercion #1', () => { - const engine = HyperFormula.buildFromArray([ - ['=XOR("TRUE")'], - ['=XOR(1)'], - ['=XOR(1, "foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('A2'))).toBe(true) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('use coercion #2', () => { - const engine = HyperFormula.buildFromArray([ - ['=XOR(A4:B4)'], - ['=XOR(C4:D4)'], - ['=XOR(C4:D4, "foo")'], - ['TRUE', 1, 'foo', '=TRUE()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('A2'))).toBe(true) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('when no coercible to number arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=XOR("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.WrongType)) - }) - - it('returns TRUE iff odd number of TRUEs present', () => { - const engine = HyperFormula.buildFromArray([ - ['=XOR(TRUE(), TRUE(), TRUE())'], - ['=XOR(TRUE(), TRUE(), TRUE(), TRUE())'], - ['=XOR(TRUE(), TRUE(), TRUE(), TRUE(), TRUE())'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('A2'))).toBe(false) - expect(engine.getCellValue(adr('A3'))).toBe(true) - }) - - it('if error in range found, returns first one in row-by-row order', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '=4/0'], - ['=FOOBAR()', '1'], - ['=XOR(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('works with ranges', () => { - const engine = HyperFormula.buildFromArray([ - ['0', '0'], - ['0', '1'], - ['=XOR(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(true) - }) - - it('is computed eagerly', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=4/0'], - ['0', '1'], - ['=XOR(A1:B2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('when called with a range, ignores strings other than "true", "false" and ""', () => { - const engine = HyperFormula.buildFromArray([ - ['foo', '=TRUE()', '=XOR(A1:B1)'], - ['foo', '=FALSE()', '=XOR(A2:B2)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(true) - expect(engine.getCellValue(adr('C2'))).toEqual(false) - }) -}) diff --git a/test/unit/interpreter/function-year.spec.ts b/test/unit/interpreter/function-year.spec.ts deleted file mode 100644 index 538485ec15..0000000000 --- a/test/unit/interpreter/function-year.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function YEAR', () => { - it('validate arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=YEAR(1, 2)'], - ['=YEAR()'], - ['=YEAR("foo")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('with numerical arguments', () => { - const engine = HyperFormula.buildFromArray([['=YEAR(0)', '=YEAR(2)', '=YEAR(43465)']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1899) - expect(engine.getCellValue(adr('B1'))).toEqual(1900) - expect(engine.getCellValue(adr('C1'))).toEqual(2018) - }) - - it('with string arguments', () => { - const engine = HyperFormula.buildFromArray([['=YEAR("31/12/1899")', '=YEAR("01/01/1900")', '=YEAR("31/12/2018")']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1899) - expect(engine.getCellValue(adr('B1'))).toEqual(1900) - expect(engine.getCellValue(adr('C1'))).toEqual(2018) - }) - - it('use datenumber coercion for 1st argument', () => { - const engine = HyperFormula.buildFromArray([ - ['=YEAR(TRUE())'], - ['=YEAR(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1899) - expect(engine.getCellValue(adr('A2'))).toEqual(1899) - }) - - it('propagate errors', () => { - const engine = HyperFormula.buildFromArray([ - ['=YEAR(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/function-yearfrac.spec.ts b/test/unit/interpreter/function-yearfrac.spec.ts deleted file mode 100644 index a8090ff043..0000000000 --- a/test/unit/interpreter/function-yearfrac.spec.ts +++ /dev/null @@ -1,132 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Function YEARFRAC', () => { - it('should not work for wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=YEARFRAC(1, 2, 3, 4)'], - ['=YEARFRAC(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('should not work for wrong type of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=YEARFRAC("foo", 1, TRUE())'], - ['=YEARFRAC(2, "bar")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('US mode', () => { - const engine = HyperFormula.buildFromArray([ - ['=YEARFRAC("30/03/2020", "31/03/2020")'], - ['=YEARFRAC("28/02/2020", "29/02/2020")'], - ['=YEARFRAC("29/02/2020", "01/03/2020")'], - ['=YEARFRAC("28/02/2021", "01/03/2021")'], - ['=YEARFRAC("31/03/2020", "30/03/2020")'], - ['=YEARFRAC("29/02/2020", "28/02/2020")'], - ['=YEARFRAC("01/03/2020", "29/02/2020")'], - ['=YEARFRAC("01/03/2021", "28/02/2021")'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(1 / 360, 9) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(1 / 360, 9) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(1 / 360, 9) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(1 / 360, 9) - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(1 / 360, 9) - expect(engine.getCellValue(adr('A8'))).toBeCloseTo(1 / 360, 9) - }) - - it('actual/actual mode', () => { - const engine = HyperFormula.buildFromArray([ - ['=YEARFRAC("01/01/2020", "02/01/2020", 1)'], - ['=YEARFRAC("01/01/2021", "02/01/2021", 1)'], - ['=YEARFRAC("28/02/2020", "01/03/2020", 1)'], - ['=YEARFRAC("28/02/2021", "01/03/2021", 1)'], - ['=YEARFRAC("31/12/2019", "01/03/2020", 1)'], - ['=YEARFRAC("31/12/2019", "29/02/2020", 1)'], - ['=YEARFRAC("31/12/2019", "28/02/2020", 1)'], - ['=YEARFRAC("01/01/2020", "01/01/2021", 1)'], - ['=YEARFRAC("01/01/2020", "02/01/2021", 1)'], - ['=YEARFRAC("01/01/2020", "01/01/2024", 1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1 / 366, 9) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(1 / 365, 9) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(2 / 366, 9) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(1 / 365, 9) - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(61 / 366, 9) - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(60 / 366, 9) - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(59 / 365, 9) - expect(engine.getCellValue(adr('A8'))).toBeCloseTo(1, 9) - expect(engine.getCellValue(adr('A9'))).toBeCloseTo(367 / 365.5, 9) - expect(engine.getCellValue(adr('A10'))).toBeCloseTo((366 + 365 + 365 + 365) / ((366 + 365 + 365 + 365 + 366) / 5), 9) - }) - - it('actual/360 mode', () => { - const engine = HyperFormula.buildFromArray([ - ['=YEARFRAC("30/03/2020", "31/03/2020", 2)'], - ['=YEARFRAC("28/02/2020", "29/02/2020", 2)'], - ['=YEARFRAC("29/02/2020", "01/03/2020", 2)'], - ['=YEARFRAC("28/02/2021", "01/03/2021", 2)'], - ['=YEARFRAC("31/03/2020", "30/03/2021", 2)'], - ['=YEARFRAC("01/03/2021", "28/02/2020", 2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1 / 360, 9) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(1 / 360, 9) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(1 / 360, 9) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(1 / 360, 9) - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(364 / 360, 9) - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(367 / 360, 9) - }) - - it('actual/365 mode', () => { - const engine = HyperFormula.buildFromArray([ - ['=YEARFRAC("30/03/2020", "31/03/2020", 3)'], - ['=YEARFRAC("28/02/2020", "29/02/2020", 3)'], - ['=YEARFRAC("29/02/2020", "01/03/2020", 3)'], - ['=YEARFRAC("28/02/2021", "01/03/2021", 3)'], - ['=YEARFRAC("31/03/2020", "30/03/2021", 3)'], - ['=YEARFRAC("01/03/2021", "28/02/2020", 3)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1 / 365, 9) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(1 / 365, 9) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(1 / 365, 9) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(1 / 365, 9) - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(364 / 365, 9) - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(367 / 365, 9) - }) - - it('EU mode', () => { - const engine = HyperFormula.buildFromArray([ - ['=YEARFRAC("30/03/2020", "31/03/2020", 4)'], - ['=YEARFRAC("28/02/2020", "29/02/2020", 4)'], - ['=YEARFRAC("29/02/2020", "01/03/2020", 4)'], - ['=YEARFRAC("28/02/2021", "01/03/2021", 4)'], - ['=YEARFRAC("31/03/2020", "30/03/2020", 4)'], - ['=YEARFRAC("29/02/2020", "28/02/2020", 4)'], - ['=YEARFRAC("01/03/2020", "29/02/2020", 4)'], - ['=YEARFRAC("01/03/2021", "28/02/2021", 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(1 / 360, 9) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(2 / 360, 9) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(3 / 360, 9) - expect(engine.getCellValue(adr('A5'))).toEqual(0) - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(1 / 360, 9) - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(2 / 360, 9) - expect(engine.getCellValue(adr('A8'))).toBeCloseTo(3 / 360, 9) - }) -}) diff --git a/test/unit/interpreter/function-z.test.spec.ts b/test/unit/interpreter/function-z.test.spec.ts deleted file mode 100644 index 0ad16a48ea..0000000000 --- a/test/unit/interpreter/function-z.test.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Z.TEST', () => { - it('validates number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=Z.TEST(1)'], - ['=Z.TEST(1, 2, 3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('works (no sigma)', () => { - const engine = HyperFormula.buildFromArray([ - ['=Z.TEST(A2:D2, 1)'], - [1, 2, 3, 4] - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0100683757751732, 6) - }) - - it('works (with sigma)', () => { - const engine = HyperFormula.buildFromArray([ - ['=Z.TEST(A2:D2, 1, 1)'], - [1, 2, 3, 4] - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0013498980316301, 6) - }) - - it('validates input', () => { - const engine = HyperFormula.buildFromArray([ - ['=Z.TEST(B1:C1, 1)', 1, null], - ['=Z.TEST(B2:C2, 1, 1)', null, null], - ['=Z.TEST(B3:C3, 1)', 1, 1], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO, ErrorMessage.TwoValues)) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.OneValue)) - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('doesnt do coercions, nonnumeric values are skipped', () => { - const engine = HyperFormula.buildFromArray([ - ['=Z.TEST(B1:E1, 1, 1)', null, 2, 3, 4], - ['=Z.TEST(B2:E2, 1, 1)', true, 2, 3, 4], - ]) - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.000266002752569605, 6) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.000266002752569605, 6) - }) - - it('propagates errors', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '10'], - ['=NA()', '50'], - ['3', '30'], - ['=Z.TEST(A1:B3, 1, 1)'], - ]) - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.NA)) - }) -}) diff --git a/test/unit/interpreter/matrix-plugin.spec.ts b/test/unit/interpreter/matrix-plugin.spec.ts deleted file mode 100644 index caf35103fd..0000000000 --- a/test/unit/interpreter/matrix-plugin.spec.ts +++ /dev/null @@ -1,301 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {ErrorMessage} from '../../../src/error-message' -import {MatrixPlugin} from '../../../src/interpreter/plugin/MatrixPlugin' -import {adr, detailedError, detailedErrorWithOrigin} from '../testUtils' - -describe('Matrix plugin', () => { - beforeAll(() => { - HyperFormula.registerFunctionPlugin(MatrixPlugin) - }) - - it('matrix multiplication', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ['1', '2'], - ['3', '4'], - ['=MMULT(A1:B3,A4:B5)'], - ]) - - - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(7) - expect(engine.getCellValue(adr('B6'))).toBeCloseTo(10) - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(15) - expect(engine.getCellValue(adr('B7'))).toBeCloseTo(22) - expect(engine.getCellValue(adr('A8'))).toBeCloseTo(23) - expect(engine.getCellValue(adr('B8'))).toBeCloseTo(34) - }) - - it('matrix multiplication wrong size', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ['1', '2', '3'], - ['4', '5', '6'], - ['7', '8', '9'], - ['=mmult(A1:B3,A4:C6)'], - ]) - - expect(engine.getCellValue(adr('A7'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ArrayDimensions)) - expect(engine.getCellValue(adr('B7'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ArrayDimensions)) - }) - - it('matrix multiplication with string in data', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=MMULT(A1:B2,A3:B4)'], - ['3', 'foo'], - ['1', '2', '=MMULT(A3:B4,A1:B2)'], - ['3', '4'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberRange)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberRange)) - expect(engine.getCellValue(adr('C2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberRange)) - expect(engine.getCellValue(adr('D2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberRange)) - expect(engine.getCellValue(adr('C3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberRange)) - expect(engine.getCellValue(adr('D4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberRange)) - expect(engine.getCellValue(adr('C3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberRange)) - expect(engine.getCellValue(adr('D4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberRange)) - }) - - it('nested matrix multiplication', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['=MMULT(A1:B2, MMULT(A1:B2,A1:B2))'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(37) - expect(engine.getCellValue(adr('B3'))).toEqual(54) - expect(engine.getCellValue(adr('A4'))).toEqual(81) - expect(engine.getCellValue(adr('B4'))).toEqual(118) - }) - - it('mmult of other mmult', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=MMULT(A1:B2, A1:B2)'], - ['3', '4'], - ['=MMULT(A1:B2, C1:D2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(37) - expect(engine.getCellValue(adr('B3'))).toEqual(54) - expect(engine.getCellValue(adr('A4'))).toEqual(81) - expect(engine.getCellValue(adr('B4'))).toEqual(118) - }) - - it('mmult of a number', () => { - const engine = HyperFormula.buildFromArray([ - ['=MMULT(3, 4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(12) - }) - - it('mmult wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=MMULT(0)', '=MMULT(0,0,0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('matrix multiplication by sumproduct', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ['1', '2'], - ['3', '4'], - ['=SUMPRODUCT($A1:$B1,transpose(A$4:A$5))', '=SUMPRODUCT($A1:$B1,transpose(B$4:B$5))'], - ['=SUMPRODUCT($A2:$B2,transpose(A$4:A$5))', '=SUMPRODUCT($A2:$B2,transpose(B$4:B$5))'], - ['=SUMPRODUCT($A3:$B3,transpose(A$4:A$5))', '=SUMPRODUCT($A3:$B3,transpose(B$4:B$5))'], - ]) - - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(7) - expect(engine.getCellValue(adr('B6'))).toBeCloseTo(10) - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(15) - expect(engine.getCellValue(adr('B7'))).toBeCloseTo(22) - expect(engine.getCellValue(adr('A8'))).toBeCloseTo(23) - expect(engine.getCellValue(adr('B8'))).toBeCloseTo(34) - }) - - it('matrix maxpool', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '4', '5', '6'], - ['11', '12', '13', '14', '15', '16'], - ['21', '22', '23', '24', '25', '26'], - ['=maxpool(A1:F3,3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(23) - expect(engine.getCellValue(adr('B4'))).toBeCloseTo(26) - }) - - it('matrix maxpool non-numeric values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', 'bar', '3', '4', '5', '6'], - ['11', '12', '13', 'foo', '15', '16'], - ['21', '22', '23', '24', '25', '26'], - ['=maxpool(A1:F3,3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberRange)) - }) - - it('matrix maxpool, custom stride', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '4', '5', '6'], - ['11', '12', '13', '14', '15', '16'], - ['21', '22', '23', '24', '25', '26'], - ['28', '29', '30', '31', '32', '33'], - ['=maxpool(A1:F4,3,1)'], - ]) - - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(23) - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(30) - expect(engine.getCellValue(adr('B5'))).toBeCloseTo(24) - expect(engine.getCellValue(adr('B6'))).toBeCloseTo(31) - expect(engine.getCellValue(adr('C5'))).toBeCloseTo(25) - expect(engine.getCellValue(adr('C6'))).toBeCloseTo(32) - expect(engine.getCellValue(adr('D5'))).toBeCloseTo(26) - expect(engine.getCellValue(adr('D6'))).toBeCloseTo(33) - }) - - it('maxpool wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=MAXPOOL(0)', '=MAXPOOL(0, 0,0,0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('matrix medianpool on even square', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '1', '2', '1', '5'], - ['3', '4', '3', '7', '6', '7'], - ['=medianpool(A1:F2,2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(2.5) - expect(engine.getCellValue(adr('B3'))).toBeCloseTo(2.5) - expect(engine.getCellValue(adr('C3'))).toBeCloseTo(5.5) - }) - - it('medianpool wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=MEDIANPOOL(0)', '=MEDIANPOOL(0,0,0,0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('medianpool non-numeric values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', 'foo', '2', '1', '5'], - ['3', 'bar', '3', '7', '6', '7'], - ['=medianpool(A1:F2,2)'], - ]) - - expect(engine.getCellValue(adr('A3'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberRange)) - }) - - it('matrix medianpool on odd square', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1', '1'], // right shot from the beginning - ['1', '2', '3'], - ['3', '3', '3'], - - ['2', '2', '2'], // need one step to the left - ['3', '4', '6'], - ['10', '10', '10'], - - ['0', '0', '0'], // need one step to the right - ['4', '6', '7'], - ['8', '8', '8'], - - ['=medianpool(A1:C9,3)'], - ]) - - expect(engine.getCellValue(adr('A10'))).toBeCloseTo(2) - expect(engine.getCellValue(adr('A11'))).toBeCloseTo(4) - expect(engine.getCellValue(adr('A12'))).toBeCloseTo(6) - }) -}) - -describe('Function TRANSPOSE', () => { - it('transpose works', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ['=TRANSPOSE(A1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(1) - expect(engine.getCellValue(adr('B4'))).toBeCloseTo(3) - expect(engine.getCellValue(adr('C4'))).toBeCloseTo(5) - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(2) - expect(engine.getCellValue(adr('B5'))).toBeCloseTo(4) - expect(engine.getCellValue(adr('C5'))).toBeCloseTo(6) - }) - - it('transpose works for scalar', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRANSPOSE(1)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(1) - }) - - it('transpose returns error if argument evaluates to error', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRANSPOSE(4/0)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('transpose wrong number of arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRANSPOSE()', '=TRANSPOSE(C1:C2, D1:D2)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.NA, ErrorMessage.WrongArgNumber)) - }) - - it('transpose without braces', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ['5', '6'], - ['=TRANSPOSE(A1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toBe(1) - expect(engine.getCellValue(adr('A5'))).toBe(2) - expect(engine.getCellValue(adr('B4'))).toBe(3) - }) - - it('transpose any values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['foo', 'bar'], - ['=1/0', '=TRUE()'], - ['=TRANSPOSE(A1:B3)'], - ]) - - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(1) - expect(engine.getCellValue(adr('B4'))).toEqual('foo') - expect(engine.getCellValue(adr('C4'))).toEqual(detailedErrorWithOrigin(ErrorType.DIV_BY_ZERO, 'Sheet1!A3')) - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(2) - expect(engine.getCellValue(adr('B5'))).toEqual('bar') - expect(engine.getCellValue(adr('C5'))).toEqual(true) - }) -}) diff --git a/test/unit/interpreter/nullvalue.spec.ts b/test/unit/interpreter/nullvalue.spec.ts deleted file mode 100644 index 31126052c6..0000000000 --- a/test/unit/interpreter/nullvalue.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {adr, detailedError} from '../testUtils' - -describe('EmptyValue tests', () => { - it('EmptyValue vs EmptyValue tests', () => { - const engine = HyperFormula.buildFromArray( - [ - [null, null, '=A1=B1', '=A1>B1', '=A1=B1', '=A1<=B1', '=A1<>B1', '=A1+B1', '=A1-B1', '=A1*B1', '=A1/B1', '=A1^B1', '=A1&B1', '=+A1', '=-A1', '=A1%'] - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(true) // EQUAL - expect(engine.getCellValue(adr('D1'))).toEqual(false) // GT - expect(engine.getCellValue(adr('E1'))).toEqual(false) // LT - expect(engine.getCellValue(adr('F1'))).toEqual(true) // GTE - expect(engine.getCellValue(adr('G1'))).toEqual(true) // LTE - expect(engine.getCellValue(adr('H1'))).toEqual(false) // NOT EQUAL - expect(engine.getCellValue(adr('I1'))).toEqual(0) // ADD - expect(engine.getCellValue(adr('J1'))).toEqual(0) // SUB - expect(engine.getCellValue(adr('K1'))).toEqual(0) // MULT - expect(engine.getCellValue(adr('L1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // DIV - expect(engine.getCellValue(adr('M1'))).toEqual(1) // EXP - expect(engine.getCellValue(adr('N1'))).toEqual('') // CONCAT - expect(engine.getCellValue(adr('O1'))).toBe(null) // UNARY PLUS - expect(engine.getCellValue(adr('P1'))).toEqual(0) // UNARY MINUS - expect(engine.getCellValue(adr('Q1'))).toEqual(0) // PERCENTAGE - }) - - it('Boolean vs EmptyValue tests', () => { - const engine = HyperFormula.buildFromArray( - [ - ['=TRUE()', null, '=A1=B1', '=A1>B1', '=A1=B1', '=A1<=B1', '=A1<>B1', '=A1+B1', '=A1-B1', '=A1*B1', '=A1/B1', '=A1^B1', '=A1&B1', '=+A1', '=-A1', '=A1%'] - ]) - expect(engine.getCellValue(adr('C1'))).toEqual(false) // EQUAL - expect(engine.getCellValue(adr('D1'))).toEqual(true) // GT - expect(engine.getCellValue(adr('E1'))).toEqual(false) // LT - expect(engine.getCellValue(adr('F1'))).toEqual(true) // GTE - expect(engine.getCellValue(adr('G1'))).toEqual(false) // LTE - expect(engine.getCellValue(adr('H1'))).toEqual(true) // NOT EQUAL - expect(engine.getCellValue(adr('I1'))).toEqual(1) // ADD - expect(engine.getCellValue(adr('J1'))).toEqual(1) // SUB - expect(engine.getCellValue(adr('K1'))).toEqual(0) // MULT - expect(engine.getCellValue(adr('L1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) // DIV - expect(engine.getCellValue(adr('M1'))).toEqual(1) // EXP - expect(engine.getCellValue(adr('N1'))).toEqual('TRUE') // CONCAT - expect(engine.getCellValue(adr('O1'))).toEqual(true) // UNARY PLUS - expect(engine.getCellValue(adr('P1'))).toEqual(-1) // UNARY MINUS - expect(engine.getCellValue(adr('Q1'))).toEqual(0.01) // PERCENTAGE - }) -}) diff --git a/test/unit/interpreter/number-literals.spec.ts b/test/unit/interpreter/number-literals.spec.ts deleted file mode 100644 index 0dad742e15..0000000000 --- a/test/unit/interpreter/number-literals.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {Config} from '../../../src/Config' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Number literals', () => { - it('should work for integer', () => { - const engine = HyperFormula.buildFromArray([['="1" + 2']]) - expect(engine.getCellValue(adr('A1'))).toEqual(3) - }) - - it('should work for float with standard decimal separator', () => { - const engine = HyperFormula.buildFromArray([['="1.23" + 2']]) - expect(engine.getCellValue(adr('A1'))).toEqual(3.23) - }) - - it('should work for float with custom decimal separator', () => { - const engine = HyperFormula.buildFromArray([['="1,23" + 2']], new Config({ - decimalSeparator: ',', functionArgSeparator: ';' - })) - expect(engine.getCellValue(adr('A1'))).toEqual(3.23) - }) - - it('should work for number with thousand separator', () => { - const engine = HyperFormula.buildFromArray([['="1,000" + 2']], new Config({ - thousandSeparator: ',', functionArgSeparator: ';' - })) - expect(engine.getCellValue(adr('A1'))).toEqual(1002) - }) - - it('should work for number with another thousand separator', () => { - const engine = HyperFormula.buildFromArray([['="1 000" + 2']], new Config({ - thousandSeparator: ' ' - })) - expect(engine.getCellValue(adr('A1'))).toEqual(1002) - }) - - it('should work with thousand and decimal separator', () => { - const engine = HyperFormula.buildFromArray([['="1 000,2" + 2']], new Config({ - decimalSeparator: ',', thousandSeparator: ' ', functionArgSeparator: ';' - })) - expect(engine.getCellValue(adr('A1'))).toEqual(1002.2) - }) - - it('should work for multiple thousand separator in one literal', () => { - const engine = HyperFormula.buildFromArray([['="1 000 000" + 2']], new Config({ - thousandSeparator: ' ' - })) - expect(engine.getCellValue(adr('A1'))).toEqual(1000002) - }) - - it('should return value when thousand separator in literal does not match config', () => { - const engine = HyperFormula.buildFromArray([['="1 000" + 2']], new Config({ - thousandSeparator: ',', functionArgSeparator: ';' - })) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('should work for number with dot as thousand separator', () => { - const engine = HyperFormula.buildFromArray([['="1.000,3" + 2']], new Config({ - thousandSeparator: '.', decimalSeparator: ',', functionArgSeparator: ';' - })) - expect(engine.getCellValue(adr('A1'))).toEqual(1002.3) - }) -}) diff --git a/test/unit/interpreter/operator-concatenate.spec.ts b/test/unit/interpreter/operator-concatenate.spec.ts deleted file mode 100644 index d26e27d26e..0000000000 --- a/test/unit/interpreter/operator-concatenate.spec.ts +++ /dev/null @@ -1,53 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {adr, detailedError} from '../testUtils' - -describe('Interpreter - concatenate operator', () => { - it('Ampersand with string arguments', () => { - const engine = HyperFormula.buildFromArray([ - ['="foo"&"bar"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe('foobar') - }) - - it('Ampersand with cell address', () => { - const engine = HyperFormula.buildFromArray([ - ['foo', '=A1&"bar"'], - ]) - - expect(engine.getCellValue(adr('B1'))).toBe('foobar') - }) - - it('Ampersand with number', () => { - const engine = HyperFormula.buildFromArray([ - ['=1&2'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe('12') - }) - - it('Ampersand with bool', () => { - const engine = HyperFormula.buildFromArray([ - ['="foo"&TRUE()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe('fooTRUE') - }) - - it('Ampersand with null', () => { - const engine = HyperFormula.buildFromArray([ - ['="foo"&B1'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe('foo') - }) - - it('Ampersand with error', () => { - const engine = HyperFormula.buildFromArray([ - ['=1/0', '=A1&TRUE()'], - ]) - - expect(engine.getCellValue(adr('B1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/operator-division.spec.ts b/test/unit/interpreter/operator-division.spec.ts deleted file mode 100644 index 5efcf9460c..0000000000 --- a/test/unit/interpreter/operator-division.spec.ts +++ /dev/null @@ -1,83 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Operator DIVISION', () => { - it('works for obvious case', () => { - const engine = HyperFormula.buildFromArray([ - ['=9/3'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(3) - }) - - it('returns div when dividing by zero', () => { - const engine = HyperFormula.buildFromArray([ - ['=10/0'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('use number coerce', () => { - const engine = HyperFormula.buildFromArray([ - ['="9"/"3"'], - ['="foobar"/1'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(3) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('pass error from left operand', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2/3'], - ['=FOOBAR()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - }) - - it('pass error from right operand', () => { - const engine = HyperFormula.buildFromArray([ - ['=3/A2'], - ['=FOOBAR()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - }) - - it('pass error from left operand if both operands have error', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2/B2'], - ['=FOOBAR()', '=4/0'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['9'], - ['3'], - ['=10 / A1:A3'], - ['=A1:A3 / 10'], - ], {useArrayArithmetic: false}) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) - - it('Division propagates errors correctly', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=(1/0)/2', '=2/(1/0)', '=(A1:B1)/(1/0)', '=(1/0)/(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/operator-minus.spec.ts b/test/unit/interpreter/operator-minus.spec.ts deleted file mode 100644 index 847abd9e7c..0000000000 --- a/test/unit/interpreter/operator-minus.spec.ts +++ /dev/null @@ -1,75 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Operator MINUS', () => { - it('works for obvious case', () => { - const engine = HyperFormula.buildFromArray([ - ['=8-3'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(5) - }) - - it('use number coerce', () => { - const engine = HyperFormula.buildFromArray([ - ['="8"-"3"'], - ['="foobar"-1'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(5) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('pass error from left operand', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2-3'], - ['=4/0'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('pass error from right operand', () => { - const engine = HyperFormula.buildFromArray([ - ['=3-A2'], - ['=4/0'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('pass error from left operand if both operands have error', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2-B2'], - ['=FOOBAR()', '=4/0'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['8'], - ['3'], - ['=10 - A1:A3'], - ['=A1:A3 - 10'], - ], {useArrayArithmetic: false}) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) - - it('Minus propagates errors correctly', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=(1/0)-2', '=2-(1/0)', '=(A1:B1)-(1/0)', '=(1/0)-(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/operator-percent.spec.ts b/test/unit/interpreter/operator-percent.spec.ts deleted file mode 100644 index 2e486bfa20..0000000000 --- a/test/unit/interpreter/operator-percent.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Percent operator', () => { - it('works for obvious case', () => { - const engine = HyperFormula.buildFromArray([ - ['=3%'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0.03) - }) - - it('use number coerce', () => { - const engine = HyperFormula.buildFromArray([ - ['="3"%'], - ['="foobar"%'], - ['=TRUE()%'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0.03) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A3'))).toEqual(0.01) - }) - - it('pass reference', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2%'], - ['=42'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.42) - }) - - it('pass error', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2%'], - ['=FOOBAR()'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - }) - - it('works with other operator and coercion', () => { - const engine = HyperFormula.buildFromArray([['=TRUE()%*1']]) - - expect(engine.getCellValue(adr('A1'))).toEqual(0.01) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['9'], - ['3'], - ['=A1:A3%'], - ], {useArrayArithmetic: false}) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) -}) diff --git a/test/unit/interpreter/operator-plus.spec.ts b/test/unit/interpreter/operator-plus.spec.ts deleted file mode 100644 index 228d886bb7..0000000000 --- a/test/unit/interpreter/operator-plus.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Operator PLUS', () => { - it('works for obvious case', () => { - const engine = HyperFormula.buildFromArray([ - ['=2+3'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(5) - }) - - it('use number coerce', () => { - const engine = HyperFormula.buildFromArray([ - ['="2"+"3"'], - ['="foobar"+1'], - ['\'3'], - ['=A3+A3'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(5) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - expect(engine.getCellValue(adr('A4'))).toEqual(6) - }) - - it('pass error from left operand', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2+3'], - ['=4/0'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('pass error from right operand', () => { - const engine = HyperFormula.buildFromArray([ - ['=3+A2'], - ['=4/0'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('pass error from left operand if both operands have error', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2+B2'], - ['=FOOBAR()', '=4/0'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - ['=10 + A1:A3'], - ['=A1:A3 + 10'], - ], {useArrayArithmetic: false}) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) - - it('Plus propagates errors correctly', () => { - const engine = HyperFormula.buildFromArray([ - [0b1, '2', '=(1/0)+2', '=2+(1/0)', '=(A1:B1)+(1/0)', '=(1/0)+(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/operator-power.spec.ts b/test/unit/interpreter/operator-power.spec.ts deleted file mode 100644 index f2ca5b00b4..0000000000 --- a/test/unit/interpreter/operator-power.spec.ts +++ /dev/null @@ -1,100 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Operator POWER', () => { - it('works for obvious case', () => { - const engine = HyperFormula.buildFromArray([ - ['=8^3'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(512) - }) - - it('use number coerce', () => { - const engine = HyperFormula.buildFromArray([ - ['="8"^"3"'], - ['="foobar"^1'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(512) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('pass error from left operand', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2^3'], - ['=4/0'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('pass error from right operand', () => { - const engine = HyperFormula.buildFromArray([ - ['=3^A2'], - ['=4/0'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('pass error from left operand if both operands have error', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2^B2'], - ['=FOOBAR()', '=4/0'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['8'], - ['3'], - ['=10 ^ A1:A3'], - ['=A1:A3 ^ 10'], - ], {useArrayArithmetic: false}) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) - - it('Power propagates errors correctly', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=(1/0)^2', '=2^(1/0)', '=(A1:B1)^(1/0)', '=(1/0)^(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('NaN as a result', () => { - - const engine = HyperFormula.buildFromArray([ - ['01/02/1999', '02/02/1999', '=A1^B1'], - ['3.1415', '36193.2', '=A2^B2'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - expect(engine.getCellValue(adr('C2'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - }) - - it('negative base', () => { - const engine = HyperFormula.buildFromArray([ - ['', 2, -2], - [3, '=B1^A2', '=C1^A2'], - [3.5, '=B1^A3', '=C1^A3'], - ]) - - expect(engine.getCellValue(adr('B2'))).toBe(8) - expect(engine.getCellValue(adr('B3'))).toBeCloseTo(11.3137) - expect(engine.getCellValue(adr('C2'))).toBe(-8) - expect(engine.getCellValue(adr('C3'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.NaN)) - - }) -}) diff --git a/test/unit/interpreter/operator-times.spec.ts b/test/unit/interpreter/operator-times.spec.ts deleted file mode 100644 index 1c44649f5c..0000000000 --- a/test/unit/interpreter/operator-times.spec.ts +++ /dev/null @@ -1,83 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Operator TIMES', () => { - it('works for obvious case', () => { - const engine = HyperFormula.buildFromArray([ - ['=8*3'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(24) - }) - - it('no -0', () => { - const engine = HyperFormula.buildFromArray([ - ['=(-12)*0'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - }) - - it('use number coerce', () => { - const engine = HyperFormula.buildFromArray([ - ['="8"*"3"'], - ['="foobar"*1'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(24) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('pass error from left operand', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2*3'], - ['=4/0'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('pass error from right operand', () => { - const engine = HyperFormula.buildFromArray([ - ['=3*A2'], - ['=4/0'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('pass error from left operand if both operands have error', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2*B2'], - ['=FOOBAR()', '=4/0'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=10 * A1:A3'], - ['8', '=A1:A3 * 10'], - ['3'], - ['=10 * A1:A3'], - ['=A1:A3 * 10'], - ], {useArrayArithmetic: false}) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('A5'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) - - it('Times propagates errors correctly', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '=(1/0)*2', '=2*(1/0)', '=(A1:B1)*(1/0)', '=(1/0)*(A1:B1)'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('E1'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - expect(engine.getCellValue(adr('F1'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) -}) diff --git a/test/unit/interpreter/operator-unary-minus.spec.ts b/test/unit/interpreter/operator-unary-minus.spec.ts deleted file mode 100644 index 9364e17698..0000000000 --- a/test/unit/interpreter/operator-unary-minus.spec.ts +++ /dev/null @@ -1,53 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Unary operator MINUS', () => { - it('works for obvious case', () => { - const engine = HyperFormula.buildFromArray([ - ['=-3'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(-3) - }) - - it('use number coerce', () => { - const engine = HyperFormula.buildFromArray([ - ['=-"3"'], - ['=-"foobar"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(-3) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.NumberCoercion)) - }) - - it('pass error', () => { - const engine = HyperFormula.buildFromArray([ - ['=-B1', '=FOOBAR()'], - ['=-B2', '=1/0'], - - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['9'], - ['3'], - ['=-A1:A3'] - ], {useArrayArithmetic: false}) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) - - it('double unary plus', () => { - const engine = HyperFormula.buildFromArray([ - ['=--2'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) -}) diff --git a/test/unit/interpreter/operator-unary-plus.spec.ts b/test/unit/interpreter/operator-unary-plus.spec.ts deleted file mode 100644 index 94410b764a..0000000000 --- a/test/unit/interpreter/operator-unary-plus.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('Unary operator PLUS', () => { - it('works for obvious case', () => { - const engine = HyperFormula.buildFromArray([ - ['=+3'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(3) - }) - - it('use number coerce', () => { - const engine = HyperFormula.buildFromArray([ - ['=+"3"'], - ['=+"foobar"'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe('3') - expect(engine.getCellValue(adr('A2'))).toEqual('foobar') - }) - - it('pass error', () => { - const engine = HyperFormula.buildFromArray([ - ['=+B1', '=FOOBAR()'], - ['=+B2', '=1/0'], - - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.FunctionName('FOOBAR'))) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - }) - - it('range value results in VALUE error', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['9'], - ['3'], - ['=+A1:A3'], - ], {useArrayArithmetic: false}) - - expect(engine.getCellValue(adr('A4'))).toEqualError(detailedError(ErrorType.VALUE, ErrorMessage.ScalarExpected)) - }) - - it('string given by reference should return string with UNARY+', () => { - const engine = HyperFormula.buildFromArray([ - ['Liz'], - ['=+A1'] - ]) - expect(engine.getCellValue(adr('A2'))).toEqual('Liz') // UNARY PLUS value - }) - - it('double unary plus', () => { - const engine = HyperFormula.buildFromArray([ - ['=++2'], - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(2) - }) -}) diff --git a/test/unit/interpreter/paren-deps.spec.ts b/test/unit/interpreter/paren-deps.spec.ts deleted file mode 100644 index 48653216a0..0000000000 --- a/test/unit/interpreter/paren-deps.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {HyperFormula} from '../../../src' -import {adr} from '../testUtils' - -describe('dependencies with parenthesis', () => { - it('should be collected when required', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(1)'], - ['=(A1)+((A3))'], - ['=SUM(1)'], - ]) - expect(engine.getCellValue(adr('A2'))).toEqual(2) - }) - - it('should not build ref for special function', () => { - const engine = HyperFormula.buildFromArray([ - ['=COLUMN((((A1))))'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) -}) diff --git a/test/unit/interpreter/precision.spec.ts b/test/unit/interpreter/precision.spec.ts deleted file mode 100644 index 2ceecfe19e..0000000000 --- a/test/unit/interpreter/precision.spec.ts +++ /dev/null @@ -1,290 +0,0 @@ -import {HyperFormula} from '../../../src' -import {adr} from '../testUtils' - -describe('Imprecise comparisons', () => { - - it('less-than', () => { - const chunk1 = '.0000000001' - const chunk2 = '.00000000000005' - const engine = HyperFormula.buildFromArray([ - ['=1<1' + chunk1, '=1<1' + chunk2], - ['=1' + chunk1 + '<1', '=1' + chunk2 + '<1'], - ['=-1' + chunk1 + '<-1', '=-1' + chunk2 + '<-1'], - ['=-1<-1' + chunk1, '=-1<-1' + chunk2], - ['=0<0' + chunk1, '=0<0' + chunk2], - ['=0' + chunk1 + '<0', '=0' + chunk2 + '<0'], - ['=-0' + chunk1 + '<0', '=-0' + chunk2 + '<0'], - ['=0<-0' + chunk1, '=0<-0' + chunk2], - ], {smartRounding: true}) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('A2'))).toBe(false) - expect(engine.getCellValue(adr('B2'))).toBe(false) - expect(engine.getCellValue(adr('A3'))).toBe(true) - expect(engine.getCellValue(adr('B3'))).toBe(false) - expect(engine.getCellValue(adr('A4'))).toBe(false) - expect(engine.getCellValue(adr('B4'))).toBe(false) - expect(engine.getCellValue(adr('A5'))).toBe(true) - expect(engine.getCellValue(adr('B5'))).toBe(true) - expect(engine.getCellValue(adr('A6'))).toBe(false) - expect(engine.getCellValue(adr('B6'))).toBe(false) - expect(engine.getCellValue(adr('A7'))).toBe(true) - expect(engine.getCellValue(adr('B7'))).toBe(true) - expect(engine.getCellValue(adr('A8'))).toBe(false) - expect(engine.getCellValue(adr('B8'))).toBe(false) - }) - - it('greater-than', () => { - const chunk1 = '.0000000001' - const chunk2 = '.0000000000001' - const engine = HyperFormula.buildFromArray([ - ['=1>1' + chunk1, '=1>1' + chunk2], - ['=1' + chunk1 + '>1', '=1' + chunk2 + '>1'], - ['=-1' + chunk1 + '>-1', '=-1' + chunk2 + '>-1'], - ['=-1>-1' + chunk1, '=-1>-1' + chunk2], - ['=0>0' + chunk1, '=0>0' + chunk2], - ['=0' + chunk1 + '>0', '=0' + chunk2 + '>0'], - ['=-0' + chunk1 + '>0', '=-0' + chunk2 + '>0'], - ['=0>-0' + chunk1, '=0>-0' + chunk2], - ], {smartRounding: true}) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('B1'))).toBe(false) - expect(engine.getCellValue(adr('A2'))).toBe(true) - expect(engine.getCellValue(adr('B2'))).toBe(false) - expect(engine.getCellValue(adr('A3'))).toBe(false) - expect(engine.getCellValue(adr('B3'))).toBe(false) - expect(engine.getCellValue(adr('A4'))).toBe(true) - expect(engine.getCellValue(adr('B4'))).toBe(false) - expect(engine.getCellValue(adr('A5'))).toBe(false) - expect(engine.getCellValue(adr('B5'))).toBe(false) - expect(engine.getCellValue(adr('A6'))).toBe(true) - expect(engine.getCellValue(adr('B6'))).toBe(true) - expect(engine.getCellValue(adr('A7'))).toBe(false) - expect(engine.getCellValue(adr('B7'))).toBe(false) - expect(engine.getCellValue(adr('A8'))).toBe(true) - expect(engine.getCellValue(adr('B8'))).toBe(true) - }) - - it('greater-equal', () => { - const chunk1 = '.0000000001' - const chunk2 = '.0000000000001' - const engine = HyperFormula.buildFromArray([ - ['=1>=1' + chunk1, '=1>=1' + chunk2], - ['=1' + chunk1 + '>=1', '=1' + chunk2 + '>=1'], - ['=-1' + chunk1 + '>=-1', '=-1' + chunk2 + '>=-1'], - ['=-1>=-1' + chunk1, '=-1>=-1' + chunk2], - ['=0>=0' + chunk1, '=0>=0' + chunk2], - ['=0' + chunk1 + '>=0', '=0' + chunk2 + '>=0'], - ['=-0' + chunk1 + '>=0', '=-0' + chunk2 + '>=0'], - ['=0>=-0' + chunk1, '=0>=-0' + chunk2], - ], {smartRounding: true}) - - expect(engine.getCellValue(adr('A1'))).toBe(false) - expect(engine.getCellValue(adr('B1'))).toBe(true) - expect(engine.getCellValue(adr('A2'))).toBe(true) - expect(engine.getCellValue(adr('B2'))).toBe(true) - expect(engine.getCellValue(adr('A3'))).toBe(false) - expect(engine.getCellValue(adr('B3'))).toBe(true) - expect(engine.getCellValue(adr('A4'))).toBe(true) - expect(engine.getCellValue(adr('B4'))).toBe(true) - expect(engine.getCellValue(adr('A5'))).toBe(false) - expect(engine.getCellValue(adr('B5'))).toBe(false) - expect(engine.getCellValue(adr('A6'))).toBe(true) - expect(engine.getCellValue(adr('B6'))).toBe(true) - expect(engine.getCellValue(adr('A7'))).toBe(false) - expect(engine.getCellValue(adr('B7'))).toBe(false) - expect(engine.getCellValue(adr('A8'))).toBe(true) - expect(engine.getCellValue(adr('B8'))).toBe(true) - }) - - it('less-equal', () => { - const chunk1 = '.0000000001' - const chunk2 = '.0000000000001' - const engine = HyperFormula.buildFromArray([ - ['=1<=1' + chunk1, '=1<=1' + chunk2], - ['=1' + chunk1 + '<=1', '=1' + chunk2 + '<=1'], - ['=-1' + chunk1 + '<=-1', '=-1' + chunk2 + '<=-1'], - ['=-1<=-1' + chunk1, '=-1<=-1' + chunk2], - ['=0<=0' + chunk1, '=0<=0' + chunk2], - ['=0' + chunk1 + '<=0', '=0' + chunk2 + '<=0'], - ['=-0' + chunk1 + '<=0', '=-0' + chunk2 + '<=0'], - ['=0<=-0' + chunk1, '=0<=-0' + chunk2], - ], {smartRounding: true}) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(true) - expect(engine.getCellValue(adr('A2'))).toBe(false) - expect(engine.getCellValue(adr('B2'))).toBe(true) - expect(engine.getCellValue(adr('A3'))).toBe(true) - expect(engine.getCellValue(adr('B3'))).toBe(true) - expect(engine.getCellValue(adr('A4'))).toBe(false) - expect(engine.getCellValue(adr('B4'))).toBe(true) - expect(engine.getCellValue(adr('A5'))).toBe(true) - expect(engine.getCellValue(adr('B5'))).toBe(true) - expect(engine.getCellValue(adr('A6'))).toBe(false) - expect(engine.getCellValue(adr('B6'))).toBe(false) - expect(engine.getCellValue(adr('A7'))).toBe(true) - expect(engine.getCellValue(adr('B7'))).toBe(true) - expect(engine.getCellValue(adr('A8'))).toBe(false) - expect(engine.getCellValue(adr('B8'))).toBe(false) - }) -}) - -describe('Snap to zero', () => { - - it('minus', () => { - const chunk1 = '.0000000001' - const chunk2 = '.0000000000001' - const engine = HyperFormula.buildFromArray([ - ['=1-1' + chunk1, '=1-1' + chunk2], - ['=1' + chunk1 + '-1', '=1' + chunk2 + '-1'], - ['=-1' + chunk1 + '--1', '=-1' + chunk2 + '--1'], - ['=-1--1' + chunk1, '=-1--1' + chunk2], - ['=0-0' + chunk1, '=0-0' + chunk2], - ['=0' + chunk1 + '-0', '=0' + chunk2 + '-0'], - ['=-0' + chunk1 + '-0', '=-0' + chunk2 + '-0'], - ['=0--0' + chunk1, '=0--0' + chunk2], - ], {smartRounding: true}) - - expect(engine.dependencyGraph.getCellValue(adr('A1'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B1'))).toEqual(0) - expect(engine.dependencyGraph.getCellValue(adr('A2'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B2'))).toEqual(0) - expect(engine.dependencyGraph.getCellValue(adr('A3'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B3'))).toEqual(0) - expect(engine.dependencyGraph.getCellValue(adr('A4'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B4'))).toEqual(0) - expect(engine.dependencyGraph.getCellValue(adr('A5'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B5'))).toBeCloseTo(0.0000000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('A6'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B6'))).toBeCloseTo(0.0000000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('A7'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B7'))).toBeCloseTo(0.0000000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('A8'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B8'))).toBeCloseTo(0.0000000000001, 5) - }) - - it('plus', () => { - const chunk1 = '.0000000001' - const chunk2 = '.0000000000001' - const engine = HyperFormula.buildFromArray([ - ['=1+-1' + chunk1, '=1+-1' + chunk2], - ['=1' + chunk1 + '+-1', '=1' + chunk2 + '+-1'], - ['=-1' + chunk1 + '+1', '=-1' + chunk2 + '+1'], - ['=-1+1' + chunk1, '=-1+1' + chunk2], - ['=0+-0' + chunk1, '=0+-0' + chunk2], - ['=0' + chunk1 + '+-0', '=0' + chunk2 + '+-0'], - ['=-0' + chunk1 + '+-0', '=-0' + chunk2 + '+-0'], - ['=0+0' + chunk1, '=0+0' + chunk2], - ], {smartRounding: true}) - - expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0000000001, 5) - expect(engine.getCellValue(adr('B1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0000000001, 5) - expect(engine.getCellValue(adr('B2'))).toEqual(0) - expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.0000000001, 5) - expect(engine.getCellValue(adr('B3'))).toEqual(0) - expect(engine.getCellValue(adr('A4'))).toBeCloseTo(0.0000000001, 5) - expect(engine.getCellValue(adr('B4'))).toEqual(0) - expect(engine.getCellValue(adr('A5'))).toBeCloseTo(0.0000000001, 5) - expect(engine.getCellValue(adr('B5'))).toBeCloseTo(0.0000000000001, 5) - expect(engine.getCellValue(adr('A6'))).toBeCloseTo(0.0000000001, 5) - expect(engine.getCellValue(adr('B6'))).toBeCloseTo(0.0000000000001, 5) - expect(engine.getCellValue(adr('A7'))).toBeCloseTo(0.0000000001, 5) - expect(engine.getCellValue(adr('B7'))).toBeCloseTo(0.0000000000001, 5) - expect(engine.getCellValue(adr('A8'))).toBeCloseTo(0.0000000001, 5) - expect(engine.getCellValue(adr('B8'))).toBeCloseTo(0.0000000000001, 5) - }) -}) - -describe('Value-fixed', () => { - it('should correctly calculate 0.2 + 0.1 as 0.3', () => { - const engine = HyperFormula.buildFromArray([ - ['=0.2+0.1'], - ], {smartRounding: true}) - - expect(engine.getCellValue(adr('A1'))).toBe(0.3) - }) -}) - -describe('tests', () => { - it('addition of small numbers with smartRounding #1', () => { - const engine = HyperFormula.buildFromArray([ - ['0.000123456789', '1', '=A1+B1'], - ], {smartRounding: true}) - - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(1.000123456789, 10) - }) - - it('addition of small numbers with smartRounding #2', () => { - const engine = HyperFormula.buildFromArray([ - ['0.000123456789', '1', '=A1+B1'], - ], {smartRounding: true, precisionRounding: 9}) - - expect(engine.getCellValue(adr('C1'))).toEqual(1.000123457) //as GS and E - }) -}) - -describe('internal rounding', () => { - it('Precision accumulates', () => { - const engine = HyperFormula.buildFromArray([ - ['', 'Revenue', '', '1000', '=D1*(1+E2)', '=E1*(1+F2)', '=F1*(1+G2)', '=G1*(1+H2)', '=H1*(1+I2)', '=I1*(1+J2)', '=J1*(1+K2)', '=K1*(1+L2)', '=L1*(1+M2)', '=M1*(1+N2)'], - ['', '% Growth', '', '', '.100000000000000', '=E2', '=F2', '=G2', '=H2', '=I2', '=J2', '=K2', '=L2', '=M2'] - ]) - expect(engine.getSheetValues(0)).toEqual([ - // eslint-disable-next-line @typescript-eslint/no-loss-of-precision - ['', 'Revenue', '', 1000.000000000000000, 1100.000000000000000, 1210.000000000000000, 1331.000000000000000, 1464.100000000000000, 1610.510000000000000, 1771.561000000000000, 1948.717100000000000, 2143.588810000000000, 2357.947691000000000, 2593.742460100000000], - ['', '% Growth', '', '', .100000000000000, .100000000000000, .100000000000000, .100000000000000, .100000000000000, .100000000000000, .100000000000000, .100000000000000, .100000000000000, .100000000000000] - ]) - }) -}) - -describe('number of leading digits', () => { - it('rounding extensive test', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '0.33333333333333300000', '=A1/3'], - ['10', '3.33333333333333000000', '=A2/3'], - ['100', '33.33333333333330000000', '=A3/3'], - ['1000', '333.33333333333300000000', '=A4/3'], - ['10000', '3333.33333333333000000000', '=A5/3'], - ['100000', '33333.33333333330000000000', '=A6/3'], - ['1000000', '333333.33333333300000000000', '=A7/3'], - ['10000000', '3333333.33333333000000000000', '=A8/3'], - ['100000000', '33333333.33333330000000000000', '=A9/3'], - ['1000000000', '333333333.33333300000000000000', '=A10/3'], - ['10000000000', '3333333333.33333000000000000000', '=A11/3'], - ['100000000000', '33333333333.33330000000000000000', '=A12/3'], - ['1000000000000', '333333333333.33300000000000000000', '=A13/3'], - ['10000000000000', '3333333333333.33000000000000000000', '=A14/3'], - ['100000000000000', '33333333333333.30000000000000000000', '=A15/3'], - ['1000000000000000', '333333333333333.00000000000000000000', '=A16/3'], - ['10000000000000000', '3333333333333330.00000000000000000000', '=A17/3'], - ['100000000000000000', '33333333333333300.00000000000000000000', '=A18/3'], - ['1000000000000000000', '333333333333333000.00000000000000000000', '=A19/3'], - ['10000000000000000000', '3333333333333330000.00000000000000000000', '=A20/3'], - ]) - - expect(engine.getCellValue(adr('C1'))).toEqual(engine.getCellValue(adr('B1'))) - expect(engine.getCellValue(adr('C2'))).toEqual(engine.getCellValue(adr('B2'))) - expect(engine.getCellValue(adr('C3'))).toEqual(engine.getCellValue(adr('B3'))) - expect(engine.getCellValue(adr('C4'))).toEqual(engine.getCellValue(adr('B4'))) - expect(engine.getCellValue(adr('C5'))).toEqual(engine.getCellValue(adr('B5'))) - expect(engine.getCellValue(adr('C6'))).toEqual(engine.getCellValue(adr('B6'))) - expect(engine.getCellValue(adr('C7'))).toEqual(engine.getCellValue(adr('B7'))) - expect(engine.getCellValue(adr('C8'))).toEqual(engine.getCellValue(adr('B8'))) - expect(engine.getCellValue(adr('C9'))).toEqual(engine.getCellValue(adr('B9'))) - expect(engine.getCellValue(adr('C10'))).toEqual(engine.getCellValue(adr('B10'))) - expect(engine.getCellValue(adr('C11'))).toEqual(engine.getCellValue(adr('B11'))) - expect(engine.getCellValue(adr('C12'))).toEqual(engine.getCellValue(adr('B12'))) - expect(engine.getCellValue(adr('C13'))).toEqual(engine.getCellValue(adr('B13'))) - expect(engine.getCellValue(adr('C14'))).toEqual(engine.getCellValue(adr('B14'))) - expect(engine.getCellValue(adr('C15'))).toEqual(engine.getCellValue(adr('B15'))) - expect(engine.getCellValue(adr('C16'))).toEqual(engine.getCellValue(adr('B16'))) - expect(engine.getCellValue(adr('C17'))).toEqual(engine.getCellValue(adr('B17'))) - expect(engine.getCellValue(adr('C18'))).toEqual(engine.getCellValue(adr('B18'))) - expect(engine.getCellValue(adr('C19'))).toEqual(engine.getCellValue(adr('B19'))) - expect(engine.getCellValue(adr('C20'))).toEqual(engine.getCellValue(adr('B20'))) - }) -}) diff --git a/test/unit/interpreter/scalar.spec.ts b/test/unit/interpreter/scalar.spec.ts deleted file mode 100644 index bd7b74c84f..0000000000 --- a/test/unit/interpreter/scalar.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {ErrorType} from '../../../src' -import {CellError} from '../../../src/Cell' -import {Config} from '../../../src/Config' -import {DateTimeHelper} from '../../../src/DateTimeHelper' -import {ArithmeticHelper} from '../../../src/interpreter/ArithmeticHelper' -import {NumberLiteralHelper} from '../../../src/NumberLiteralHelper' - -describe('nonstrictadd', () => { - const config = new Config() - const dateTimeHelper = new DateTimeHelper(config) - const numberLiteralsHelper = new NumberLiteralHelper(config) - const arithmeticHelper = new ArithmeticHelper(config, dateTimeHelper, numberLiteralsHelper) - it('adds', () => { - expect(arithmeticHelper.nonstrictadd(2, 3)).toEqual(5) - }) - - it('return error of right operand', () => { - expect(arithmeticHelper.nonstrictadd(2, new CellError(ErrorType.DIV_BY_ZERO))).toEqual(new CellError(ErrorType.DIV_BY_ZERO)) - }) - - it('return error of left operand if both present', () => { - expect(arithmeticHelper.nonstrictadd(new CellError(ErrorType.NA), new CellError(ErrorType.DIV_BY_ZERO))).toEqual(new CellError(ErrorType.NA)) - }) - - it('ignores non-numerics', () => { - expect(arithmeticHelper.nonstrictadd('foo', 5)).toEqual(5) - expect(arithmeticHelper.nonstrictadd(5, 'foo')).toEqual(5) - expect(arithmeticHelper.nonstrictadd('bar', 'foo')).toEqual(0) - }) - - it('returns 0 if only non-numerics', () => { - expect(arithmeticHelper.nonstrictadd('bar', 'foo')).toEqual(0) - }) -}) diff --git a/test/unit/interpreter/separate-cache.spec.ts b/test/unit/interpreter/separate-cache.spec.ts deleted file mode 100644 index d9ac53ce7f..0000000000 --- a/test/unit/interpreter/separate-cache.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {HyperFormula} from '../../../src' -import {adr} from '../testUtils' - -describe('numeric aggreagtion functions', () => { - it('should use separate caches', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 5, 10, 20], - ['=MIN(A1:E1)', '=MAX(A1:E1)', '=SUM(A1:E1)', '=SUMSQ(A1:E1)', '=AVERAGE(A1:E1)'], - ['=MIN(A1:E1)', '=MAX(A1:E1)', '=SUM(A1:E1)', '=SUMSQ(A1:E1)', '=AVERAGE(A1:E1)'], - ]) - expect(engine.getCellValue(adr('A3'))).toEqual(1) - expect(engine.getCellValue(adr('B3'))).toEqual(20) - expect(engine.getCellValue(adr('C3'))).toEqual(38) - expect(engine.getCellValue(adr('D3'))).toEqual(530) - expect(engine.getCellValue(adr('E3'))).toEqual(7.6) - }) -}) diff --git a/test/unit/interpreter/string-cmp.spec.ts b/test/unit/interpreter/string-cmp.spec.ts deleted file mode 100644 index 24df280063..0000000000 --- a/test/unit/interpreter/string-cmp.spec.ts +++ /dev/null @@ -1,144 +0,0 @@ -import {HyperFormula} from '../../../src' -import {adr} from '../testUtils' - -describe('string comparison', () => { - it('comparison default', () => { - const engine = HyperFormula.buildFromArray([ - ['a', 'A', '=A1>B1'], - ['aa', 'AA', '=A2>B2'], - ['aA', 'aa', '=A3>B3'], - ['Aa', 'aa', '=A4>B4'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(false) - expect(engine.getCellValue(adr('C2'))).toBe(false) - expect(engine.getCellValue(adr('C3'))).toBe(false) - expect(engine.getCellValue(adr('C4'))).toBe(false) - }) - - it('works with localeLang = "en-US"', () => { - const engine = HyperFormula.buildFromArray([ - ['a', 'A', '=A1>B1'], - ['aa', 'AA', '=A2>B2'], - ['aA', 'aa', '=A3>B3'], - ['Aa', 'aa', '=A4>B4'], - ], { localeLang: 'en-US' }) - - expect(engine.getCellValue(adr('C1'))).toBe(false) - expect(engine.getCellValue(adr('C2'))).toBe(false) - expect(engine.getCellValue(adr('C3'))).toBe(false) - expect(engine.getCellValue(adr('C4'))).toBe(false) - }) - - it('accents default', () => { - const engine = HyperFormula.buildFromArray([ - ['a', 'ä', '=A1>B1'], - ['áá', 'ää', '=A2>B2'], - ['ää', 'ĄĄ', '=A3>B3'], - ['ää', 'ZZ', '=A4>B4'], - ]) - - expect(engine.getCellValue(adr('C1'))).toBe(false) - expect(engine.getCellValue(adr('C2'))).toBe(false) - expect(engine.getCellValue(adr('C3'))).toBe(false) - expect(engine.getCellValue(adr('C4'))).toBe(false) - }) - - it('accents sensitive', () => { - const engine = HyperFormula.buildFromArray([ - ['Ą', 'ą', '=A1>B1'], - ['ää', 'áá', '=A2>B2'], - ['ää', 'ĄĄ', '=A3>B3'], - ['ää', 'ŹŹ', '=A4>B4'], - ], {accentSensitive: true}) - - expect(engine.getCellValue(adr('C1'))).toBe(false) - expect(engine.getCellValue(adr('C2'))).toBe(true) - expect(engine.getCellValue(adr('C3'))).toBe(false) - expect(engine.getCellValue(adr('C4'))).toBe(false) - }) - - it('accents+case sensitive', () => { - const engine = HyperFormula.buildFromArray([ - ['Ą', 'ą', '=A1>B1'], - ['áá', 'ää', '=A2>B2'], - ['ää', 'ĄĄ', '=A3>B3'], - ['ää', 'ŹŹ', '=A4>B4'], - ], {accentSensitive: true, caseSensitive: true}) - - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('C2'))).toBe(false) - expect(engine.getCellValue(adr('C3'))).toBe(false) - expect(engine.getCellValue(adr('C4'))).toBe(false) - }) - - it('accents+case sensitive, reverse order', () => { - const engine = HyperFormula.buildFromArray([ - ['Ą', 'ą', '=A1>B1'], - ['áá', 'ää', '=A2>B2'], - ['ää', 'ĄĄ', '=A3>B3'], - ['ää', 'ŹŹ', '=A4>B4'], - ], {accentSensitive: true, caseSensitive: true, caseFirst: 'upper'}) - - expect(engine.getCellValue(adr('C1'))).toBe(false) - expect(engine.getCellValue(adr('C2'))).toBe(false) - expect(engine.getCellValue(adr('C3'))).toBe(false) - expect(engine.getCellValue(adr('C4'))).toBe(false) - }) - - it('accents lang', () => { - const engine = HyperFormula.buildFromArray([ - ['a', 'ä', '=A1>B1'], - ['aa', 'ää', '=A2>B2'], - ['ää', 'ĄĄ', '=A3>B3'], - ['ää', 'ZZ', '=A4>B4'], - ], {localeLang: 'sv', accentSensitive: true}) - - expect(engine.getCellValue(adr('C1'))).toBe(false) - expect(engine.getCellValue(adr('C2'))).toBe(false) - expect(engine.getCellValue(adr('C3'))).toBe(true) - expect(engine.getCellValue(adr('C4'))).toBe(true) - }) - - it('comparison case sensitive', () => { - const engine = HyperFormula.buildFromArray([ - ['ą', 'A', '=A1>B1'], - ['aa', 'AA', '=A2>B2'], - ['aA', 'aa', '=A3>B3'], - ['Aa', 'aa', '=A4>B4'], - ], {caseSensitive: true}) - - expect(engine.getCellValue(adr('C1'))).toBe(false) - expect(engine.getCellValue(adr('C2'))).toBe(false) - expect(engine.getCellValue(adr('C3'))).toBe(true) - expect(engine.getCellValue(adr('C4'))).toBe(true) - }) - - it('comparison case sensitive, reverse order', () => { - const engine = HyperFormula.buildFromArray([ - ['ą', 'A', '=A1>B1'], - ['aa', 'AA', '=A2>B2'], - ['aA', 'aa', '=A3>B3'], - ['Aa', 'aa', '=A4>B4'], - ], {caseSensitive: true, caseFirst: 'upper'}) - - expect(engine.getCellValue(adr('C1'))).toBe(true) - expect(engine.getCellValue(adr('C2'))).toBe(true) - expect(engine.getCellValue(adr('C3'))).toBe(false) - expect(engine.getCellValue(adr('C4'))).toBe(false) - }) - - it('comparison ignore punctuation', () => { - const engine = HyperFormula.buildFromArray([ - ['a', 'A,A,A', '=A1>B1'], - ['aa', '...AA', '=A2>B2'], - ['aA', ';;;;aa', '=A3>B3'], - ['Aa', '????aa', '=A4>B4'], - ], {ignorePunctuation: true}) - - expect(engine.getCellValue(adr('C1'))).toBe(false) - expect(engine.getCellValue(adr('C2'))).toBe(false) - expect(engine.getCellValue(adr('C3'))).toBe(false) - expect(engine.getCellValue(adr('C4'))).toBe(false) - }) -}) diff --git a/test/unit/licence.spec.ts b/test/unit/licence.spec.ts deleted file mode 100644 index 2deaf5dbbe..0000000000 --- a/test/unit/licence.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {ErrorType, HyperFormula} from '../../src' -import {ErrorMessage} from '../../src/error-message' -import {adr, detailedError} from './testUtils' - -describe('Wrong licence', () => { - it('eval', () => { - const engine = HyperFormula.buildFromArray([['=TRUE()', null, 1, '=A(']], {licenseKey: ''}) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.LIC, ErrorMessage.LicenseKey('missing'))) - expect(engine.getCellValue(adr('B1'))).toEqual(null) - expect(engine.getCellValue(adr('C1'))).toEqual(1) - expect(engine.getCellValue(adr('D1'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - }) - - it('serialization', () => { - const engine = HyperFormula.buildFromArray([['=TRUE()', null, 1, '=A(']], {licenseKey: ''}) - expect(engine.getSheetSerialized(0)).toEqual([['=TRUE()', null, 1, '=A(']]) - }) -}) diff --git a/test/unit/matchers.spec.ts b/test/unit/matchers.spec.ts deleted file mode 100644 index 6e3cdd8d36..0000000000 --- a/test/unit/matchers.spec.ts +++ /dev/null @@ -1,59 +0,0 @@ -import {DetailedCellError} from '../../src' -import {ArraySize} from '../../src/ArraySize' -import {CellError, ErrorType} from '../../src/Cell' -import {FormulaVertex} from '../../src/DependencyGraph/FormulaVertex' -import {buildNumberAst} from '../../src/parser/Ast' -import {adr} from './testUtils' - -describe('Matchers', () => { - it('should compare two simple values', () => { - expect(1).toEqualError(1) - expect(1).not.toEqualError(2) - }) - - it('should compare two cell errors ignoring vertices', () => { - function dummyFormulaVertex(): FormulaVertex { - return FormulaVertex.fromAst(buildNumberAst(1), adr('A1'), ArraySize.scalar(), 0) - } - - expect( - new CellError(ErrorType.ERROR, '', dummyFormulaVertex()) - ).toEqualError( - new CellError(ErrorType.ERROR, '') - ) - - expect( - new CellError(ErrorType.ERROR, 'a', dummyFormulaVertex()) - ).not.toEqualError( - new CellError(ErrorType.ERROR, 'b', dummyFormulaVertex()) - ) - - expect( - new CellError(ErrorType.NA, '', dummyFormulaVertex()) - ).not.toEqualError( - new CellError(ErrorType.ERROR, '', dummyFormulaVertex()) - ) - }) - - it('compare two detailed errors ignoring addresses', () => { - expect( - new DetailedCellError(new CellError(ErrorType.ERROR), '') - ).toEqualError( - new DetailedCellError(new CellError(ErrorType.ERROR), '', 'A1') - ) - - expect( - new DetailedCellError(new CellError(ErrorType.ERROR), 'a') - ).not.toEqualError( - new DetailedCellError(new CellError(ErrorType.ERROR), '', 'A1') - ) - }) - - it('should compare two ad-hoc objects ignoring addresses', () => { - expect( - {type: ErrorType.ERROR, message: '', address: adr('A1')} - ).toEqualError( - {type: ErrorType.ERROR, message: '', address: undefined} - ) - }) -}) diff --git a/test/unit/matrix-mapping.spec.ts b/test/unit/matrix-mapping.spec.ts deleted file mode 100644 index d03f5d670f..0000000000 --- a/test/unit/matrix-mapping.spec.ts +++ /dev/null @@ -1,59 +0,0 @@ -import {AbsoluteCellRange} from '../../src/AbsoluteCellRange' -import {ArraySize} from '../../src/ArraySize' -import {ArrayMapping, ArrayFormulaVertex} from '../../src/DependencyGraph' -import {buildNumberAst} from '../../src/parser/Ast' -import {adr} from './testUtils' - -describe('MatrixMapping', () => { - it('should be empty', () => { - const matrixMapping = new ArrayMapping() - - expect(matrixMapping.count()).toEqual(0) - }) - it('should set matrix', () => { - const matrixMapping = new ArrayMapping() - - const vertex = new ArrayFormulaVertex(buildNumberAst(1), adr('A1'), new ArraySize(2, 2)) - const range = AbsoluteCellRange.spanFrom(adr('A1'), 2, 2) - matrixMapping.setArray(range, vertex) - - expect(matrixMapping.count()).toEqual(1) - expect(matrixMapping.getArray(range)).toEqual(vertex) - }) - - it('should answer some questions', () => { - const matrixMapping = new ArrayMapping() - const vertex = new ArrayFormulaVertex(buildNumberAst(1), adr('B2'), new ArraySize(2, 2)) - const range = AbsoluteCellRange.spanFrom(adr('B2'), 2, 2) - matrixMapping.setArray(range, vertex) - - expect(matrixMapping.isFormulaArrayInRow(0, 0)).toBe(false) - expect(matrixMapping.isFormulaArrayInRow(0, 1)).toBe(true) - expect(matrixMapping.isFormulaArrayAtAddress(adr('A1'))).toBe(false) - expect(matrixMapping.isFormulaArrayAtAddress(adr('B2'))).toBe(true) - expect(matrixMapping.isFormulaArrayInRange(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1))).toEqual(false) - expect(matrixMapping.isFormulaArrayInRange(AbsoluteCellRange.spanFrom(adr('B1'), 1, 2))).toEqual(true) - expect(matrixMapping.isFormulaArrayInRange(AbsoluteCellRange.spanFrom(adr('A2'), 2, 1))).toEqual(true) - expect(matrixMapping.isFormulaArrayInColumn(0, 0)).toEqual(false) - expect(matrixMapping.isFormulaArrayInColumn(0, 1)).toEqual(true) - expect(matrixMapping.isFormulaArrayInRow(0, 0)).toEqual(false) - expect(matrixMapping.isFormulaArrayInRow(0, 1)).toEqual(true) - }) - - it('should move matrices below row', () => { - const matrixMapping = new ArrayMapping() - const vertex1 = new ArrayFormulaVertex(buildNumberAst(1), adr('B1'), new ArraySize(2, 2)) - const range1 = AbsoluteCellRange.spanFrom(adr('B1'), 2, 2) - const vertex2 = new ArrayFormulaVertex(buildNumberAst(1), adr('D2'), new ArraySize(2, 2)) - const range2 = AbsoluteCellRange.spanFrom(adr('D2'), 2, 2) - matrixMapping.setArray(range1, vertex1) - matrixMapping.setArray(range2, vertex2) - - matrixMapping.moveArrayVerticesAfterRowByRows(0, 0, 2) - - expect(matrixMapping.getArrayByCorner(range1.start)).toBeUndefined() - expect(matrixMapping.getArrayByCorner(range2.start)).toBeUndefined() - expect(matrixMapping.getArrayByCorner(adr('B3'))).toEqual(vertex1) - expect(matrixMapping.getArrayByCorner(adr('D4'))).toEqual(vertex2) - }) -}) diff --git a/test/unit/matrix-size-check.spec.ts b/test/unit/matrix-size-check.spec.ts deleted file mode 100644 index 0b422062dd..0000000000 --- a/test/unit/matrix-size-check.spec.ts +++ /dev/null @@ -1,195 +0,0 @@ -import {ArraySize, ArraySizePredictor} from '../../src/ArraySize' -import {Config} from '../../src/Config' -import {FunctionRegistry} from '../../src/interpreter/FunctionRegistry' -import {Interpreter} from '../../src/interpreter/Interpreter' -import {buildEmptyParserWithCaching} from './parser/common' -import {adr} from './testUtils' -import {DependencyGraph} from '../../src/DependencyGraph' -import {ColumnSearchStrategy} from '../../src/Lookup/SearchStrategy' -import {Statistics} from '../../src/statistics' -import {ArithmeticHelper} from '../../src/interpreter/ArithmeticHelper' -import {NamedExpressions} from '../../src/NamedExpressions' -import {Serialization} from '../../src/Serialization' -import {DateTimeHelper} from '../../src/DateTimeHelper' - -describe('Matrix size check tests', () => { - const config = new Config() - const functionRegistry = new FunctionRegistry(config) - const arraySizePredictor = new ArraySizePredictor(config, functionRegistry) - - new Interpreter( - config, - undefined as unknown as DependencyGraph, - undefined as unknown as ColumnSearchStrategy, - undefined as unknown as Statistics, - undefined as unknown as ArithmeticHelper, - functionRegistry, - undefined as unknown as NamedExpressions, - undefined as unknown as Serialization, - arraySizePredictor, - undefined as unknown as DateTimeHelper - ) - - it('check', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('=mmult(A1:B3,C1:E2)', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(3, 3)) - }) - - it('even for wrong size, we need to estimate', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('=mmult(A1:B3,C1:E3)', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(3, 3)) - }) - - it('check recursive', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('=mmult(mmult(A1:B3,C1:E2), A1:B3)', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(2, 3)) - }) - - it('wrong size estimation', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('=mmult(mmult(A1:B3,C1:E3), A1:B3)', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(2, 3)) - }) - - it('check maxpool', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('=maxpool(A1:I9,3)', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(3, 3)) - }) - - it('check with noninteger args', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('=maxpool(A1:I9,B3)', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(9, 9)) - }) - - it('check transpose with cell reference', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('=transpose(A2)', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(1, 1)) - }) - - it('check scalar', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('=1234', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(1, 1, false)) - }) - - it('check cell reference', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('=A1', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(1, 1, true)) - }) - - it('check binary array arithmetic #1', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('=A1:D3+A1:C4', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(ArraySize.error()) - }) - - it('check binary array arithmetic #2', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('=ARRAYFORMULA(A1:D3+A1:C4)', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(4, 4)) - }) - - it('check unary array arithmetic #1', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('=-A1:B3', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(ArraySize.error()) - }) - - it('check unary array arithmetic #2', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('=ARRAYFORMULA(-A1:B3)', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(2, 3)) - }) - - it('check matrix parsing #1', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('={1,2,3;4,5,6}', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(3, 2)) - }) - - it('check matrix parsing #2', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('={{1;2},{3;4}}', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(2, 2)) - }) - - it('check matrix parsing #3', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('={1,{2,3},4;{5;6},{7,8;9,10},{11;12};13,{14,15},16}', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(4, 4)) - }) -}) - -describe('Matrix size check tests, with different config', () => { - const config = new Config({useArrayArithmetic: true}) - const functionRegistry = new FunctionRegistry(config) - const arraySizePredictor = new ArraySizePredictor(config, functionRegistry) - - new Interpreter( - config, - undefined as unknown as DependencyGraph, - undefined as unknown as ColumnSearchStrategy, - undefined as unknown as Statistics, - undefined as unknown as ArithmeticHelper, - functionRegistry, - undefined as unknown as NamedExpressions, - undefined as unknown as Serialization, - arraySizePredictor, - undefined as unknown as DateTimeHelper - ) - - it('check binary array arithmetic', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('=A1:D3+A1:C4', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(4, 4)) - }) - - it('check unary array arithmetic', () => { - const parser = buildEmptyParserWithCaching(config) - const ast = parser.parse('=-A1:B3', adr('A1')).ast - - const size = arraySizePredictor.checkArraySize(ast, adr('A1')) - expect(size).toEqual(new ArraySize(2, 3)) - }) -}) diff --git a/test/unit/matrix.spec.ts b/test/unit/matrix.spec.ts deleted file mode 100644 index 3946567652..0000000000 --- a/test/unit/matrix.spec.ts +++ /dev/null @@ -1,303 +0,0 @@ -import { ArraySize } from '../../src/ArraySize' -import { ArrayValue, NotComputedArray } from '../../src/ArrayValue' -import { EmptyValue } from '../../src/interpreter/InterpreterValue' - -describe('Matrix', () => { - it('fill', () => { - const matrix = new ArrayValue([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - expect(matrix.raw()).toEqual([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) - }) - - it('fill - zero sized rows', () => { - expect(() => { - new ArrayValue([]) - }).toThrowError('Incorrect array size') - }) - - it('fill - zero sized columns', () => { - expect(() => { - new ArrayValue([[], [], []]) - }).toThrowError('Incorrect array size') - }) - - it('add rows', () => { - const matrix = new ArrayValue([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ]) - matrix.addRows(1, 3) - expect(matrix.raw()).toEqual([ - [1, 2, 3], - [EmptyValue, EmptyValue, EmptyValue], - [EmptyValue, EmptyValue, EmptyValue], - [EmptyValue, EmptyValue, EmptyValue], - [4, 5, 6], - [7, 8, 9], - ]) - expect(matrix.height()).toEqual(6) - }) - - it('remove rows', () => { - const matrix = new ArrayValue([ - [1, 2], - [3, 4], - [5, 6], - [7, 8], - ]) - matrix.removeRows(1, 2) - expect(matrix.height()).toEqual(2) - expect(matrix.raw()).toEqual([ - [1, 2], - [7, 8], - ]) - }) - - it('remove rows out of bound', () => { - const matrix = new ArrayValue([ - [1, 2], - ]) - expect(() => { - matrix.removeRows(1, 1) - }).toThrowError('Array index out of bound') - }) - - it('add columns', () => { - const matrix = new ArrayValue([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ]) - matrix.addColumns(1, 2) - expect(matrix.width()).toEqual(5) - expect(matrix.raw()).toEqual([ - [1, EmptyValue, EmptyValue, 2, 3], - [4, EmptyValue, EmptyValue, 5, 6], - [7, EmptyValue, EmptyValue, 8, 9], - ]) - }) - - it('remove columns', () => { - const matrix = new ArrayValue([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ]) - matrix.removeColumns(1, 2) - expect(matrix.width()).toEqual(1) - expect(matrix.raw()).toEqual([ - [1], - [4], - [7], - ]) - }) - - it('remove columns - out of bounds', () => { - const matrix = new ArrayValue([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ]) - expect(() => { - matrix.removeColumns(3, 2) - }).toThrowError('Array index out of bound') - }) - - it('resize dimensions - increase height', () => { - const matrix = new ArrayValue([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ]) - const newSize = new ArraySize(3, 5) - matrix.resize(newSize) - expect(matrix.height()).toEqual(5) - expect(matrix.raw()).toEqual([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - [EmptyValue, EmptyValue, EmptyValue], - [EmptyValue, EmptyValue, EmptyValue], - ]) - }) - - it('resize dimensions - reduce height', () => { - const matrix = new ArrayValue([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ]) - expect(() => { - const newSize = new ArraySize(3, 1) - matrix.resize(newSize) - }).toThrowError('Resizing to smaller array') - }) - - it('resize dimensions - increase width', () => { - const matrix = new ArrayValue([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ]) - const newSize = new ArraySize(5, 3) - matrix.resize(newSize) - expect(matrix.width()).toEqual(5) - expect(matrix.raw()).toEqual([ - [1, 2, 3, EmptyValue, EmptyValue], - [4, 5, 6, EmptyValue, EmptyValue], - [7, 8, 9, EmptyValue, EmptyValue], - ]) - }) - - it('resize dimensions - reduce width', () => { - const matrix = new ArrayValue([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ]) - expect(() => { - const newSize = new ArraySize(1, 3) - matrix.resize(newSize) - }).toThrowError('Resizing to smaller array') - }) - - it('resize dimensions - increase height and width', () => { - const matrix = new ArrayValue([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ]) - const newSize = new ArraySize(5, 5) - matrix.resize(newSize) - expect(matrix.width()).toEqual(5) - expect(matrix.height()).toEqual(5) - expect(matrix.raw()).toEqual([ - [1, 2, 3, EmptyValue, EmptyValue], - [4, 5, 6, EmptyValue, EmptyValue], - [7, 8, 9, EmptyValue, EmptyValue], - [EmptyValue, EmptyValue, EmptyValue, EmptyValue, EmptyValue], - [EmptyValue, EmptyValue, EmptyValue, EmptyValue, EmptyValue], - ]) - }) - - it('get value', () => { - const matrix = new ArrayValue([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ]) - expect(matrix.get(0, 0)).toEqual(1) - expect(matrix.get(1, 0)).toEqual(2) - expect(matrix.get(2, 0)).toEqual(3) - expect(matrix.get(0, 1)).toEqual(4) - expect(matrix.get(1, 1)).toEqual(5) - expect(matrix.get(2, 1)).toEqual(6) - expect(matrix.get(0, 2)).toEqual(7) - expect(matrix.get(1, 2)).toEqual(8) - expect(matrix.get(2, 2)).toEqual(9) - }) - - it('get value - out of bounds', () => { - const matrix = new ArrayValue([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ]) - expect(() => { - matrix.get(3, 0) - }).toThrowError('Array index out of bound') - expect(() => { - matrix.get(0, 3) - }).toThrowError('Array index out of bound') - expect(() => { - matrix.get(3, 3) - }).toThrowError('Array index out of bound') - expect(() => { - matrix.get(-1, 0) - }).toThrowError('Array index out of bound') - expect(() => { - matrix.get(0, -1) - }).toThrowError('Array index out of bound') - expect(() => { - matrix.get(-1, -1) - }).toThrowError('Array index out of bound') - }) - - it('set value', () => { - const matrix = new ArrayValue([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ]) - matrix.set(0, 0, 11) - matrix.set(1, 0, 12) - matrix.set(2, 0, 13) - matrix.set(0, 1, 14) - matrix.set(1, 1, 15) - matrix.set(2, 1, 16) - matrix.set(0, 2, 17) - matrix.set(1, 2, 18) - matrix.set(2, 2, 19) - expect(matrix.raw()).toEqual([ - [11, 12, 13], - [14, 15, 16], - [17, 18, 19], - ]) - }) - - it('set value - out of bounds', () => { - const matrix = new ArrayValue([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ]) - expect(() => { - matrix.set(3, 0, 99) - }).toThrowError('Array index out of bound') - expect(() => { - matrix.set(0, 3, 99) - }).toThrowError('Array index out of bound') - expect(() => { - matrix.set(3, 3, 99) - }).toThrowError('Array index out of bound') - expect(() => { - matrix.set(-1, 0, 99) - }).toThrowError('Array index out of bound') - expect(() => { - matrix.set(0, -1, 99) - }).toThrowError('Array index out of bound') - expect(() => { - matrix.set(-1, -1, 99) - }).toThrowError('Array index out of bound') - }) -}) - -describe('NotComputedArray', () => { - it('width', () => { - const ncArraySize = new ArraySize(3, 2) - const ncArray = new NotComputedArray(ncArraySize) - expect(ncArray.width()).toEqual(3) - }) - - it('height', () => { - const ncArraySize = new ArraySize(3, 2) - const ncArray = new NotComputedArray(ncArraySize) - expect(ncArray.height()).toEqual(2) - }) - - it('get', () => { - const ncArraySize = new ArraySize(3, 2) - const ncArray = new NotComputedArray(ncArraySize) - expect(() => { - ncArray.get(0, 0) - }).toThrowError('Array not computed yet.') - }) - - it('simpleRangeValue', () => { - const ncArraySize = new ArraySize(3, 2) - const ncArray = new NotComputedArray(ncArraySize) - expect(() => { - ncArray.simpleRangeValue() - }).toThrowError('Array not computed yet.') - }) -}) diff --git a/test/unit/named-expressions.spec.ts b/test/unit/named-expressions.spec.ts deleted file mode 100644 index 774a8a372c..0000000000 --- a/test/unit/named-expressions.spec.ts +++ /dev/null @@ -1,2176 +0,0 @@ -import { - ExpectedValueOfTypeError, - ExportedCellChange, - ExportedNamedExpressionChange, - HyperFormula, - NamedExpressionDoesNotExistError, - NoSheetWithIdError, NotAFormulaError -} from '../../src' -import {AbsoluteCellRange} from '../../src/AbsoluteCellRange' -import {ErrorType} from '../../src' -import {Vertex} from '../../src/DependencyGraph' -import {ErrorMessage} from '../../src/error-message' -import {NoRelativeAddressesAllowedError} from '../../src' -import {adr, detailedError, expectArrayWithSameContent} from './testUtils' -import {Config} from '../../src/Config' -import {DependencyGraph} from '../../src/DependencyGraph' -import {Statistics} from '../../src/statistics' -import {FunctionRegistry} from '../../src/interpreter/FunctionRegistry' -import {LazilyTransformingAstService} from '../../src/LazilyTransformingAstService' -import {NamedExpressions} from '../../src/NamedExpressions' -import {Operations} from '../../src/Operations' -import {buildColumnSearchStrategy} from '../../src/Lookup/SearchStrategy' -import {CellContentParser} from '../../src/CellContentParser' -import {DateTimeHelper} from '../../src/DateTimeHelper' -import {NumberLiteralHelper} from '../../src/NumberLiteralHelper' -import {ParserWithCaching} from '../../src/parser' -import {ArraySizePredictor} from '../../src/ArraySize' - -describe('Named expressions - checking if its possible', () => { - it('should be possible to add named expression', () => { - const engine = HyperFormula.buildFromArray([]) - expect(engine.isItPossibleToAddNamedExpression('foo', '1')).toBe(true) - expect(engine.isItPossibleToAddNamedExpression('foo', 'foo')).toBe(true) - expect(engine.isItPossibleToAddNamedExpression('foo', null)).toBe(true) - expect(engine.isItPossibleToAddNamedExpression('foo', '=Sheet1!$A$1')).toBe(true) - expect(engine.isItPossibleToAddNamedExpression('foo', '=Sheet1!$A$1', 0)).toBe(true) - expect(engine.isItPossibleToAddNamedExpression('_A', 1)).toBe(true) - expect(engine.isItPossibleToAddNamedExpression('A', 1)).toBe(true) - expect(engine.isItPossibleToAddNamedExpression('Aa', 1)).toBe(true) - expect(engine.isItPossibleToAddNamedExpression('B.', 1)).toBe(true) - expect(engine.isItPossibleToAddNamedExpression('foo_bar', 1)).toBe(true) - expect(engine.isItPossibleToAddNamedExpression('A...', 1)).toBe(true) - expect(engine.isItPossibleToAddNamedExpression('B___', 1)).toBe(true) - }) - - it('no if expression name invalid', () => { - const engine = HyperFormula.buildFromArray([]) - expect(engine.isItPossibleToAddNamedExpression('A1', 'foo')).toBe(false) - }) - - it('no if scope does not exists', () => { - const engine = HyperFormula.buildFromArray([]) - expect(engine.isItPossibleToAddNamedExpression('foo', '=A1', 1)).toBe(false) - }) - - it('no if trying to add formula with relative references', () => { - const engine = HyperFormula.buildFromArray([]) - expect(engine.isItPossibleToAddNamedExpression('foo', '=A1')).toBe(false) - expect(engine.isItPossibleToAddNamedExpression('foo', '=A$1')).toBe(false) - expect(engine.isItPossibleToAddNamedExpression('foo', '=$A1')).toBe(false) - expect(engine.isItPossibleToAddNamedExpression('foo', '=Sheet1!A1:A2')).toBe(false) - }) - - it('should be possible to remove named expression', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addNamedExpression('foo', 'foo') - engine.addNamedExpression('bar', 'bar', 0) - expect(engine.isItPossibleToRemoveNamedExpression('foo')).toBe(true) - expect(engine.isItPossibleToRemoveNamedExpression('bar', 0)).toBe(true) - }) - - it('no if trying to remove not existing expression', () => { - const engine = HyperFormula.buildFromArray([]) - expect(engine.isItPossibleToRemoveNamedExpression('foo')).toBe(false) - }) - - it('no if trying to remove named expression from not existing scope', () => { - const engine = HyperFormula.buildFromArray([]) - expect(engine.isItPossibleToRemoveNamedExpression('foo', 1)).toBe(false) - }) - - it('should be possible to change named expression', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addNamedExpression('foo', 'foo') - engine.addNamedExpression('bar', 'bar', 0) - expect(engine.isItPossibleToChangeNamedExpression('foo', 'bar')).toBe(true) - expect(engine.isItPossibleToChangeNamedExpression('bar', 'baz', 0)).toBe(true) - }) - - it('no if trying to change to formula with relative references', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addNamedExpression('foo', 'foo') - expect(engine.isItPossibleToChangeNamedExpression('foo', '=A1')).toBe(false) - expect(engine.isItPossibleToChangeNamedExpression('foo', '=A$1')).toBe(false) - expect(engine.isItPossibleToChangeNamedExpression('foo', '=$A1')).toBe(false) - expect(engine.isItPossibleToChangeNamedExpression('foo', '=Sheet1!A1:A2')).toBe(false) - }) - - it('no if trying to change named expression in not existing scope', () => { - const engine = HyperFormula.buildFromArray([]) - expect(engine.isItPossibleToChangeNamedExpression('foo', '=A1', 1)).toBe(false) - }) - - it('no if trying to change not existing expression', () => { - const engine = HyperFormula.buildFromArray([]) - expect(engine.isItPossibleToChangeNamedExpression('foo', 'foo')).toBe(false) - }) -}) - -describe('Named expressions - name validity', () => { - describe('when created with addNamedExpression', () => { - it('name made of letters works', () => { - const name = 'ABC' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).not.toThrowError() - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name that starts with a valid cell reference works', () => { - const name = 'A9IOP' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).not.toThrowError() - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name that contains a valid cell reference works', () => { - const name = '___C42___' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).not.toThrowError() - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name that ends with a valid cell reference works', () => { - const name = '___C42' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).not.toThrowError() - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name that contains an underscore works', () => { - const name = 'HF2_2' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).not.toThrowError() - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name that contains a dot character works', () => { - const name = 'EXPR.2' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).not.toThrowError() - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name starts with an underscore works', () => { - const name = '___' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).not.toThrowError() - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name consisting of a underscore nad many dots works', () => { - const name = '_.......' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).not.toThrowError() - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name with unicode characters works', () => { - const name = 'ąęļćłó' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).not.toThrowError() - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('a one-character name works', () => { - const name = 'A' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).not.toThrowError() - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('a 1000-character name works', () => { - const name = 'A'.repeat(1000) - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).not.toThrowError() - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name that starts with a digit does not work', () => { - const name = '1definitelyIncorrectName' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).toThrowError(/Name .* is invalid/) - expect(engine.getCellValue(adr('A1', 0))).toEqualError(detailedError(ErrorType.ERROR, 'Parsing error.')) - }) - - it('name that starts with a dot character does not work', () => { - const name = '.EXPR' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).toThrowError(/Name .* is invalid/) - expect(engine.getCellValue(adr('A1', 0))).toEqualError(detailedError(ErrorType.ERROR, 'Parsing error.')) - }) - - it('name that contains a single quote character does not work', () => { - const name = "NAMED'EXPR" - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).toThrowError(/Name .* is invalid/) - expect(engine.getCellValue(adr('A1', 0))).toEqualError(detailedError(ErrorType.ERROR, 'Parsing error.')) - }) - - it('name that contains an exclamation mark does not work', () => { - const name = 'NAMED!EXPR' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).toThrowError(/Name .* is invalid/) - expect(engine.getCellValue(adr('A1', 0))).toEqualError(detailedError(ErrorType.ERROR, 'Parsing error.')) - }) - - it('name that is a valid cell reference does not work', () => { - const name = 'D42' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).toThrowError(/Name .* is invalid/) - expect(engine.getCellValue(adr('A1', 0))).toEqual(null) // not error bc it is treated as a regular cell ref - }) - - it('name that is a valid cell reference with 4-letter column address does not work', () => { - const name = 'ABCD42' - const engine = HyperFormula.buildFromArray([[`=${name}`]], { maxColumns: 500000 }) - - expect(() => engine.addNamedExpression(name, '=42')).toThrowError(/Name .* is invalid/) - expect(engine.getCellValue(adr('A1', 0))).toEqual(null) // not error bc it is treated as a regular cell ref - }) - - it('name that is a valid R1C1-style reference does not work', () => { - const name = 'R5C17' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).toThrowError(/Name .* is invalid/) - expect(engine.getCellValue(adr('A1', 0))).toEqualError(detailedError(ErrorType.ERROR, 'Parsing error.')) - }) - - it('name that is a R1C1-style reference with empty row number does not work', () => { - const name = 'RC17' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).toThrowError(/Name .* is invalid/) - expect(engine.getCellValue(adr('A1', 0))).toEqual(null) // not error bc it is treated as a regular cell ref - }) - - it('name that is a R1C1-style reference with empty column number does not work', () => { - const name = 'R5C' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).toThrowError(/Name .* is invalid/) - expect(engine.getCellValue(adr('A1', 0))).toEqualError(detailedError(ErrorType.ERROR, 'Parsing error.')) - }) - - it('name that is a R1C1-style reference with empty row and column numbers does not work', () => { - const name = 'RC' - const engine = HyperFormula.buildFromArray([[`=${name}`]]) - - expect(() => engine.addNamedExpression(name, '=42')).toThrowError(/Name .* is invalid/) - expect(engine.getCellValue(adr('A1', 0))).toEqualError(detailedError(ErrorType.ERROR, 'Parsing error.')) - }) - - it('validates characters which are allowed in name', () => { - const engine = HyperFormula.buildEmpty() - - expect(() => engine.addNamedExpression('1CantStartWithNumber', '=42')).toThrowError(/Name .* is invalid/) - expect(() => engine.addNamedExpression('Spaces Are Not Allowed', '=42')).toThrowError(/Name .* is invalid/) - expect(() => engine.addNamedExpression('.CantStartWithDot', '=42')).toThrowError(/Name .* is invalid/) - expect(() => engine.addNamedExpression('_CanStartWithUnderscore', '=42')).not.toThrowError() - expect(() => engine.addNamedExpression('dots.are.fine', '=42')).not.toThrowError() - expect(() => engine.addNamedExpression('underscores_are_fine', '=42')).not.toThrowError() - expect(() => engine.addNamedExpression('ś.zażółć.gęślą.jaźń.unicode.is.fine', '=42')).not.toThrowError() - expect(() => engine.addNamedExpression('If.It.Only.Has.Something.Like.Reference.Not.In.Beginning.Then.Its.Ok.A100', '=42')).not.toThrowError() - expect(() => engine.addNamedExpression('A100.Reference_Like_Substrings_In_The_Beginning_Are_Also_Fine', '=42')).not.toThrowError() - expect(() => engine.addNamedExpression('A100', '=42')).toThrowError(/Name .* is invalid/) - expect(() => engine.addNamedExpression('$A$50', '=42')).toThrowError(/Name .* is invalid/) - expect(() => engine.addNamedExpression('SheetName!$A$50', '=42')).toThrowError(/Name .* is invalid/) - }) - }) - - describe('when created on initialization', () => { - it('name made of letters works', () => { - const name = 'ABC' - const engine = HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }]) - - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name that starts with a valid cell reference works', () => { - const name = 'A9IOP' - const engine = HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }]) - - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name that contains a valid cell reference works', () => { - const name = '___C42___' - const engine = HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }]) - - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name that ends with a valid cell reference works', () => { - const name = '___C42' - const engine = HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }]) - - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name that contains an underscore works', () => { - const name = 'HF2_2' - const engine = HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }]) - - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name that contains a dot character works', () => { - const name = 'EXPR.2' - const engine = HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }]) - - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name starts with an underscore works', () => { - const name = '___' - const engine = HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }]) - - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name consisting of a underscore nad many dots works', () => { - const name = '_.......' - const engine = HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }]) - - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name with unicode characters works', () => { - const name = 'ąęļćłó' - const engine = HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }]) - - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('a one-character name works', () => { - const name = 'A' - const engine = HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }]) - - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('a 1000-character name works', () => { - const name = 'A'.repeat(1000) - const engine = HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }]) - - expect(engine.getCellValue(adr('A1', 0))).toEqual(42) - }) - - it('name that starts with a digit does not work', () => { - const name = '1definitelyIncorrectName' - - expect(() => HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }])).toThrowError(/Name .* is invalid/) - }) - - it('name that starts with a dot character does not work', () => { - const name = '.EXPR' - - expect(() => HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }])).toThrowError(/Name .* is invalid/) - }) - - it('name that contains a single quote character does not work', () => { - const name = "NAMED'EXPR" - - expect(() => HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }])).toThrowError(/Name .* is invalid/) - }) - - it('name that contains an exclamation mark does not work', () => { - const name = 'NAMED!EXPR' - - expect(() => HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }])).toThrowError(/Name .* is invalid/) - }) - - it('name that is a valid cell reference does not work', () => { - const name = 'D42' - - expect(() => HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }])).toThrowError(/Name .* is invalid/) - }) - - it('name that is a valid cell reference with 4-letter column address does not work', () => { - const name = 'ABCD42' - - expect(() => HyperFormula.buildFromArray([[`=${name}`]], { maxColumns: 500000 }, [{ name, expression: '=42' }])).toThrowError(/Name .* is invalid/) - }) - - it('name that is a valid R1C1-style reference does not work', () => { - const name = 'R5C17' - - expect(() => HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }])).toThrowError(/Name .* is invalid/) - }) - - it('name that is a R1C1-style reference with empty row number does not work', () => { - const name = 'RC17' - - expect(() => HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }])).toThrowError(/Name .* is invalid/) - }) - - it('name that is a R1C1-style reference with empty column number does not work', () => { - const name = 'R5C' - - expect(() => HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }])).toThrowError(/Name .* is invalid/) - }) - - it('name that is a R1C1-style reference with empty row and column numbers does not work', () => { - const name = 'RC' - - expect(() => HyperFormula.buildFromArray([[`=${name}`]], {}, [{ name, expression: '=42' }])).toThrowError(/Name .* is invalid/) - }) - - it('validates characters which are allowed in name', () => { - expect(() => HyperFormula.buildEmpty({}, [{ name: '1CantStartWithNumber', expression: '=42' }])).toThrowError(/Name .* is invalid/) - expect(() => HyperFormula.buildEmpty({}, [{ name: 'Spaces Are Not Allowed', expression: '=42' }])).toThrowError(/Name .* is invalid/) - expect(() => HyperFormula.buildEmpty({}, [{ name: '.CantStartWithDot', expression: '=42' }])).toThrowError(/Name .* is invalid/) - expect(() => HyperFormula.buildEmpty({}, [{ name: '_CanStartWithUnderscore', expression: '=42' }])).not.toThrowError() - expect(() => HyperFormula.buildEmpty({}, [{ name: 'dots.are.fine', expression: '=42' }])).not.toThrowError() - expect(() => HyperFormula.buildEmpty({}, [{ name: 'underscores_are_fine', expression: '=42' }])).not.toThrowError() - expect(() => HyperFormula.buildEmpty({}, [{ name: 'ś.zażółć.gęślą.jaźń.unicode.is.fine', expression: '=42' }])).not.toThrowError() - expect(() => HyperFormula.buildEmpty({}, [{ name: 'If.It.Only.Has.Something.Like.Reference.Not.In.Beginning.Then.Its.Ok.A100', expression: '=42' }])).not.toThrowError() - expect(() => HyperFormula.buildEmpty({}, [{ name: 'A100.Reference_Like_Substrings_In_The_Beginning_Are_Also_Fine', expression: '=42' }])).not.toThrowError() - expect(() => HyperFormula.buildEmpty({}, [{ name: 'A100', expression: '=42' }])).toThrowError(/Name .* is invalid/) - expect(() => HyperFormula.buildEmpty({}, [{ name: '$A$50', expression: '=42' }])).toThrowError(/Name .* is invalid/) - expect(() => HyperFormula.buildEmpty({}, [{ name: 'SheetName!$A$50', expression: '=42' }])).toThrowError(/Name .* is invalid/) - }) - }) -}) - -describe('Named expressions - absolute references only', () => { - describe('when created with addNamedExpression', () => { - it('adding named expression allows only for absolute addresses', () => { - const engine = HyperFormula.buildFromArray([]) - - expect(() => { - engine.addNamedExpression('foo', '=A1') - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - engine.addNamedExpression('foo', '=Sheet1!A1') - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - engine.addNamedExpression('foo', '=$A$1') - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - engine.addNamedExpression('foo', '=Sheet1!$A1') - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - engine.addNamedExpression('foo', '=Sheet1!A$1') - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - engine.addNamedExpression('foo', '=Sheet1!A1:A2') - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - engine.addNamedExpression('foo', '=Sheet1!A:B') - }).toThrow(new NoRelativeAddressesAllowedError()) - }) - - it('changing named expression allows only for absolute addresses', () => { - const engine = HyperFormula.buildFromArray([]) - - engine.addNamedExpression('foo', 'foo') - - expect(() => { - engine.changeNamedExpression('foo', '=A1') - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - engine.changeNamedExpression('foo', '=Sheet1!A1') - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - engine.changeNamedExpression('foo', '=$A$1') - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - engine.changeNamedExpression('foo', '=Sheet1!$A1') - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - engine.changeNamedExpression('foo', '=Sheet1!A$1') - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - engine.changeNamedExpression('foo', '=Sheet1!A1:A2') - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - engine.changeNamedExpression('foo', '=Sheet1!A:B') - }).toThrow(new NoRelativeAddressesAllowedError()) - }) - }) - - describe('when created on initialization', () => { - it('creating named expression allows only for absolute addresses', () => { - expect(() => { - HyperFormula.buildFromArray([], {}, [{ name: 'foo', expression: '=A1' }]) - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - HyperFormula.buildFromArray([], {}, [{ name: 'foo', expression: '=Sheet1!A1' }]) - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - HyperFormula.buildFromArray([], {}, [{ name: 'foo', expression: '=$A$1' }]) - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - HyperFormula.buildFromArray([], {}, [{ name: 'foo', expression: '=Sheet1!$A1' }]) - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - HyperFormula.buildFromArray([], {}, [{ name: 'foo', expression: '=Sheet1!A$1' }]) - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - HyperFormula.buildFromArray([], {}, [{ name: 'foo', expression: '=Sheet1!A1:A2' }]) - }).toThrow(new NoRelativeAddressesAllowedError()) - expect(() => { - HyperFormula.buildFromArray([], {}, [{ name: 'foo', expression: '=Sheet1!A:B' }]) - }).toThrow(new NoRelativeAddressesAllowedError()) - }) - }) -}) - -describe('Named expressions - store manipulation', () => { - describe('when created with addNamedExpression', () => { - it('basic usage with global named expression', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ]) - - engine.addNamedExpression('myName', '=Sheet1!$A$1+10') - - expect(engine.getNamedExpressionValue('myName')).toEqual(52) - }) - - it('using string expression', () => { - const engine = HyperFormula.buildEmpty() - - const changes = engine.addNamedExpression('myName', 'foobarbaz') - - expect(changes).toEqual([new ExportedNamedExpressionChange('myName', 'foobarbaz')]) - expect(engine.getNamedExpressionValue('myName')).toEqual('foobarbaz') - }) - - it('using number expression', () => { - const engine = HyperFormula.buildEmpty() - - const changes = engine.addNamedExpression('myName', '42') - - expect(changes).toEqual([new ExportedNamedExpressionChange('myName', 42)]) - expect(engine.getNamedExpressionValue('myName')).toEqual(42) - }) - - it('using empty expression', () => { - const engine = HyperFormula.buildEmpty() - - const changes = engine.addNamedExpression('myName', null) - - expect(changes).toEqual([new ExportedNamedExpressionChange('myName', null)]) - expect(engine.getNamedExpressionValue('myName')).toBe(null) - }) - - it('using native number as expression', () => { - const engine = HyperFormula.buildEmpty() - - const changes = engine.addNamedExpression('myName', 42) - - expect(changes).toEqual([new ExportedNamedExpressionChange('myName', 42)]) - expect(engine.getNamedExpressionValue('myName')).toEqual(42) - }) - - it('using native boolean as expression', () => { - const engine = HyperFormula.buildEmpty() - - const changes = engine.addNamedExpression('myName', true) - - expect(changes).toEqual([new ExportedNamedExpressionChange('myName', true)]) - expect(engine.getNamedExpressionValue('myName')).toEqual(true) - }) - - it('using error expression', () => { - const engine = HyperFormula.buildEmpty() - - const changes = engine.addNamedExpression('myName', '#VALUE!') - - expect(changes).toEqual([new ExportedNamedExpressionChange('myName', detailedError(ErrorType.VALUE))]) - expect(engine.getNamedExpressionValue('myName')).toEqualError(detailedError(ErrorType.VALUE)) - }) - - it('works for more formulas', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ]) - - engine.addNamedExpression('myName.1', '=Sheet1!$A$1+10') - engine.addNamedExpression('myName.2', '=Sheet1!$A$1+11') - - expect(engine.getNamedExpressionValue('myName.1')).toEqual(52) - expect(engine.getNamedExpressionValue('myName.2')).toEqual(53) - }) - - it('adding the same named expression twice on global level is forbidden', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addNamedExpression('myName', '=Sheet1!$A$1+10') - - expect(() => { - engine.addNamedExpression('myName', '=Sheet1!A1+10') - }).toThrowError('Name of Named Expression \'myName\' is already present') - }) - - it('adding the same named expression twice on local level is forbidden', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addNamedExpression('myName', '=Sheet1!$A$1+10', 0) - - expect(() => { - engine.addNamedExpression('myName', '=Sheet1!A1+10', 0) - }).toThrowError('Name of Named Expression \'myName\' is already present') - }) - - it('when adding named expression, matrix formulas are not accepted', () => { - const engine = HyperFormula.buildEmpty() - - expect(() => { - engine.addNamedExpression('myName', '=TRANSPOSE(A1:B2)') - }).toThrowError(/Relative addresses not allowed in named expressions./) - }) - - it('retrieving non-existing named expression', () => { - const engine = HyperFormula.buildEmpty() - - expect(engine.getNamedExpressionValue('nonExistentNameExpression')).toBe(undefined) - expect(engine.getNamedExpressionFormula('nonExistentNameExpression')).toBe(undefined) - }) - - it('removing named expression', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ]) - engine.addNamedExpression('myName', '=Sheet1!$A$1') - - engine.removeNamedExpression('myName') - - expect(engine.getNamedExpressionValue('myName')).toBe(undefined) - expect(engine.setCellContents(adr('A1'), '43').length).toBe(1) - }) - - it('removing local named expression', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ]) - engine.addNamedExpression('myName', '13') - engine.addNamedExpression('myName', '=Sheet1!$A$1', 0) - - engine.removeNamedExpression('myName', 0) - - expect(engine.getNamedExpressionValue('myName', 0)).toBe(undefined) - expect(engine.getNamedExpressionValue('myName')).toBe(13) - }) - - it('is possible to change named expression formula to other', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ]) - engine.addNamedExpression('myName', '=Sheet1!$A$1+10') - - engine.changeNamedExpression('myName', '=Sheet1!$A$1+11') - - expect(engine.getNamedExpressionValue('myName')).toEqual(53) - }) - - it('is possible to change named expression formula to other expression', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ]) - engine.addNamedExpression('myName', '=Sheet1!$A$1+10') - - engine.changeNamedExpression('myName', 58) - - expect(engine.getNamedExpressionValue('myName')).toEqual(58) - }) - - it('is possible to change named expression formula on local level', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ]) - engine.addNamedExpression('myName', '=100', 0) - - engine.changeNamedExpression('myName', '=200', 0) - - expect(engine.getNamedExpressionValue('myName', 0)).toEqual(200) - }) - - it('when changing named expression, matrices are not supported', () => { - const engine = HyperFormula.buildEmpty() - - engine.addNamedExpression('myName', '=42') - - expect(() => { - engine.changeNamedExpression('myName', '=TRANSPOSE(A1:B2)') - }).toThrowError(/Relative addresses not allowed in named expressions./) - }) - - it('changing not existing named expression', () => { - const engine = HyperFormula.buildEmpty() - - expect(() => { - engine.changeNamedExpression('myName', '=42') - }).toThrowError('Named Expression \'myName\' does not exist') - }) - - it('changing named expression from non existing sheet', () => { - const engine = HyperFormula.buildEmpty() - - expect(() => { - engine.changeNamedExpression('myName', '=42', 1) - }).toThrowError(NoSheetWithIdError) - }) - - it('listing named expressions', () => { - const engine = HyperFormula.buildEmpty() - engine.addNamedExpression('myName.1', '=42') - engine.addNamedExpression('myName.2', '=42') - - const namedExpressions = engine.listNamedExpressions() - - expect(namedExpressions).toEqual([ - 'myName.1', - 'myName.2', - ]) - }) - - it('listing scoped named expressions', () => { - const engine = HyperFormula.buildFromSheets({sheet1: [], sheet2: []}) - engine.addNamedExpression('myName.1', '=42', 0) - engine.addNamedExpression('myName.2', '=42', 1) - - const namedExpressions = engine.listNamedExpressions(1) - - expect(namedExpressions).toEqual([ - 'myName.2', - ]) - }) - - it('adding named expressions is case insensitive', () => { - const engine = HyperFormula.buildEmpty() - - engine.addNamedExpression('myName', '=42') - - expect(engine.getNamedExpressionValue('MYname')).toEqual(42) - expect(() => { - engine.changeNamedExpression('MYname', '=43') - }).not.toThrowError() - expect(() => { - engine.removeNamedExpression('MYname') - }).not.toThrowError() - }) - - it('allow even 255 character named expressions', () => { - const engine = HyperFormula.buildEmpty() - - const longExpressionName = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' - - expect(longExpressionName.length).toBe(255) - expect(() => { - engine.addNamedExpression(longExpressionName, '=42') - }).not.toThrowError() - }) - - it('#getNamedExpressionFormula when it exists', () => { - const engine = HyperFormula.buildFromArray([]) - - engine.addNamedExpression('myName.1', '=Sheet1!$A$1+10') - - expect(engine.getNamedExpressionFormula('myName.1')).toEqual('=Sheet1!$A$1+10') - }) - - it('#getNamedExpressionFormula when there is no such named expression', () => { - const engine = HyperFormula.buildFromArray([]) - - expect(engine.getNamedExpressionFormula('not.existing')).toBeUndefined() - }) - - it('#getNamedExpressionFormula when named expression is not formula', () => { - const engine = HyperFormula.buildFromArray([]) - - engine.addNamedExpression('myName.1', '42') - - expect(engine.getNamedExpressionFormula('myName.1')).toBeUndefined() - }) - - it('#getNamedExpressionFormula when there is no such sheet', () => { - const engine = HyperFormula.buildFromArray([]) - - expect(() => { - engine.getNamedExpressionFormula('myName.1', 1) - }).toThrowError(NoSheetWithIdError) - }) - - it('local level named expressions have separate storages', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ]) - - engine.addNamedExpression('myName', '=42') - engine.addNamedExpression('myName', '=13', 0) - - expect(engine.getNamedExpressionValue('myName')).toEqual(42) - expect(engine.getNamedExpressionValue('myName', 0)).toEqual(13) - expect(engine.getNamedExpressionFormula('myName')).toEqual('=42') - expect(engine.getNamedExpressionFormula('myName', 0)).toEqual('=13') - }) - - it('when trying to add named expression to nonexisting sheet', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ]) - - expect(() => { - engine.addNamedExpression('myName', '=13', 1) - }).toThrowError(NoSheetWithIdError) - }) - }) - - describe('when created on initialization', () => { - it('basic usage with global named expression', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ], {}, [{ name: 'myName', expression: '=Sheet1!$A$1+10' }]) - - expect(engine.getNamedExpressionValue('myName')).toEqual(52) - }) - - it('using string expression', () => { - const engine = HyperFormula.buildEmpty({}, [{ name: 'myName', expression: 'foobarbaz' }]) - - expect(engine.getNamedExpressionValue('myName')).toEqual('foobarbaz') - }) - - it('using number expression', () => { - const engine = HyperFormula.buildEmpty({}, [{ name: 'myName', expression: '42' }]) - - expect(engine.getNamedExpressionValue('myName')).toEqual(42) - }) - - it('using empty expression', () => { - const engine = HyperFormula.buildEmpty({}, [{ name: 'myName', expression: null }]) - - expect(engine.getNamedExpressionValue('myName')).toBe(null) - }) - - it('using native number as expression', () => { - const engine = HyperFormula.buildEmpty({}, [{ name: 'myName', expression: 42 }]) - - expect(engine.getNamedExpressionValue('myName')).toEqual(42) - }) - - it('using native boolean as expression', () => { - const engine = HyperFormula.buildEmpty({}, [{ name: 'myName', expression: true }]) - - expect(engine.getNamedExpressionValue('myName')).toEqual(true) - }) - - it('using error expression', () => { - const engine = HyperFormula.buildEmpty({}, [{ name: 'myName', expression: '#VALUE!' }]) - - expect(engine.getNamedExpressionValue('myName')).toEqualError(detailedError(ErrorType.VALUE)) - }) - - it('works for more formulas', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ], {}, [ - { name: 'myName.1', expression: '=Sheet1!$A$1+10' }, - { name: 'myName.2', expression: '=Sheet1!$A$1+11' } - ]) - - expect(engine.getNamedExpressionValue('myName.1')).toEqual(52) - expect(engine.getNamedExpressionValue('myName.2')).toEqual(53) - }) - - it('adding the same named expression twice on global level is forbidden', () => { - expect(() => { - HyperFormula.buildFromArray([], {}, [ - { name: 'myName', expression: '=Sheet1!$A$1+10' }, - { name: 'myName', expression: '=Sheet1!$A$1+11' } - ]) - }).toThrowError('Name of Named Expression \'myName\' is already present') - }) - - it('adding the same named expression twice on local level is forbidden', () => { - expect(() => { - HyperFormula.buildFromArray([], {}, [ - { name: 'myName', expression: '=Sheet1!$A$1+10', scope: 0 }, - { name: 'myName', expression: '=Sheet1!$A$1+11', scope: 0 } - ]) - }).toThrowError('Name of Named Expression \'myName\' is already present') - }) - - it('when creating named expression, matrix formulas are not accepted', () => { - expect(() => { - HyperFormula.buildEmpty({}, [{ name: 'myName', expression: '=TRANSPOSE(A1:B2)' }]) - }).toThrowError(/Relative addresses not allowed in named expressions./) - }) - - it('retrieving non-existing named expression', () => { - const engine = HyperFormula.buildEmpty() - - expect(engine.getNamedExpressionValue('nonExistentNameExpression')).toBe(undefined) - expect(engine.getNamedExpressionFormula('nonExistentNameExpression')).toBe(undefined) - }) - - it('listing named expressions', () => { - const engine = HyperFormula.buildEmpty({}, [ - { name: 'myName.1', expression: '=42' }, - { name: 'myName.2', expression: '=42' } - ]) - - const namedExpressions = engine.listNamedExpressions() - - expect(namedExpressions).toEqual([ - 'myName.1', - 'myName.2', - ]) - }) - - it('listing scoped named expressions', () => { - const engine = HyperFormula.buildFromSheets({sheet1: [], sheet2: []}, {}, [ - { name: 'myName.1', expression: '=42', scope: 0 }, - { name: 'myName.2', expression: '=42', scope: 1 } - ]) - - const namedExpressions = engine.listNamedExpressions(1) - - expect(namedExpressions).toEqual([ - 'myName.2', - ]) - }) - - it('named expressions are case insensitive', () => { - const engine = HyperFormula.buildEmpty({}, [{ name: 'myName', expression: '=42' }]) - - expect(engine.getNamedExpressionValue('MYname')).toEqual(42) - }) - - it('allow even 255 character named expressions', () => { - const longExpressionName = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' - - expect(longExpressionName.length).toBe(255) - expect(() => { - HyperFormula.buildEmpty({}, [{ name: longExpressionName, expression: '=42' }]) - }).not.toThrowError() - }) - - it('#getNamedExpressionFormula when it exists', () => { - const engine = HyperFormula.buildFromArray([], {}, [{ name: 'myName.1', expression: '=Sheet1!$A$1+10' }]) - - expect(engine.getNamedExpressionFormula('myName.1')).toEqual('=Sheet1!$A$1+10') - }) - - it('#getNamedExpressionFormula when there is no such named expression', () => { - const engine = HyperFormula.buildFromArray([]) - - expect(engine.getNamedExpressionFormula('not.existing')).toBeUndefined() - }) - - it('#getNamedExpressionFormula when named expression is not formula', () => { - const engine = HyperFormula.buildFromArray([], {}, [{ name: 'myName.1', expression: '42' }]) - - expect(engine.getNamedExpressionFormula('myName.1')).toBeUndefined() - }) - - it('#getNamedExpressionFormula when there is no such sheet', () => { - const engine = HyperFormula.buildFromArray([]) - - expect(() => { - engine.getNamedExpressionFormula('myName.1', 1) - }).toThrowError(NoSheetWithIdError) - }) - - it('local level named expressions have separate storages', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ], {}, [ - { name: 'myName', expression: '=42' }, - { name: 'myName', expression: '=13', scope: 0 } - ]) - - expect(engine.getNamedExpressionValue('myName')).toEqual(42) - expect(engine.getNamedExpressionValue('myName', 0)).toEqual(13) - expect(engine.getNamedExpressionFormula('myName')).toEqual('=42') - expect(engine.getNamedExpressionFormula('myName', 0)).toEqual('=13') - }) - - it('when trying to create named expression with nonexisting sheet scope', () => { - expect(() => { - HyperFormula.buildFromArray([ - ['42'], - ], {}, [{ name: 'myName', expression: '=13', scope: 1 }]) - }).toThrowError(NoSheetWithIdError) - }) - }) -}) - -/** - * A mock object for testing the dependency graph for the existence of the named expression vertex. - */ -const namedExpressionVertex = (engine: HyperFormula, expressionName: string, sheetId?: number): Vertex => { - let namedExpression - if (sheetId === undefined) { - namedExpression = engine.dependencyGraph.namedExpressions.workbookNamedExpressionOrPlaceholder(expressionName)! - } else { - namedExpression = engine.dependencyGraph.namedExpressions.namedExpressionForScope(expressionName, sheetId)! - } - return engine.dependencyGraph.fetchCell(namedExpression.address) -} - - describe('Named expressions - adding', () => { - describe('when created with addNamedExpression', () => { - it('doesn\'t throw when added named expression is used in a formula twice', () => { - const engine = HyperFormula.buildFromArray([ - ['55', '=age + age'] - ]) - - engine.addNamedExpression('age', '=Sheet1!$A$1', 0) - - expect(engine.getCellValue(adr('B1'))).toEqual(110) - }) - }) - - describe('when created on initialization', () => { - it('doesn\'t throw when named expression is used in a formula twice', () => { - const engine = HyperFormula.buildFromArray([ - ['55', '=age + age'] - ], {}, [{ name: 'age', expression: '=Sheet1!$A$1', scope: 0 }]) - - expect(engine.getCellValue(adr('B1'))).toEqual(110) - }) - }) - }) - -describe('Named expressions - evaluation', () => { - describe('when created with addNamedExpression', () => { - it('is recomputed', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ]) - engine.addNamedExpression('myName', '=Sheet1!$A$1+10') - - const changes = engine.setCellContents(adr('A1'), '20') - - expect(changes.length).toBe(2) - expect(changes).toContainEqual(new ExportedNamedExpressionChange('myName', 30)) - expect(engine.getNamedExpressionValue('myName')).toEqual(30) - }) - - it('is recomputed on every change', () => { - const hfInstance = HyperFormula.buildFromSheets( - { Sheet1: [[1]] }, - {} - ) - - hfInstance.addNamedExpression('test', '=Sheet1!$A$1') - - expect(hfInstance.getNamedExpressionValue('test')).toEqual(1) - - hfInstance.setCellContents({ sheet: 0, col: 0, row: 0 }, 2) - expect(hfInstance.getNamedExpressionValue('test')).toEqual(2) - - hfInstance.setCellContents({ sheet: 0, col: 0, row: 0 }, 3) - expect(hfInstance.getNamedExpressionValue('test')).toEqual(3) - - hfInstance.setCellContents({ sheet: 0, col: 0, row: 0 }, 4) - expect(hfInstance.getNamedExpressionValue('test')).toEqual(4) - }) - - it('is recomputed on every change (array formula)', () => { - const options = { - licenseKey: 'gpl-v3', - evaluateNullToZero: true, - } - - const hfInstance = HyperFormula.buildFromSheets( - { - Sheet1: [[1, 2, '=SUM(test)']], - }, - options - ) - - hfInstance.addNamedExpression('test', '=Sheet1!$A$1:$B$1') - - hfInstance.setCellContents({ sheet: 0, col: 0, row: 0 }, 4) - expect(hfInstance.getCellValue(adr('C1'))).toEqual(6) - - hfInstance.setCellContents({ sheet: 0, col: 0, row: 0 }, 14) - expect(hfInstance.getCellValue(adr('C1'))).toEqual(16) - }) - - it('should reevaluate volatile function in named expression', () => { - const engine = HyperFormula.buildFromArray([]) - - engine.addNamedExpression('volatileExpression', '=RAND()') - const valueBeforeRecomputation = engine.getNamedExpressionValue('volatileExpression') - - const changes = engine.setCellContents(adr('A1'), 'foo') - - const valueAfterRecomputation = engine.getNamedExpressionValue('volatileExpression') - expect(valueAfterRecomputation).not.toEqual(valueBeforeRecomputation) - expect(changes).toContainEqual(new ExportedCellChange(adr('A1'), 'foo')) - expect(changes).toContainEqual(new ExportedNamedExpressionChange('volatileExpression', valueAfterRecomputation!)) - }) - - it('adds edge to dependency', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addNamedExpression('FOO', '=42') - - engine.setCellContents(adr('A1'), '=FOO+10') - - const fooVertex = engine.dependencyGraph.fetchNamedExpressionVertex('FOO', 0).vertex - const a1 = engine.dependencyGraph.fetchCell(adr('A1')) - expect(engine.graph.existsEdge(fooVertex, a1)).toBe(true) - expect(engine.getCellValue(adr('A1'))).toEqual(52) - }) - - it('named expression dependency works if named expression was defined later', () => { - const engine = HyperFormula.buildFromArray([ - ['=FOO'] - ]) - - engine.addNamedExpression('FOO', '=42') - - const fooVertex = engine.dependencyGraph.fetchNamedExpressionVertex('FOO', 0).vertex - const a1 = engine.dependencyGraph.fetchCell(adr('A1')) - expect(engine.graph.existsEdge(fooVertex, a1)).toBe(true) - expect(engine.getCellValue(adr('A1'))).toEqual(42) - }) - - it('removed named expression returns NAME error', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addNamedExpression('FOO', '=42') - engine.setCellContents(adr('A1'), '=FOO+10') - - engine.removeNamedExpression('FOO') - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.NamedExpressionName('FOO'))) - }) - - it('removing node dependent on named expression', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addNamedExpression('FOO', '=42') - engine.setCellContents(adr('A1'), '=FOO+10') - - engine.setCellContents(adr('A1'), null) - - const fooVertex = engine.dependencyGraph.fetchNamedExpressionVertex('FOO', 0).vertex - expect(engine.graph.adjacentNodes(fooVertex).size).toBe(0) - }) - - it('named expression returns REF error after removing referenced sheet', () => { - const engine = HyperFormula.buildFromArray([ - ['=42'] - ]) - engine.addNamedExpression('FOO', '=Sheet1!$A$1 + 10') - - engine.removeSheet(engine.getSheetId('Sheet1')!) - - expect(engine.getNamedExpressionFormula('FOO')).toEqual('=Sheet1!$A$1 + 10') - expect(engine.getNamedExpressionValue('FOO')).toEqualError(detailedError(ErrorType.REF)) - }) - - it('local named expression shadows global one', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addNamedExpression('FOO', '=42') - engine.addNamedExpression('FOO', '=13', 0) - - engine.setCellContents(adr('A1'), '=FOO+10') - - const localFooVertex = engine.dependencyGraph.fetchNamedExpressionVertex('FOO', 0).vertex - const globalFooVertex = engine.dependencyGraph.fetchCell(engine.dependencyGraph.namedExpressions.namedExpressionForScope('FOO')!.address) - const a1 = engine.dependencyGraph.fetchCell(adr('A1')) - expect(engine.graph.existsEdge(localFooVertex, a1)).toBe(true) - expect(engine.graph.existsEdge(globalFooVertex, a1)).toBe(false) - expect(engine.getCellValue(adr('A1'))).toEqual(23) - }) - - it('removing local named expression binds all the edges to global one', () => { - const engine = HyperFormula.buildFromArray([[]]) - engine.addNamedExpression('foo', '10') - engine.addNamedExpression('foo', '20', 0) - engine.setCellContents(adr('A1'), [['=foo']]) - const localFooVertex = namedExpressionVertex(engine, 'foo', 0) - const globalFooVertex = namedExpressionVertex(engine, 'foo') - - engine.removeNamedExpression('foo', 0) - - const a1 = engine.dependencyGraph.fetchCell(adr('A1')) - expect(engine.graph.existsEdge(localFooVertex, a1)).toBe(false) - expect(engine.graph.existsEdge(globalFooVertex, a1)).toBe(true) - expect(engine.getCellValue(adr('A1'))).toEqual(10) - }) - - it('removing local named expression binds all the edges to global one even if it doesnt exist', () => { - const engine = HyperFormula.buildFromArray([[]]) - engine.addNamedExpression('foo', '20', 0) - engine.setCellContents(adr('A1'), [['=foo']]) - const localFooVertex = namedExpressionVertex(engine, 'foo', 0) - - engine.removeNamedExpression('foo', 0) - - const globalFooVertex = namedExpressionVertex(engine, 'foo') - const a1 = engine.dependencyGraph.fetchCell(adr('A1')) - expect(engine.graph.existsEdge(localFooVertex, a1)).toBe(false) - expect(engine.graph.existsEdge(globalFooVertex, a1)).toBe(true) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.NamedExpressionName('foo'))) - }) - - it('adding local named expression binds all the edges from global one', () => { - const engine = HyperFormula.buildFromArray([[]]) - engine.addNamedExpression('foo', '20') - engine.setCellContents(adr('A1'), [['=foo']]) - const globalFooVertex = namedExpressionVertex(engine, 'foo') - - engine.addNamedExpression('foo', '30', 0) - - const localFooVertex = namedExpressionVertex(engine, 'foo', 0) - const a1 = engine.dependencyGraph.fetchCell(adr('A1')) - expect(engine.graph.existsEdge(localFooVertex, a1)).toBe(true) - expect(engine.graph.existsEdge(globalFooVertex, a1)).toBe(false) - expect(engine.getCellValue(adr('A1'))).toEqual(30) - }) - }) - - describe('when created on initialization', () => { - it('is recomputed on every change', () => { - const hfInstance = HyperFormula.buildFromSheets( - { Sheet1: [[1]] }, - {}, - [{ name: 'test', expression: '=Sheet1!$A$1' }] - ) - - expect(hfInstance.getNamedExpressionValue('test')).toEqual(1) - - hfInstance.setCellContents({ sheet: 0, col: 0, row: 0 }, 2) - expect(hfInstance.getNamedExpressionValue('test')).toEqual(2) - - hfInstance.setCellContents({ sheet: 0, col: 0, row: 0 }, 3) - expect(hfInstance.getNamedExpressionValue('test')).toEqual(3) - - hfInstance.setCellContents({ sheet: 0, col: 0, row: 0 }, 4) - expect(hfInstance.getNamedExpressionValue('test')).toEqual(4) - }) - - it('is recomputed on every change (array formula)', () => { - const options = { - licenseKey: 'gpl-v3', - evaluateNullToZero: true, - } - - const hfInstance = HyperFormula.buildFromSheets( - { - Sheet1: [[1, 2, '=SUM(test)']], - }, - options, - [ - { - name: 'test', - expression: '=Sheet1!$A$1:$B$1', - }, - ] - ) - - hfInstance.setCellContents({ sheet: 0, col: 0, row: 0 }, 4) - expect(hfInstance.getCellValue(adr('C1'))).toEqual(6) - - hfInstance.setCellContents({ sheet: 0, col: 0, row: 0 }, 14) - expect(hfInstance.getCellValue(adr('C1'))).toEqual(16) - }) - - it('is recomputed', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ], {}, [{ name: 'myName', expression: '=Sheet1!$A$1+10' }]) - - const changes = engine.setCellContents(adr('A1'), '20') - - expect(changes.length).toBe(2) - expect(changes).toContainEqual(new ExportedNamedExpressionChange('myName', 30)) - expect(engine.getNamedExpressionValue('myName')).toEqual(30) - }) - - it('should reevaluate volatile function in named expression', () => { - const engine = HyperFormula.buildFromArray([], {}, [{ name: 'volatileExpression', expression: '=RAND()' }]) - - const valueBeforeRecomputation = engine.getNamedExpressionValue('volatileExpression') - - const changes = engine.setCellContents(adr('A1'), 'foo') - - const valueAfterRecomputation = engine.getNamedExpressionValue('volatileExpression') - expect(valueAfterRecomputation).not.toEqual(valueBeforeRecomputation) - expect(changes).toContainEqual(new ExportedCellChange(adr('A1'), 'foo')) - expect(changes).toContainEqual(new ExportedNamedExpressionChange('volatileExpression', valueAfterRecomputation!)) - }) - - it('adds edge to dependency', () => { - const engine = HyperFormula.buildFromArray([ - ['=FOO+10'] - ], {}, [{ name: 'FOO', expression: '=42' }]) - - const fooVertex = engine.dependencyGraph.fetchNamedExpressionVertex('FOO', 0).vertex - const a1 = engine.dependencyGraph.fetchCell(adr('A1')) - expect(engine.graph.existsEdge(fooVertex, a1)).toBe(true) - expect(engine.getCellValue(adr('A1'))).toEqual(52) - }) - - it('named expression returns REF error after removing referenced sheet', () => { - const engine = HyperFormula.buildFromArray([ - ['=42'] - ], {}, [{ name: 'FOO', expression: '=Sheet1!$A$1 + 10' }]) - - engine.removeSheet(0) - - expect(engine.getNamedExpressionFormula('FOO')).toEqual('=Sheet1!$A$1 + 10') - expect(engine.getNamedExpressionValue('FOO')).toEqualError(detailedError(ErrorType.REF)) - }) - - it('local named expression shadows global one', () => { - const engine = HyperFormula.buildFromArray([ - ['=FOO+10'] - ], {}, [ - { name: 'FOO', expression: '=42' }, - { name: 'FOO', expression: '=13', scope: 0 } - ]) - - const localFooVertex = engine.dependencyGraph.fetchNamedExpressionVertex('FOO', 0).vertex - const globalFooVertex = engine.dependencyGraph.fetchCell(engine.dependencyGraph.namedExpressions.namedExpressionForScope('FOO')!.address) - const a1 = engine.dependencyGraph.fetchCell(adr('A1')) - expect(engine.graph.existsEdge(localFooVertex, a1)).toBe(true) - expect(engine.graph.existsEdge(globalFooVertex, a1)).toBe(false) - expect(engine.getCellValue(adr('A1'))).toEqual(23) - }) - }) - - it('NAME error when there is no such named expression', () => { - const engine = HyperFormula.buildFromArray([ - ['=FOO'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.NamedExpressionName('FOO'))) - }) -}) - -describe('Named expressions - cross scope', () => { - describe('when created with addNamedExpression', () => { - it('should be possible to reference another sheet', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['foo']], - 'Sheet2': [['bar']] - }) - - engine.addNamedExpression('expr', '=Sheet2!$A$1', 0) - - expect(engine.getNamedExpressionValue('expr', 0)).toEqual('bar') - }) - - it('should be possible to add named expressions with same name to two different scopes', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['foo', '=expr']], - 'Sheet2': [['bar', '=expr']] - }) - - engine.addNamedExpression('expr', '=Sheet1!$A$1', 0) - engine.addNamedExpression('expr', '=Sheet2!$A$1', 1) - - expect(engine.getCellValue(adr('B1'))).toEqual('foo') - expect(engine.getCellValue(adr('B1', 1))).toEqual('bar') - }) - - it('should be possible to access named expression only from its scope', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['foo', '=expr']], - 'Sheet2': [['bar', '=expr']] - }) - - engine.addNamedExpression('expr', '=Sheet1!$A$1', 0) - - expect(engine.getCellValue(adr('B1'))).toEqual('foo') - expect(engine.getCellValue(adr('B1', 1))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.NamedExpressionName('expr'))) - }) - - it('should add named expression to global scope when moving formula to other sheet', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['foo', '=expr']], - 'Sheet2': [['bar']] - }) - - engine.addNamedExpression('expr', '=Sheet1!$A$1', 0) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('B1'), 1, 1), adr('B1', 1)) - - expect(engine.getNamedExpressionFormula('expr', 0)).toEqual('=Sheet1!$A$1') - expect(engine.getNamedExpressionFormula('expr')).toEqual('=Sheet1!$A$1') - expect(engine.getCellValue(adr('B1', 0))).toBe(null) - expect(engine.getCellValue(adr('B1', 1))).toEqual('foo') - }) - - it('should add named expression to global scope when cut pasting formula to other sheet', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['foo', '=expr']], - 'Sheet2': [['bar']] - }) - - engine.addNamedExpression('expr', '=Sheet1!$A$1', 0) - - engine.cut(AbsoluteCellRange.spanFrom(adr('B1'), 1, 1)) - engine.paste(adr('B1', 1)) - - expect(engine.getNamedExpressionFormula('expr', 0)).toEqual('=Sheet1!$A$1') - expect(engine.getNamedExpressionFormula('expr')).toEqual('=Sheet1!$A$1') - expect(engine.getCellValue(adr('B1', 0))).toBe(null) - expect(engine.getCellValue(adr('B1', 1))).toEqual('foo') - }) - - it('should add named expression to global scope when copying formula to other sheet', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['foo', '=expr']], - 'Sheet2': [['bar']] - }) - - engine.addNamedExpression('expr', '=Sheet1!$A$1', 0) - - engine.copy(AbsoluteCellRange.spanFrom(adr('B1'), 1, 1)) - engine.paste(adr('B1', 1)) - - expect(engine.getNamedExpressionFormula('expr', 0)).toEqual('=Sheet1!$A$1') - expect(engine.getNamedExpressionFormula('expr')).toEqual('=Sheet1!$A$1') - expect(engine.getCellValue(adr('B1', 0))).toEqual('foo') - expect(engine.getCellValue(adr('B1', 1))).toEqual('foo') - }) - - it('should add named expression to global scope even if cell was modified before pasting', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['foo', '=expr']], - 'Sheet2': [['bar']] - }) - - engine.addNamedExpression('expr', '=Sheet1!$A$1', 0) - - engine.copy(AbsoluteCellRange.spanFrom(adr('B1'), 1, 1)) - engine.setCellContents(adr('B1'), [['baz']]) - engine.paste(adr('B1', 1)) - - expect(engine.getNamedExpressionFormula('expr', 0)).toEqual('=Sheet1!$A$1') - expect(engine.getNamedExpressionFormula('expr')).toEqual('=Sheet1!$A$1') - expect(engine.getCellValue(adr('B1', 0))).toEqual('baz') - expect(engine.getCellValue(adr('B1', 1))).toEqual('foo') - }) - - it('should use already existing named expression in other sheet when moving formula', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['foo', '=expr']], - 'Sheet2': [['bar']] - }) - - engine.addNamedExpression('expr', '=Sheet1!$A$1', 0) - engine.addNamedExpression('expr', '=Sheet2!$A$1', 1) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('B1'), 1, 1), adr('B1', 1)) - - expect(engine.getNamedExpressionFormula('expr')).toEqual(undefined) - expect(engine.getNamedExpressionFormula('expr', 0)).toEqual('=Sheet1!$A$1') - expect(engine.getNamedExpressionFormula('expr', 1)).toEqual('=Sheet2!$A$1') - expect(engine.getCellValue(adr('B1', 0))).toBe(null) - expect(engine.getCellValue(adr('B1', 1))).toEqual('bar') - // ensure edges are correct - const sourceScopeNEVertex = engine.dependencyGraph.fetchNamedExpressionVertex('expr', 0).vertex - const targetScopeNEVertex = engine.dependencyGraph.fetchNamedExpressionVertex('expr', 1).vertex - const targetFormulaVertex = engine.dependencyGraph.getCell(adr('B1', 1))! - expect(engine.dependencyGraph.existsEdge(sourceScopeNEVertex, targetFormulaVertex)).toBe(false) - expect(engine.dependencyGraph.existsEdge(targetScopeNEVertex, targetFormulaVertex)).toBe(true) - }) - - it('should use already existing named expression in other sheet when cut pasting formula', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['foo', '=expr']], - 'Sheet2': [['bar']] - }) - - engine.addNamedExpression('expr', '=Sheet1!$A$1', 0) - engine.addNamedExpression('expr', '=Sheet2!$A$1', 1) - - engine.cut(AbsoluteCellRange.spanFrom(adr('B1'), 1, 1)) - engine.paste(adr('B1', 1)) - - expect(engine.getNamedExpressionFormula('expr')).toEqual(undefined) - expect(engine.getNamedExpressionFormula('expr', 0)).toEqual('=Sheet1!$A$1') - expect(engine.getNamedExpressionFormula('expr', 1)).toEqual('=Sheet2!$A$1') - expect(engine.getCellValue(adr('B1', 0))).toBe(null) - expect(engine.getCellValue(adr('B1', 1))).toEqual('bar') - // ensure edges are correct - const sourceScopeNEVertex = engine.dependencyGraph.fetchNamedExpressionVertex('expr', 0).vertex - const targetScopeNEVertex = engine.dependencyGraph.fetchNamedExpressionVertex('expr', 1).vertex - const targetFormulaVertex = engine.dependencyGraph.getCell(adr('B1', 1))! - expect(engine.dependencyGraph.existsEdge(sourceScopeNEVertex, targetFormulaVertex)).toBe(false) - expect(engine.dependencyGraph.existsEdge(targetScopeNEVertex, targetFormulaVertex)).toBe(true) - }) - - it('should use already existing named expression in other sheet when copying formula', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['foo', '=expr']], - 'Sheet2': [['bar']] - }) - - engine.addNamedExpression('expr', '=Sheet1!$A$1', 0) - engine.addNamedExpression('expr', '=Sheet2!$A$1', 1) - - engine.copy(AbsoluteCellRange.spanFrom(adr('B1'), 1, 1)) - engine.paste(adr('B1', 1)) - - expect(engine.getNamedExpressionFormula('expr')).toEqual(undefined) - expect(engine.getNamedExpressionFormula('expr', 0)).toEqual('=Sheet1!$A$1') - expect(engine.getNamedExpressionFormula('expr', 1)).toEqual('=Sheet2!$A$1') - expect(engine.getCellValue(adr('B1', 0))).toEqual('foo') - expect(engine.getCellValue(adr('B1', 1))).toEqual('bar') - // ensure edges are correct - const sourceScopeNEVertex = engine.dependencyGraph.fetchNamedExpressionVertex('expr', 0).vertex - const targetScopeNEVertex = engine.dependencyGraph.fetchNamedExpressionVertex('expr', 1).vertex - const targetFormulaVertex = engine.dependencyGraph.getCell(adr('B1', 1))! - expect(engine.dependencyGraph.existsEdge(sourceScopeNEVertex, targetFormulaVertex)).toBe(false) - expect(engine.dependencyGraph.existsEdge(targetScopeNEVertex, targetFormulaVertex)).toBe(true) - }) - }) - - describe('when created on initialization', () => { - it('should be possible to reference another sheet', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['foo']], - 'Sheet2': [['bar']] - }, {}, [{ name: 'expr', expression: '=Sheet2!$A$1', scope: 0 }]) - - expect(engine.getNamedExpressionValue('expr', 0)).toEqual('bar') - }) - - it('should be possible to create named expressions with same name in two different scopes', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['foo', '=expr']], - 'Sheet2': [['bar', '=expr']] - }, {}, [ - { name: 'expr', expression: '=Sheet1!$A$1', scope: 0 }, - { name: 'expr', expression: '=Sheet2!$A$1', scope: 1 } - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual('foo') - expect(engine.getCellValue(adr('B1', 1))).toEqual('bar') - }) - - it('should be possible to access named expression only from its scope', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['foo', '=expr']], - 'Sheet2': [['bar', '=expr']] - }, {}, [{ name: 'expr', expression: '=Sheet1!$A$1', scope: 0 }]) - - expect(engine.getCellValue(adr('B1'))).toEqual('foo') - expect(engine.getCellValue(adr('B1', 1))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.NamedExpressionName('expr'))) - }) - }) -}) - -describe('Named expressions - named ranges', () => { - describe('when created with addNamedExpression', () => { - it('should be possible to define simple range in named expression', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['3'], - ]) - - engine.addNamedExpression('fooo', '=Sheet1!$A$1:Sheet1!$A$2') - engine.setCellContents(adr('B1'), [['=SUM(fooo)']]) - - expect(engine.getCellValue(adr('B1'))).toEqual(4) - }) - - it('should be possible to define column range in named expression', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['3'], - ]) - - engine.addNamedExpression('foo', '=Sheet1!$A:Sheet1!$A') - engine.addNamedExpression('bar', '=Sheet1!$A:$A') - engine.setCellContents(adr('B1'), [['=SUM(foo)']]) - engine.setCellContents(adr('B2'), [['=SUM(bar)']]) - - expect(engine.getCellValue(adr('B1'))).toEqual(4) - expect(engine.getCellValue(adr('B2'))).toEqual(4) - }) - - it('should recalculate when named range changes definition', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ]) - - engine.addNamedExpression('fooo', '=Sheet1!$A:Sheet1!$A') - engine.setCellContents(adr('C1'), [['=SUM(fooo)']]) - engine.changeNamedExpression('fooo', '=Sheet1!$B:Sheet1!$B') - - expect(engine.getCellValue(adr('C1'))).toEqual(6) - }) - - it('should return array value of named expression', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'], - ]) - - const changes = engine.addNamedExpression('fooo', '=TRANSPOSE(Sheet1!$A$1:Sheet1!$B$2)') - - expect(changes).toContainEqual(new ExportedNamedExpressionChange('fooo', [[1, 3], [2, 4]])) - }) - - it('should update automatically when row is added to the referenced range', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2], - [3, 4], - ]) - - engine.addNamedExpression('range', '=Sheet1!$A$1:$B$2') - expect(engine.getNamedExpressionFormula('range')).toEqual('=Sheet1!$A$1:$B$2') - - engine.addRows(0, [1, 1]) - expect(engine.getSheetValues(0)).toEqual([[1, 2], [], [3, 4]]) - expect(engine.getNamedExpressionFormula('range')).toEqual('=Sheet1!$A$1:$B$3') - }) - - it('should update automatically when column is added to the referenced range', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2], - [3, 4], - ]) - - engine.addNamedExpression('range', '=Sheet1!$A$1:$B$2') - expect(engine.getNamedExpressionFormula('range')).toEqual('=Sheet1!$A$1:$B$2') - - engine.addColumns(0, [1, 1]) - expect(engine.getSheetValues(0)).toEqual([[1, null, 2], [3, null, 4]]) - expect(engine.getNamedExpressionFormula('range')).toEqual('=Sheet1!$A$1:$C$2') - }) - }) - - describe('when created on initialization', () => { - it('should be possible to define simple range in named expression', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=SUM(fooo)'], - ['3'], - ], {}, [{ name: 'fooo', expression: '=Sheet1!$A$1:Sheet1!$A$2' }]) - - expect(engine.getCellValue(adr('B1'))).toEqual(4) - }) - - it('should be possible to define column range in named expression', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=SUM(foo)', '=SUM(bar)'], - ['3'], - ], {}, [ - { name: 'foo', expression: '=Sheet1!$A:Sheet1!$A' }, - { name: 'bar', expression: '=Sheet1!$A:$A' } - ]) - - expect(engine.getCellValue(adr('B1'))).toEqual(4) - expect(engine.getCellValue(adr('C1'))).toEqual(4) - }) - - it('should update automatically when row is added to the referenced range', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2], - [3, 4], - ], {}, [{ name: 'range', expression: '=Sheet1!$A$1:$B$2' }]) - expect(engine.getNamedExpressionFormula('range')).toEqual('=Sheet1!$A$1:$B$2') - - engine.addRows(0, [1, 1]) - expect(engine.getSheetValues(0)).toEqual([[1, 2], [], [3, 4]]) - expect(engine.getNamedExpressionFormula('range')).toEqual('=Sheet1!$A$1:$B$3') - }) - - it('should update automatically when column is added to the referenced range', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2], - [3, 4], - ], {}, [{ name: 'range', expression: '=Sheet1!$A$1:$B$2' }]) - expect(engine.getNamedExpressionFormula('range')).toEqual('=Sheet1!$A$1:$B$2') - - engine.addColumns(0, [1, 1]) - expect(engine.getSheetValues(0)).toEqual([[1, null, 2], [3, null, 4]]) - expect(engine.getNamedExpressionFormula('range')).toEqual('=Sheet1!$A$1:$C$2') - }) - }) -}) - -describe('Named expressions - options', () => { - describe('when created with addNamedExpression', () => { - it('should return named expression with empty options', () => { - const engine = HyperFormula.buildEmpty() - - engine.addNamedExpression('foo', '=foo') - - expect(engine.getNamedExpression('foo')).toEqual({ - name: 'foo', - expression: '=foo', - scope: undefined, - options: undefined - }) - }) - - it('should return named expression with empty options - scope provided', () => { - const engine = HyperFormula.buildFromArray([[]]) - - engine.addNamedExpression('foo', '=foo', 0) - - expect(engine.getNamedExpression('foo', 0)).toEqual({ - name: 'foo', - expression: '=foo', - scope: 0, - options: undefined - }) - }) - - it('should return undefined for non-existent named expression', () => { - const engine = HyperFormula.buildFromArray([[]]) - - engine.addNamedExpression('foo', '=foo') - - expect(engine.getNamedExpression('foo-bar')).toEqual(undefined) - }) - - it('should return named expression with options', () => { - const engine = HyperFormula.buildEmpty() - - engine.addNamedExpression('foo', '=foo', undefined, {visible: false, comment: 'bar'}) - - expect(engine.getNamedExpression('foo')).toEqual({ - name: 'foo', - expression: '=foo', - scope: undefined, - options: { - visible: false, - comment: 'bar' - } - }) - }) - - it('should preserve options after undo-redo', () => { - const engine = HyperFormula.buildEmpty() - - engine.addNamedExpression('foo', '=foo', undefined, {visible: false, comment: 'bar'}) - - engine.undo() - engine.redo() - - expect(engine.getNamedExpression('foo')).toEqual({ - name: 'foo', - expression: '=foo', - scope: undefined, - options: { - visible: false, - comment: 'bar' - } - }) - }) - - it('should change options of named expression', () => { - const engine = HyperFormula.buildEmpty() - - engine.addNamedExpression('foo', '=foo', undefined, {visible: false, comment: 'foo'}) - - engine.changeNamedExpression('foo', '=bar', undefined, {visible: true, comment: 'bar'}) - - expect(engine.getNamedExpression('foo')).toEqual({ - name: 'foo', - expression: '=bar', - scope: undefined, - options: { - visible: true, - comment: 'bar' - } - }) - }) - - it('should undo changing options of named expression', () => { - const engine = HyperFormula.buildEmpty() - - engine.addNamedExpression('foo', '=foo', undefined, {visible: false, comment: 'foo'}) - engine.changeNamedExpression('foo', '=bar', undefined, {visible: true, comment: 'bar'}) - - engine.undo() - - expect(engine.getNamedExpression('foo')).toEqual({ - name: 'foo', - expression: '=foo', - scope: undefined, - options: { - visible: false, - comment: 'foo' - } - }) - }) - - it('should undo-redo changing options of named expression', () => { - const engine = HyperFormula.buildEmpty() - - engine.addNamedExpression('foo', '=foo', undefined, {visible: false, comment: 'foo'}) - engine.changeNamedExpression('foo', '=bar', undefined, {visible: true, comment: 'bar'}) - - engine.undo() - engine.redo() - - expect(engine.getNamedExpression('foo')).toEqual({ - name: 'foo', - expression: '=bar', - scope: undefined, - options: { - visible: true, - comment: 'bar' - } - }) - }) - - it('should restore named expression with options', () => { - const engine = HyperFormula.buildEmpty() - - engine.addNamedExpression('foo', '=foo', undefined, {visible: false, comment: 'foo'}) - engine.removeNamedExpression('foo') - - engine.undo() - - expect(engine.getNamedExpression('foo')).toEqual({ - name: 'foo', - expression: '=foo', - scope: undefined, - options: { - visible: false, - comment: 'foo' - } - }) - }) - }) - - describe('when created on initialization', () => { - it('should return named expression with empty options', () => { - const engine = HyperFormula.buildEmpty({}, [{ name: 'foo', expression: '=foo' }]) - - expect(engine.getNamedExpression('foo')).toEqual({ - name: 'foo', - expression: '=foo', - scope: undefined, - options: undefined - }) - }) - - it('should return named expression with empty options - scope provided', () => { - const engine = HyperFormula.buildFromArray([[]], {}, [{ name: 'foo', expression: '=foo', scope: 0 }]) - - expect(engine.getNamedExpression('foo', 0)).toEqual({ - name: 'foo', - expression: '=foo', - scope: 0, - options: undefined - }) - }) - - it('should return undefined for non-existent named expression', () => { - const engine = HyperFormula.buildFromArray([[]], {}, [{ name: 'foo', expression: '=foo' }]) - - expect(engine.getNamedExpression('foo-bar')).toEqual(undefined) - }) - - it('should return named expression with options', () => { - const engine = HyperFormula.buildEmpty({}, [{ name: 'foo', expression: '=foo', options: {visible: false, comment: 'bar'} }]) - - expect(engine.getNamedExpression('foo')).toEqual({ - name: 'foo', - expression: '=foo', - scope: undefined, - options: { - visible: false, - comment: 'bar' - } - }) - }) - }) -}) - -describe('Named expressions - actions at the Operations layer', () => { - - let operations: Operations - - beforeEach(() => { - const config = new Config() - const stats = new Statistics() - const namedExpressions = new NamedExpressions() - const functionRegistry = new FunctionRegistry(config) - const lazilyTransformingAstService = new LazilyTransformingAstService(stats) - const dependencyGraph = DependencyGraph.buildEmpty(lazilyTransformingAstService, config, functionRegistry, namedExpressions, stats) - const columnSearch = buildColumnSearchStrategy(dependencyGraph, config, stats) - const dateTimeHelper = new DateTimeHelper(config) - const numberLiteralHelper = new NumberLiteralHelper(config) - const cellContentParser = new CellContentParser(config, dateTimeHelper, numberLiteralHelper) - const parser = new ParserWithCaching( - config, - functionRegistry, - dependencyGraph.sheetReferenceRegistrar.ensureSheetRegistered.bind(dependencyGraph.sheetReferenceRegistrar) - ) - const arraySizePredictor = new ArraySizePredictor(config, functionRegistry) - operations = new Operations(config, dependencyGraph, columnSearch, cellContentParser, parser, stats, lazilyTransformingAstService, namedExpressions, arraySizePredictor) - }) - - it('should throw an error if you try and change an unknown named expression', () => { - operations.addNamedExpression('foo', 'foo') - const unknownNamedExpression = 'bar' - expect(() => { - operations.changeNamedExpressionExpression(unknownNamedExpression, '=125') - }).toThrow(new NamedExpressionDoesNotExistError(unknownNamedExpression)) - }) - - it('should throw an error if you try and change the expression to one that contains relative references', () => { - operations.addNamedExpression('foo', 'foo') - expect(() => { - operations.changeNamedExpressionExpression('foo', '=A2') - }).toThrow(new NoRelativeAddressesAllowedError()) - }) -}) - -describe('nested named expressions', () => { - describe('when created with addNamedExpression', () => { - it('should work', () => { - const engine = HyperFormula.buildFromArray([['=ABCD']]) - engine.addNamedExpression('ABCD', '=EFGH') - engine.addNamedExpression('EFGH', 1) - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - }) - - describe('when created on initialization', () => { - it('should work', () => { - const engine = HyperFormula.buildFromArray([['=ABCD']], {}, [ - { name: 'ABCD', expression: '=EFGH' }, - { name: 'EFGH', expression: 1 } - ]) - expect(engine.getCellValue(adr('A1'))).toEqual(1) - }) - }) -}) - -describe('serialization', () => { - describe('when created with addNamedExpression', () => { - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ['50'], - ['60']]) - engine.addNamedExpression('prettyName', '=Sheet1!$A$1+100') - engine.addNamedExpression('anotherPrettyName', '=Sheet1!$A$2+100') - engine.addNamedExpression('alsoPrettyName', '=Sheet1!$A$3+100', 0) - expect(engine.getAllNamedExpressionsSerialized()).toEqual([ - {name: 'prettyName', expression: '=Sheet1!$A$1+100', options: undefined, scope: undefined}, - {name: 'anotherPrettyName', expression: '=Sheet1!$A$2+100', options: undefined, scope: undefined}, - {name: 'alsoPrettyName', expression: '=Sheet1!$A$3+100', options: undefined, scope: 0} - ]) - }) - - it('should update scopes', () => { - const engine = HyperFormula.buildFromSheets({sheet1: [[]], sheet2: [[]], sheet3: [[]]}) - engine.addNamedExpression('prettyName', '=1', 0) - engine.addNamedExpression('anotherPrettyName', '=2', 1) - engine.addNamedExpression('alsoPrettyName', '=3', 2) - engine.removeSheet(1) - expect(engine.getAllNamedExpressionsSerialized()).toEqual([ - {name: 'prettyName', expression: '=1', scope: 0, options: undefined}, - {name: 'alsoPrettyName', expression: '=3', scope: 1, options: undefined} - ]) - }) - }) - - describe('when created on initialization', () => { - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ['50'], - ['60']], {}, [ - { name: 'prettyName', expression: '=Sheet1!$A$1+100' }, - { name: 'anotherPrettyName', expression: '=Sheet1!$A$2+100' }, - { name: 'alsoPrettyName', expression: '=Sheet1!$A$3+100', scope: 0 } - ]) - expect(engine.getAllNamedExpressionsSerialized()).toEqual([ - {name: 'prettyName', expression: '=Sheet1!$A$1+100', options: undefined, scope: undefined}, - {name: 'anotherPrettyName', expression: '=Sheet1!$A$2+100', options: undefined, scope: undefined}, - {name: 'alsoPrettyName', expression: '=Sheet1!$A$3+100', options: undefined, scope: 0} - ]) - }) - - it('should update scopes', () => { - const engine = HyperFormula.buildFromSheets({sheet1: [[]], sheet2: [[]], sheet3: [[]]}, {}, [ - { name: 'prettyName', expression: '=1', scope: 0 }, - { name: 'anotherPrettyName', expression: '=2', scope: 1 }, - { name: 'alsoPrettyName', expression: '=3', scope: 2 } - ]) - engine.removeSheet(1) - expect(engine.getAllNamedExpressionsSerialized()).toEqual([ - {name: 'prettyName', expression: '=1', scope: 0, options: undefined}, - {name: 'alsoPrettyName', expression: '=3', scope: 1, options: undefined} - ]) - }) - }) -}) - -describe('getNamedExpressionsFromFormula method', () => { - describe('when created with addNamedExpression', () => { - it('should return an empty array when called with a formula that has no named expressions', () => { - const engine = HyperFormula.buildEmpty() - engine.addNamedExpression('foo', '=42') - - expect(engine.getNamedExpressionsFromFormula('="test"')).toEqual([]) - }) - - it('should return the existing named expressions', () => { - const engine = HyperFormula.buildEmpty() - engine.addNamedExpression('foo', '=42') - engine.addNamedExpression('bar', '=42') - - expectArrayWithSameContent(engine.getNamedExpressionsFromFormula('=foo+bar*2'), ['foo', 'bar']) - }) - - it('should return the non-existing named expressions', () => { - const engine = HyperFormula.buildEmpty() - engine.addNamedExpression('foo', '=42') - - expectArrayWithSameContent(engine.getNamedExpressionsFromFormula('=bar+baz*2'), ['bar', 'baz']) - }) - - it('should return each named expression only once', () => { - const engine = HyperFormula.buildEmpty() - engine.addNamedExpression('foo', '=42') - - expect(engine.getNamedExpressionsFromFormula('=foo+foo*2')).toEqual(['foo']) - }) - - it('should throw the ExpectedValueOfTypeError exception for input of wrong type', () => { - const engine = HyperFormula.buildEmpty() - engine.addNamedExpression('foo', '=42') - - expect(() => engine.getNamedExpressionsFromFormula(42 as any)).toThrow(new ExpectedValueOfTypeError('string', 'formulaString')) - }) - - it('should throw the NotAFormulaError exception for a string that doesn\'t start with "="', () => { - const engine = HyperFormula.buildEmpty() - engine.addNamedExpression('foo', '=42') - - expect(() => engine.getNamedExpressionsFromFormula('foo')).toThrow(new NotAFormulaError()) - }) - - it('should throw an exception for empty formula', () => { - const engine = HyperFormula.buildEmpty() - engine.addNamedExpression('foo', '=42') - - expect(() => engine.getNamedExpressionsFromFormula('')).toThrow(new NotAFormulaError()) - }) - - it('should return an empty array when called with a formula with an error literal', () => { - const engine = HyperFormula.buildEmpty() - engine.addNamedExpression('foo', '=42') - - expect(engine.getNamedExpressionsFromFormula('=#VALUE!')).toEqual([]) - }) - - it('should throw the NotAFormulaError exception for an unparsable formula', () => { - const engine = HyperFormula.buildEmpty() - engine.addNamedExpression('foo', '=42') - - expect(() => engine.getNamedExpressionsFromFormula('=#FOO!')).toThrow(new NotAFormulaError()) - expect(() => engine.getNamedExpressionsFromFormula('=100%%*foo')).toThrow(new NotAFormulaError()) - expect(() => engine.getNamedExpressionsFromFormula('=@foo')).toThrow(new NotAFormulaError()) - expect(() => engine.getNamedExpressionsFromFormula("=foo'bar")).toThrow(new NotAFormulaError()) - expect(() => engine.getNamedExpressionsFromFormula('=\u00A0foo')).toThrow(new NotAFormulaError()) - }) - }) - - describe('when created on initialization', () => { - it('should return an empty array when called with a formula that has no named expressions', () => { - const engine = HyperFormula.buildEmpty({}, [ - { name: 'foo', expression: '=42' }, - ]) - - expect(engine.getNamedExpressionsFromFormula('="test"')).toEqual([]) - }) - - it('should return the existing named expressions', () => { - const engine = HyperFormula.buildEmpty({}, [ - { name: 'foo', expression: '=42' }, - { name: 'bar', expression: '=42' }, - ]) - - expectArrayWithSameContent(engine.getNamedExpressionsFromFormula('=foo+bar*2'), ['foo', 'bar']) - }) - - it('should return the non-existing named expressions', () => { - const engine = HyperFormula.buildEmpty({}, [ - { name: 'foo', expression: '=42' }, - ]) - - expectArrayWithSameContent(engine.getNamedExpressionsFromFormula('=bar+baz*2'), ['bar', 'baz']) - }) - - it('should return each named expression only once', () => { - const engine = HyperFormula.buildEmpty({}, [ - { name: 'foo', expression: '=42' }, - ]) - - expect(engine.getNamedExpressionsFromFormula('=foo+foo*2')).toEqual(['foo']) - }) - - it('should throw the ExpectedValueOfTypeError exception for input of wrong type', () => { - const engine = HyperFormula.buildEmpty({}, [ - { name: 'foo', expression: '=42' }, - ]) - - expect(() => engine.getNamedExpressionsFromFormula(42 as any)).toThrow(new ExpectedValueOfTypeError('string', 'formulaString')) - }) - - it('should throw the NotAFormulaError exception for a string that doesn\'t start with "="', () => { - const engine = HyperFormula.buildEmpty({}, [ - { name: 'foo', expression: '=42' }, - ]) - - expect(() => engine.getNamedExpressionsFromFormula('foo')).toThrow(new NotAFormulaError()) - }) - - it('should throw an exception for empty formula', () => { - const engine = HyperFormula.buildEmpty({}, [ - { name: 'foo', expression: '=42' }, - ]) - - expect(() => engine.getNamedExpressionsFromFormula('')).toThrow(new NotAFormulaError()) - }) - - it('should return an empty array when called with a formula with an error literal', () => { - const engine = HyperFormula.buildEmpty({}, [ - { name: 'foo', expression: '=42' }, - ]) - - expect(engine.getNamedExpressionsFromFormula('=#VALUE!')).toEqual([]) - }) - - it('should throw the NotAFormulaError exception for an unparsable formula', () => { - const engine = HyperFormula.buildEmpty({}, [ - { name: 'foo', expression: '=42' }, - ]) - - expect(() => engine.getNamedExpressionsFromFormula('=#FOO!')).toThrow(new NotAFormulaError()) - expect(() => engine.getNamedExpressionsFromFormula('=100%%*foo')).toThrow(new NotAFormulaError()) - expect(() => engine.getNamedExpressionsFromFormula('=@foo')).toThrow(new NotAFormulaError()) - expect(() => engine.getNamedExpressionsFromFormula("=foo'bar")).toThrow(new NotAFormulaError()) - expect(() => engine.getNamedExpressionsFromFormula('=\u00A0foo')).toThrow(new NotAFormulaError()) - }) - }) -}) diff --git a/test/unit/null-compatibility.spec.ts b/test/unit/null-compatibility.spec.ts deleted file mode 100644 index 8303d2e0e5..0000000000 --- a/test/unit/null-compatibility.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import {HyperFormula} from '../../src' -import {adr} from './testUtils' - -describe('null compatibility', () => { - it('should evaluate empty reference to null', () => { - const engine = HyperFormula.buildFromArray([['=A2']], {evaluateNullToZero: false}) - expect(engine.getCellValue(adr('A1'))).toBeNull() - expect(engine.getCellValue(adr('A2'))).toBeNull() - }) - - it('should evaluate empty reference to 0', () => { - const engine = HyperFormula.buildFromArray([['=A2']], {evaluateNullToZero: true}) - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toBeNull() - }) - - it('should evaluate if to null', () => { - const engine = HyperFormula.buildFromArray([['=IF(TRUE(),A2)']], {evaluateNullToZero: false}) - expect(engine.getCellValue(adr('A1'))).toBeNull() - expect(engine.getCellValue(adr('A2'))).toBeNull() - }) - - it('should evaluate if to 0', () => { - const engine = HyperFormula.buildFromArray([['=IF(TRUE(),A2)']], {evaluateNullToZero: true}) - expect(engine.getCellValue(adr('A1'))).toEqual(0) - expect(engine.getCellValue(adr('A2'))).toBeNull() - }) - - it('should evaluate isblank with null', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2', '=ISBLANK(A1)'], - [null, '=ISBLANK(A2)'] - ], {evaluateNullToZero: false}) - expect(engine.getCellValue(adr('B1'))).toEqual(true) - expect(engine.getCellValue(adr('B2'))).toEqual(true) - }) - - it('should evaluate isblank with 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2', '=ISBLANK(A1)'], - [null, '=ISBLANK(A2)'] - ], {evaluateNullToZero: true}) - expect(engine.getCellValue(adr('B1'))).toEqual(false) - expect(engine.getCellValue(adr('B2'))).toEqual(true) - }) -}) diff --git a/test/unit/optional-parameters.spec.ts b/test/unit/optional-parameters.spec.ts deleted file mode 100644 index 9e54a6fca4..0000000000 --- a/test/unit/optional-parameters.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import {ErrorType, HyperFormula} from '../../src' -import {ErrorMessage} from '../../src/error-message' -import {InterpreterState} from '../../src/interpreter/InterpreterState' -import {FunctionArgumentType, FunctionPlugin, FunctionPluginTypecheck} from '../../src/interpreter/plugin/FunctionPlugin' -import {ProcedureAst} from '../../src/parser' -import {adr, detailedError} from './testUtils' - -class FooPlugin extends FunctionPlugin implements FunctionPluginTypecheck { - public static implementedFunctions = { - 'FOO': { - method: 'foo', - parameters: [ - {argumentType: FunctionArgumentType.STRING, defaultValue: 'default1'}, - {argumentType: FunctionArgumentType.STRING, defaultValue: 'default2'}, - ], - }, - } - - public foo(ast: ProcedureAst, state: InterpreterState) { - return this.runFunction(ast.args, state, this.metadata('FOO'), - (arg1: string, arg2: string) => arg1 + '+' + arg2 - ) - } -} - -describe('Nonexistent metadata', () => { - it('should work for function', () => { - HyperFormula.getLanguage('enGB').extendFunctions({FOO: 'FOO'}) - const engine = HyperFormula.buildFromArray([ - ['=foo(1,2)'], - ['=foo(,2)'], - ['=foo( ,2)'], - ['=foo(1,)'], - ['=foo( , )'], - ['=foo(1)'], - ['=foo()'], - ], {functionPlugins: [FooPlugin]}) - - expect(engine.getCellValue(adr('A1'))).toBe('1+2') - expect(engine.getCellValue(adr('A2'))).toBe('+2') - expect(engine.getCellValue(adr('A3'))).toBe('+2') - expect(engine.getCellValue(adr('A4'))).toBe('1+') - expect(engine.getCellValue(adr('A5'))).toBe('+') - expect(engine.getCellValue(adr('A6'))).toBe('1+default2') - expect(engine.getCellValue(adr('A7'))).toBe('default1+default2') - }) - - it('log fails with coerce to 0', () => { - const engine = HyperFormula.buildFromArray([ - ['=LOG(10,)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NUM, ErrorMessage.ValueSmall)) - }) - - it('other function coerce EmptyValue', () => { - const engine = HyperFormula.buildFromArray([ - ['=DATE(,1,1900)'], - ['=SUM(,1)'], - ['=CONCATENATE(,"abcd")'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual(1901) - expect(engine.getCellValue(adr('A2'))).toEqual(1) - expect(engine.getCellValue(adr('A3'))).toEqual('abcd') - }) - -}) diff --git a/test/unit/parser/apostrophe.spec.ts b/test/unit/parser/apostrophe.spec.ts deleted file mode 100644 index c616111cf8..0000000000 --- a/test/unit/parser/apostrophe.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {CellValueDetailedType, HyperFormula} from '../../../src' -import {adr} from '../testUtils' - -describe('When value is prepend with an apostrophe', () => { - it('treats numeric value as a string', () => { - const engine = HyperFormula.buildFromArray([ - ["'001572"], - ['=A1'] - ]) - - expect(engine.getCellValue(adr('A1'))).toEqual('001572') - expect(engine.getCellValue(adr('A2'))).toEqual('001572') - expect(engine.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.STRING) - }) -}) diff --git a/test/unit/parser/boolean-operators.spec.ts b/test/unit/parser/boolean-operators.spec.ts deleted file mode 100644 index 92473601d5..0000000000 --- a/test/unit/parser/boolean-operators.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import {Config} from '../../../src/Config' -import { - AstNodeType, - EqualsOpAst, - GreaterThanOpAst, - GreaterThanOrEqualOpAst, - LessThanOpAst, - LessThanOrEqualOpAst, - NotEqualOpAst, -} from '../../../src/parser' -import {adr} from '../testUtils' -import {buildEmptyParserWithCaching} from './common' - -describe('Parser - Boolean operators', () => { - it('Equals operator', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=1=2', adr('A1')).ast as EqualsOpAst - - expect(ast.type).toBe(AstNodeType.EQUALS_OP) - }) - - it('Not equal operator', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=1<>2', adr('A1')).ast as NotEqualOpAst - - expect(ast.type).toBe(AstNodeType.NOT_EQUAL_OP) - }) - - it('Greater than operator', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=1>2', adr('A1')).ast as GreaterThanOpAst - - expect(ast.type).toBe(AstNodeType.GREATER_THAN_OP) - }) - - it('Less than operator', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=1<2', adr('A1')).ast as LessThanOpAst - - expect(ast.type).toBe(AstNodeType.LESS_THAN_OP) - }) - - it('Greater than or equal operator', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=1>=2', adr('A1')).ast as GreaterThanOrEqualOpAst - - expect(ast.type).toBe(AstNodeType.GREATER_THAN_OR_EQUAL_OP) - }) - - it('Less than or equal operator', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=1<=2', adr('A1')).ast as LessThanOrEqualOpAst - - expect(ast.type).toBe(AstNodeType.LESS_THAN_OR_EQUAL_OP) - }) - - it('Boolean operator with more complex childs', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=1+2=1+2*6', adr('A1')).ast as EqualsOpAst - - expect(ast.type).toBe(AstNodeType.EQUALS_OP) - expect(ast.left.type).toBe(AstNodeType.PLUS_OP) - expect(ast.right.type).toBe(AstNodeType.PLUS_OP) - }) -}) diff --git a/test/unit/parser/cell-address-from-string.spec.ts b/test/unit/parser/cell-address-from-string.spec.ts deleted file mode 100644 index 7a19e8f3e9..0000000000 --- a/test/unit/parser/cell-address-from-string.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { AlwaysDense } from '../../../src' -import {AddressMapping, SheetMapping, SheetReferenceRegistrar} from '../../../src/DependencyGraph' -import {buildTranslationPackage} from '../../../src/i18n' -import {enGB} from '../../../src/i18n/languages' -import {CellAddress, cellAddressFromString} from '../../../src/parser' -import {adr} from '../testUtils' - -describe('cellAddressFromString', () => { - let sheetMapping: SheetMapping - let addressMapping: AddressMapping - let resolveSheetReference: (sheetName: string) => number - beforeEach(() => { - sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - addressMapping = new AddressMapping(new AlwaysDense()) - const registrar = new SheetReferenceRegistrar(sheetMapping, addressMapping) - resolveSheetReference = registrar.ensureSheetRegistered.bind(registrar) - }) - - it('is zero based', () => { - expect(cellAddressFromString('A1', adr('A1'), resolveSheetReference)).toEqual(CellAddress.relative(0, 0)) - }) - - it('works for bigger rows', () => { - expect(cellAddressFromString('A123', adr('A1'), resolveSheetReference)).toEqual(CellAddress.relative(0, 122)) - }) - - it('one letter', () => { - expect(cellAddressFromString('Z1', adr('A1'), resolveSheetReference)).toEqual(CellAddress.relative(25, 0)) - }) - - it('last letter is Z', () => { - expect(cellAddressFromString('AA1', adr('A1'), resolveSheetReference)).toEqual(CellAddress.relative(26, 0)) - }) - - it('works for many letters', () => { - expect(cellAddressFromString('ABC1', adr('A1'), resolveSheetReference)).toEqual(CellAddress.relative(730, 0)) - }) - - it('is not case sensitive', () => { - expect(cellAddressFromString('abc1', adr('A1'), resolveSheetReference)).toEqual(CellAddress.relative(730, 0)) - }) - - it('when sheet is missing, its took from base address', () => { - expect(cellAddressFromString('B3', adr('A1', 42), resolveSheetReference)).toEqual(CellAddress.relative(1, 2)) - }) - - it('can into sheets', () => { - const sheet1 = sheetMapping.addSheet('Sheet1') - const sheet2 = sheetMapping.addSheet('Sheet2') - const sheet3 = sheetMapping.addSheet('~`!@#$%^&*()_-+_=/|?{}[]\"') - - expect(cellAddressFromString('Sheet1!B3', adr('A1', sheet1), resolveSheetReference)).toEqual(CellAddress.relative(1, 2, sheet1)) - expect(cellAddressFromString('Sheet2!B3', adr('A1', sheet1), resolveSheetReference)).toEqual(CellAddress.relative(1, 2, sheet2)) - expect(cellAddressFromString("'~`!@#$%^&*()_-+_=/|?{}[]\"'!B3", adr('A1', sheet1), resolveSheetReference)).toEqual(CellAddress.relative(1, 2, sheet3)) - }) - - it('returns undefined when sheet resolver fails', () => { - const failingResolver = () => undefined - - expect(cellAddressFromString('Ghost!A1', adr('A1'), failingResolver)).toBeUndefined() - }) -}) diff --git a/test/unit/parser/common.ts b/test/unit/parser/common.ts deleted file mode 100644 index 86e66780c1..0000000000 --- a/test/unit/parser/common.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { AlwaysDense } from '../../../src' -import {Config} from '../../../src/Config' -import {AddressMapping, SheetMapping, SheetReferenceRegistrar} from '../../../src/DependencyGraph' -import {buildTranslationPackage} from '../../../src/i18n' -import {enGB} from '../../../src/i18n/languages' -import {FunctionRegistry} from '../../../src/interpreter/FunctionRegistry' -import {ParserWithCaching} from '../../../src/parser' - -export function buildEmptyParserWithCaching(config: Config, sheetMapping?: SheetMapping, addressMapping?: AddressMapping): ParserWithCaching { - sheetMapping = sheetMapping || new SheetMapping(buildTranslationPackage(enGB)) - addressMapping = addressMapping || new AddressMapping(new AlwaysDense()) - const registrar = new SheetReferenceRegistrar(sheetMapping, addressMapping) - return new ParserWithCaching( - config, - new FunctionRegistry(config), - registrar.ensureSheetRegistered.bind(registrar) - ) -} diff --git a/test/unit/parser/compute-hash-from-ast.spec.ts b/test/unit/parser/compute-hash-from-ast.spec.ts deleted file mode 100644 index ccc203178b..0000000000 --- a/test/unit/parser/compute-hash-from-ast.spec.ts +++ /dev/null @@ -1,223 +0,0 @@ -import {HyperFormula} from '../../../src' -import {Config} from '../../../src/Config' -import {SheetMapping} from '../../../src/DependencyGraph' -import {buildTranslationPackage} from '../../../src/i18n' -import {enGB, plPL} from '../../../src/i18n/languages' -import {buildLexerConfig, FormulaLexer} from '../../../src/parser' -import {adr, unregisterAllLanguages} from '../testUtils' -import {buildEmptyParserWithCaching} from './common' - -describe('Compute hash from ast', () => { - beforeEach(() => { - unregisterAllLanguages() - HyperFormula.registerLanguage(plPL.langCode, plPL) - HyperFormula.registerLanguage(enGB.langCode, enGB) - }) - - const config = new Config() - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const lexer = new FormulaLexer(buildLexerConfig(config)) - const parser = buildEmptyParserWithCaching(config, sheetMapping) - - function expectHashFromAstMatchHashFromTokens(formula: string) { - const baseAddress = adr('A1') - const ast = parser.parse(formula, baseAddress).ast - const lexerResult = lexer.tokenizeFormula(formula) - const hashFromTokens = parser.computeHashFromTokens(lexerResult.tokens, baseAddress) - - const hashFromAst = parser.computeHashFromAst(ast) - - expect(hashFromAst).toEqual(hashFromTokens) - } - - it('literals', () => { - const formula = '=CONCATENATE("foo", 42.34)' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('function call', () => { - const formula = '=SUM(1, 2, 3)' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('function call - case insensitive', () => { - const formula = '=SuM(1,2,3)' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('simple addreess', () => { - const formula = '=Sheet1!A1' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('absolute col', () => { - const formula = '=Sheet1!$A1' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('absolute row addreess', () => { - const formula = '=Sheet1!A$1' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('absolute address', () => { - const formula = '=Sheet1!$A$1' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('cell range', () => { - const formula = '=$A$1:B$2' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('cell range with sheet on the left', () => { - const formula = '=Sheet1!A5:B16' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('cell range with sheet on both sides', () => { - const formula = '=Sheet1!A5:Sheet2!B16' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('column range', () => { - const formula = '=$A:B' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('column range with sheet on the left', () => { - const formula = '=Sheet1!A:B' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('column range with sheet on both sides', () => { - const formula = '=Sheet1!A:Sheet2!B' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('row range', () => { - const formula = '=$1:2' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('row range with sheet on the left', () => { - const formula = '=Sheet1!1:2' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('row range with sheet on both sides', () => { - const formula = '=Sheet1!1:Sheet2!2' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('ops', () => { - const formula = '=-1+1-1*1/1^1&1=1<>1<1<=1>1<1%' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('parenthesis', () => { - const formula = '=-1*(-2)*(3+4)+5' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('nested parenthesis', () => { - const formula = '=-(-(3+4))' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('cell ref between strings', () => { - const formula = '="A5"+A4+"A6"' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('cell ref in string with escape', () => { - const formula = '="fdsaf\\"A5"' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('procedure with error literal', () => { - const formula = '=#DIV/0!' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('reference to not existing sheet', () => { - const formula = '=Sheet3!A1' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('procedure hash using canonical name', () => { - const config = new Config({language: 'plPL'}) - const sheetMapping = new SheetMapping(buildTranslationPackage(plPL)) - sheetMapping.addSheet('Sheet1') - const lexer = new FormulaLexer(buildLexerConfig(config)) - const parser = buildEmptyParserWithCaching(config, sheetMapping) - - const formula = '=SUMA(A1)' - const address = adr('A1') - const ast = parser.parse(formula, address).ast - const lexerResult = lexer.tokenizeFormula(formula) - const hashFromTokens = parser.computeHashFromTokens(lexerResult.tokens, address) - const hash = parser.computeHashFromAst(ast) - - expect(hash).toEqual(hashFromTokens) - }) - - it('procedure name with missing translation', () => { - const config = new Config({language: 'plPL'}) - const sheetMapping = new SheetMapping(buildTranslationPackage(plPL)) - sheetMapping.addSheet('Sheet1') - const lexer = new FormulaLexer(buildLexerConfig(config)) - const parser = buildEmptyParserWithCaching(config, sheetMapping) - - const formula = '=FooBAR(A1)' - const address = adr('A1') - const ast = parser.parse(formula, address).ast - const lexerResult = lexer.tokenizeFormula(formula) - const hashFromTokens = parser.computeHashFromTokens(lexerResult.tokens, address) - const hash = parser.computeHashFromAst(ast) - - expect(hash).toEqual(hashFromTokens) - }) - - it('should work with whitespaces', () => { - const formula = '= - 1 + 2 / 3 - 4 % * (1 + 2 ) + SUM( A1, A1 : A2 ) + #DIV/0!' - const address = adr('A1') - const ast = parser.parse(formula, address).ast - const hash = parser.computeHashFromAst(ast) - expect(hash).toEqual('= - 1 + 2 / 3 - 4 % * (1 + 2 ) + SUM( #0R0, #0R0:#1R0 ) + #DIV/0!') - }) - - it('should skip whitespaces before function args separators', () => { - const formula = '=SUM(A1 , A2)' - const address = adr('A1') - const ast = parser.parse(formula, address).ast - const hash = parser.computeHashFromAst(ast) - expect(hash).toEqual('=SUM(#0R0, #1R0)') - }) - - it('should not skip whitespaces when there is empty arg', () => { - const formula = '=PV(1 ,2,3, ,A2)' - expectHashFromAstMatchHashFromTokens(formula) - }) - - it('should work with decimal separator', () => { - const config = new Config({decimalSeparator: ',', functionArgSeparator: ';'}) - const sheetMapping = new SheetMapping(buildTranslationPackage(plPL)) - sheetMapping.addSheet('Sheet1') - const lexer = new FormulaLexer(buildLexerConfig(config)) - const parser = buildEmptyParserWithCaching(config, sheetMapping) - - const formula = '=1+123,456' - const address = adr('A1') - const ast = parser.parse(formula, address).ast - - const lexerResult = lexer.tokenizeFormula(formula) - const hashFromTokens = parser.computeHashFromTokens(lexerResult.tokens, address) - const hash = parser.computeHashFromAst(ast) - - expect(hash).toEqual(formula) - expect(hash).toEqual(hashFromTokens) - }) -}) diff --git a/test/unit/parser/compute-hash-from-tokens.spec.ts b/test/unit/parser/compute-hash-from-tokens.spec.ts deleted file mode 100644 index 1407f0db2b..0000000000 --- a/test/unit/parser/compute-hash-from-tokens.spec.ts +++ /dev/null @@ -1,208 +0,0 @@ -import {HyperFormula} from '../../../src' -import {SimpleCellAddress} from '../../../src/Cell' -import {Config} from '../../../src/Config' -import {SheetMapping} from '../../../src/DependencyGraph' -import {enGB, plPL} from '../../../src/i18n/languages' -import {buildLexerConfig, FormulaLexer} from '../../../src/parser' -import {adr, unregisterAllLanguages} from '../testUtils' -import {buildEmptyParserWithCaching} from './common' - -describe('computeHashFromTokens', () => { - const computeFunc = (code: string, address: SimpleCellAddress, language: string = 'enGB'): string => { - const config = new Config({language}) - const sheetMapping = new SheetMapping(HyperFormula.getLanguage(language)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const parser = buildEmptyParserWithCaching(config, sheetMapping) - const tokens = new FormulaLexer(buildLexerConfig(config)).tokenizeFormula(code).tokens - return parser.computeHashFromTokens(tokens, address) - } - beforeEach(() => { - unregisterAllLanguages() - HyperFormula.registerLanguage(plPL.langCode, plPL) - HyperFormula.registerLanguage(enGB.langCode, enGB) - }) - - it('simple case', () => { - const code = '=42' - - expect(computeFunc(code, adr('B2'))).toEqual('=42') - }) - - it('cell relative reference', () => { - const code = '=A5' - - expect(computeFunc(code, adr('B2'))).toEqual('=#3R-1') - }) - - it('cell absolute reference', () => { - const code = '=$A$5' - - expect(computeFunc(code, adr('B2'))).toEqual('=#4A0') - }) - - it('cell absolute col reference', () => { - const code = '=$A5' - - expect(computeFunc(code, adr('B2'))).toEqual('=#3AC0') - }) - - it('cell absolute row reference', () => { - const code = '=A$5' - - expect(computeFunc(code, adr('B2'))).toEqual('=#4AR-1') - }) - - it('more addresses', () => { - const code = '=A5+A7' - - expect(computeFunc(code, adr('B2'))).toEqual('=#3R-1+#5R-1') - }) - - it('cell ref in string', () => { - const code = '="A5"' - - expect(computeFunc(code, adr('B2'))).toEqual('="A5"') - }) - - it('cell ref between strings', () => { - const code = '="A5"+A4+"A6"' - - expect(computeFunc(code, adr('B2'))).toEqual('="A5"+#2R-1+"A6"') - }) - - it('cell ref in string with escape', () => { - const code = '="fdsaf\\"A5"' - - expect(computeFunc(code, adr('B2'))).toEqual('="fdsaf\\"A5"') - }) - - it('cell ref to not exsiting sheet', () => { - const code = '=Sheet3!A1' - - expect(computeFunc(code, adr('B2'))).toBe('=#2#-1R-1') - }) - - it('cell range', () => { - const code = '=A5:B16' - - expect(computeFunc(code, adr('B2'))).toEqual('=#3R-1:#14R0') - }) - - it('cell range with sheet on the left', () => { - const code = '=Sheet1!A5:B16' - - expect(computeFunc(code, adr('B2'))).toEqual('=#0#3R-1:#14R0') - }) - - it('cell range with sheet on both sides', () => { - const code = '=Sheet1!A5:Sheet2!B16' - - expect(computeFunc(code, adr('B2'))).toEqual('=#0#3R-1:#1#14R0') - }) - - it('column range', () => { - const code = '=A:$B' - - expect(computeFunc(code, adr('B2'))).toEqual('=#COLR-1:#COLA1') - }) - - it('column range with sheet on the left', () => { - const code = '=Sheet1!A:B' - - expect(computeFunc(code, adr('B2'))).toEqual('=#0#COLR-1:#COLR0') - }) - - it('column range with sheet on both sides', () => { - const code = '=Sheet1!A:Sheet2!B' - - expect(computeFunc(code, adr('B2'))).toEqual('=#0#COLR-1:#1#COLR0') - }) - - it('row range', () => { - const code = '=1:$2' - - expect(computeFunc(code, adr('B2'))).toEqual('=#ROWR-1:#ROWA1') - }) - - it('row range with sheet on the left', () => { - const code = '=Sheet1!1:2' - - expect(computeFunc(code, adr('B2'))).toEqual('=#0#ROWR-1:#ROWR0') - }) - - it('row range with sheet on both sides', () => { - const code = '=Sheet1!1:Sheet2!2' - - expect(computeFunc(code, adr('B2'))).toEqual('=#0#ROWR-1:#1#ROWR0') - }) - - it('do not ignores whitespace', () => { - const code = '= 42' - - expect(computeFunc(code, adr('B2'))).toEqual('= 42') - }) - - it('different hash for formulas with different namespace', () => { - const code1 = '= 42 ' - const code2 = '=42' - - const result1 = computeFunc(code1, adr('B2')) - const result2 = computeFunc(code2, adr('B2')) - expect(result1).not.toEqual(result2) - }) - - it('support sheets', () => { - const code = '=Sheet2!A5' - - expect(computeFunc(code, adr('B2'))).toEqual('=#1#3R-1') - }) - - it('function call names are normalized', () => { - const code = '=rAnd()' - - expect(computeFunc(code, adr('B2'))).toEqual('=RAND()') - }) - - it('function call in canonical form', () => { - const code = '=SUMA()' - - expect(computeFunc(code, adr('B2'), 'plPL')).toEqual('=SUM()') - }) - - it('function call when missing translation', () => { - const code = '=fooBAR()' - - expect(computeFunc(code, adr('B2'), 'plPL')).toEqual('=FOOBAR()') - }) - - it('should work with whitespaces', () => { - const formula = '= - 1 + 2 / 3 - 4 % * (1 + 2 ) + SUM( Sheet1!A1, A1:A2 )' - const hash = computeFunc(formula, adr('A1')) - expect(hash).toEqual('= - 1 + 2 / 3 - 4 % * (1 + 2 ) + SUM( #0#0R0, #0R0:#1R0 )') - }) - - it('should skip whitespaces inside range', () => { - const formula = '=SUM( A1 : A2 )' - const hash = computeFunc(formula, adr('A1')) - expect(hash).toEqual('=SUM( #0R0:#1R0 )') - }) - - it('should skip trailing whitespace', () => { - const formula = '=1 ' - const hash = computeFunc(formula, adr('A1')) - expect(hash).toEqual('=1') - }) - - it('should skip whitespaces before function args separators', () => { - const formula = '=SUM(A1 , A2)' - const hash = computeFunc(formula, adr('A1')) - expect(hash).toEqual('=SUM(#0R0, #1R0)') - }) - - it('should not skip whitespaces when there is empty arg', () => { - const formula = '=PV(A1 ,2,3, ,A2)' - const hash = computeFunc(formula, adr('A1')) - expect(hash).toEqual('=PV(#0R0,2,3, ,#1R0)') - }) -}) diff --git a/test/unit/parser/concatenate-operator.spec.ts b/test/unit/parser/concatenate-operator.spec.ts deleted file mode 100644 index 4e4355b007..0000000000 --- a/test/unit/parser/concatenate-operator.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {Config} from '../../../src/Config' -import {AstNodeType, ConcatenateOpAst, ProcedureAst} from '../../../src/parser' -import {adr} from '../testUtils' -import {buildEmptyParserWithCaching} from './common' - -describe('Parser - Concatenate operators', () => { - it('Greater than operator', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('="a"&"b"', adr('A1')).ast as ConcatenateOpAst - expect(ast.type).toBe(AstNodeType.CONCATENATE_OP) - expect(ast.left.type).toBe(AstNodeType.STRING) - expect(ast.left.type).toBe(AstNodeType.STRING) - }) - - it('Greater than operator as function parameter', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=CONCATENATE("="&A6,"foo")', adr('A1')).ast as ProcedureAst - expect(ast.type).toBe(AstNodeType.FUNCTION_CALL) - expect(ast.args[0]!.type).toBe(AstNodeType.CONCATENATE_OP) - }) -}) diff --git a/test/unit/parser/decimal.spec.ts b/test/unit/parser/decimal.spec.ts deleted file mode 100644 index 65ba2b159d..0000000000 --- a/test/unit/parser/decimal.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {ErrorMessage} from '../../../src/error-message' -import {adr, detailedError} from '../testUtils' - -describe('decimal parsing', () => { - it('parsing decimal without leading zero', () => { - const engine = HyperFormula.buildFromArray([ - ['.1', '=.1'], - ['-.1', '=-.1'], - ['+.1', '=+.1'], - ['+.1', '=+.1+.2'], - ['=SUM(A1:A4, 0.3, .3)', '=SUM(B1:B4)'], - ['.1.4', '=..1'], - ['1.', '=1.'], - ['1e1', '=1e1'], - ['1e+1', '=1e+1'], - ['1e-1', '=1e-1'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0.1) - expect(engine.getCellValue(adr('B1'))).toBe(0.1) - expect(engine.getCellValue(adr('A2'))).toBe(-0.1) - expect(engine.getCellValue(adr('B2'))).toBe(-0.1) - expect(engine.getCellValue(adr('A3'))).toBe(0.1) - expect(engine.getCellValue(adr('B3'))).toBe(0.1) - expect(engine.getCellValue(adr('A4'))).toBe(0.1) - expect(engine.getCellValue(adr('B4'))).toBe(0.3) - expect(engine.getCellValue(adr('A5'))).toBe(0.8) - expect(engine.getCellValue(adr('B5'))).toBe(0.4) - expect(engine.getCellValue(adr('A6'))).toBe('.1.4') - expect(engine.getCellValue(adr('B6'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - expect(engine.getCellValue(adr('A7'))).toBe(1) - expect(engine.getCellValue(adr('B7'))).toBe(1) - expect(engine.getCellValue(adr('A8'))).toBe(10) - expect(engine.getCellValue(adr('B8'))).toBe(10) - expect(engine.getCellValue(adr('A9'))).toBe(10) - expect(engine.getCellValue(adr('B9'))).toBe(10) - expect(engine.getCellValue(adr('A10'))).toBe(0.1) - expect(engine.getCellValue(adr('B10'))).toBe(0.1) - }) -}) diff --git a/test/unit/parser/error-literals.spec.ts b/test/unit/parser/error-literals.spec.ts deleted file mode 100644 index af940fc9d1..0000000000 --- a/test/unit/parser/error-literals.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import {HyperFormula} from '../../../src' -import {ErrorType} from '../../../src/Cell' -import {Config} from '../../../src/Config' -import {SheetMapping} from '../../../src/DependencyGraph' -import {buildTranslationPackage} from '../../../src/i18n' -import {enGB, plPL} from '../../../src/i18n/languages' -import {AstNodeType, ErrorAst, ParsingErrorType} from '../../../src/parser' -import {adr} from '../testUtils' -import {buildEmptyParserWithCaching} from './common' - -describe('Parsing error literals', () => { - it('should parse error literals', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const ast = parser.parse('=#VALUE!', adr('A1')).ast as ErrorAst - expect(ast.type).toBe(AstNodeType.ERROR) - expect(ast.error.type).toEqual(ErrorType.VALUE) - }) - - it('should not parse #LIC! as license error', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const ast = parser.parse('=#LIC!', adr('A1')).ast as ErrorAst - expect(ast.type).toBe(AstNodeType.ERROR) - expect(ast.error.type).toEqual(ErrorType.ERROR) - }) - - it('should parse error literals with ?', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const ast = parser.parse('=#NAME?', adr('A1')).ast as ErrorAst - expect(ast.type).toBe(AstNodeType.ERROR) - expect(ast.error.type).toEqual(ErrorType.NAME) - }) - - it('should parse error literals with slashes', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const ast = parser.parse('=#N/A', adr('A1')).ast as ErrorAst - expect(ast.type).toBe(AstNodeType.ERROR) - expect(ast.error.type).toEqual(ErrorType.NA) - }) - - it('should parse error in other languages', () => { - HyperFormula.registerLanguage('plPL', plPL) - const parser = buildEmptyParserWithCaching(new Config({language: 'plPL'}), new SheetMapping(buildTranslationPackage(plPL))) - const ast = parser.parse('=#ARG!', adr('A1')).ast as ErrorAst - expect(ast.type).toBe(AstNodeType.ERROR) - expect(ast.error.type).toEqual(ErrorType.VALUE) - }) - - it('should parse #DIV/0!', () => { - const parser = buildEmptyParserWithCaching(new Config({language: 'enGB'}), new SheetMapping(buildTranslationPackage(enGB))) - const ast = parser.parse('=#DIV/0!', adr('A1')).ast as ErrorAst - expect(ast.type).toBe(AstNodeType.ERROR) - expect(ast.error.type).toEqual(ErrorType.DIV_BY_ZERO) - }) - - it('should return parser error', () => { - const parser = buildEmptyParserWithCaching(new Config({language: 'enGB'}), new SheetMapping(buildTranslationPackage(enGB))) - const {ast, errors} = parser.parse('=#UNKNOWN!', adr('A1')) - expect(ast.type).toBe(AstNodeType.ERROR) - expect(errors[0].type).toBe(ParsingErrorType.ParserError) - expect(errors[0].message).toBe('Unknown error literal') - }) -}) diff --git a/test/unit/parser/offset-translation.spec.ts b/test/unit/parser/offset-translation.spec.ts deleted file mode 100644 index a75c6bef81..0000000000 --- a/test/unit/parser/offset-translation.spec.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { HyperFormula } from '../../../src' -import {CellError, ErrorType} from '../../../src/Cell' -import {Config} from '../../../src/Config' -import { SheetMapping } from '../../../src/DependencyGraph' -import {ErrorMessage} from '../../../src/error-message' -import { buildTranslationPackage } from '../../../src/i18n' -import { enUS } from '../../../src/i18n/languages' -import {AstNodeType, CellAddress, CellRangeAst, CellReferenceAst, ErrorAst} from '../../../src/parser' -import {adr} from '../testUtils' -import {buildEmptyParserWithCaching} from './common' - -describe('Parser - OFFSET to reference translation', () => { - it('OFFSET parsing into cell reference', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=OFFSET(F16, 0, 0)', adr('B3', 1)).ast as CellReferenceAst - expect(ast.type).toBe(AstNodeType.CELL_REFERENCE) - expect(ast.reference).toEqual(CellAddress.relative(4, 13)) - }) - - it('OFFSET parsing into cell reference with row shift', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=OFFSET(F16, 1, 0)', adr('B3', 1)).ast as CellReferenceAst - expect(ast.type).toBe(AstNodeType.CELL_REFERENCE) - expect(ast.reference).toEqual(CellAddress.relative(4, 14)) - }) - - it('OFFSET parsing into cell reference with negative row shift', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=OFFSET(C3, -1, 0)', adr('B2')).ast as CellReferenceAst - expect(ast.type).toBe(AstNodeType.CELL_REFERENCE) - expect(ast.reference).toEqual(CellAddress.relative(1, 0)) - }) - - it('OFFSET parsing into cell reference with column shift', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=OFFSET(F16, 0, 1)', adr('B3', 1)).ast as CellReferenceAst - expect(ast.type).toBe(AstNodeType.CELL_REFERENCE) - expect(ast.reference).toEqual(CellAddress.relative(5, 13)) - }) - - it('OFFSET parsing into cell reference with negative column shift', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=OFFSET(C3, 0, -1)', adr('B2')).ast as CellReferenceAst - expect(ast.type).toBe(AstNodeType.CELL_REFERENCE) - expect(ast.reference).toEqual(CellAddress.relative(0, 1)) - }) - - it('OFFSET parsing into cell reference with some height', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=OFFSET(F16, 2, 0, 3)', adr('B3', 1)).ast as CellRangeAst - expect(ast.type).toBe(AstNodeType.CELL_RANGE) - expect(ast.start).toEqual(CellAddress.relative(4, 15)) - expect(ast.end).toEqual(CellAddress.relative(4, 17)) - }) - - it('OFFSET parsing into cell reference with some width', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=OFFSET(F16, 0, 2, 1, 3)', adr('B3', 1)).ast as CellRangeAst - expect(ast.type).toBe(AstNodeType.CELL_RANGE) - expect(ast.start).toEqual(CellAddress.relative(6, 13)) - expect(ast.end).toEqual(CellAddress.relative(8, 13)) - }) - - it('OFFSET parsing into cell range in different sheet', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enUS)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const ast = parser.parse('=OFFSET(Sheet2!F16, 0, 2, 1, 3)', adr('B3', 0)).ast as CellRangeAst - expect(ast.type).toBe(AstNodeType.CELL_RANGE) - expect(ast.start).toEqual(CellAddress.relative(6, 13, 1)) - expect(ast.end).toEqual(CellAddress.relative(8, 13, 1)) - }) - - it('OFFSET first argument need to be reference', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {errors} = parser.parse('=OFFSET(42, 0, 0)', adr('A1')) - expect(errors[0].type).toBe('StaticOffsetError') - expect(errors[0].message).toBe('First argument to OFFSET is not a reference') - }) - - it('OFFSET second argument need to be static number', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {errors} = parser.parse('=OFFSET(A1, C3, 0)', adr('A1')) - expect(errors[0].type).toBe('StaticOffsetError') - expect(errors[0].message).toBe('Second argument to OFFSET is not a static number') - }) - - it('OFFSET second argument need to be integer', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {errors} = parser.parse('=OFFSET(A1, 1.3, 0)', adr('A1')) - expect(errors[0].type).toBe('StaticOffsetError') - expect(errors[0].message).toBe('Second argument to OFFSET is not a static number') - }) - - it('OFFSET third argument need to be static number', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {errors} = parser.parse('=OFFSET(A1, 0, C3)', adr('A1')) - expect(errors[0].type).toBe('StaticOffsetError') - expect(errors[0].message).toBe('Third argument to OFFSET is not a static number') - }) - - it('OFFSET third argument need to be integer', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {errors} = parser.parse('=OFFSET(A1, 0, 1.3)', adr('A1')) - expect(errors[0].type).toBe('StaticOffsetError') - expect(errors[0].message).toBe('Third argument to OFFSET is not a static number') - }) - - it('OFFSET fourth argument need to be static number', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {errors} = parser.parse('=OFFSET(A1, 0, 0, B3)', adr('A1')) - expect(errors[0].type).toBe('StaticOffsetError') - expect(errors[0].message).toBe('Fourth argument to OFFSET is not a static number') - }) - - it('OFFSET fourth argument need to be static number bigger than 0', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {errors} = parser.parse('=OFFSET(A1, 0, 0, 0)', adr('A1')) - expect(errors[0].type).toBe('StaticOffsetError') - expect(errors[0].message).toBe('Fourth argument to OFFSET is too small number') - }) - - it('OFFSET fourth argument need to be integer', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {errors} = parser.parse('=OFFSET(A1, 0, 0, 1.3)', adr('A1')) - expect(errors[0].type).toBe('StaticOffsetError') - expect(errors[0].message).toBe('Fourth argument to OFFSET is not integer') - }) - - it('OFFSET fifth argument need to be static number', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {errors} = parser.parse('=OFFSET(A1, 0, 0, 1, B3)', adr('A1')) - expect(errors[0].type).toBe('StaticOffsetError') - expect(errors[0].message).toBe('Fifth argument to OFFSET is not a static number') - }) - - it('OFFSET fifth argument need to be static number bigger than 0', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {errors} = parser.parse('=OFFSET(A1, 0, 0, 1, 0)', adr('A1')) - expect(errors[0].type).toBe('StaticOffsetError') - expect(errors[0].message).toBe('Fifth argument to OFFSET is too small number') - }) - - it('OFFSET fifth argument need to be integer', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {errors} = parser.parse('=OFFSET(A1, 0, 0, 1, 1.3)', adr('A1')) - expect(errors[0].type).toBe('StaticOffsetError') - expect(errors[0].message).toBe('Fifth argument to OFFSET is not integer') - }) - - it('OFFSET resulting reference out of the sheet in top left row', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {ast, errors} = parser.parse('=OFFSET(A1, -1, 0)', adr('A1')) - expect(errors.length).toBe(0) - expect((ast as ErrorAst).error).toEqual(new CellError(ErrorType.REF, ErrorMessage.OutOfSheet)) - }) - - it('OFFSET resulting reference out of the sheet in top left column', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {ast, errors} = parser.parse('=OFFSET(A1, 0, -1)', adr('A1')) - expect(errors.length).toBe(0) - expect((ast as ErrorAst).error).toEqual(new CellError(ErrorType.REF, ErrorMessage.OutOfSheet)) - }) - - it('OFFSET case insensitive', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=oFfSeT(F16, 0, 0)', adr('B3', 1)).ast as CellReferenceAst - expect(ast.type).toBe(AstNodeType.CELL_REFERENCE) - expect(ast.reference).toEqual(CellAddress.relative(4, 13)) - }) - - it('parser returns address with a sheet reference', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enUS)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const ast = parser.parse('=OFFSET(Sheet2!A1, 0, 0)', adr('A1', 0)).ast as CellReferenceAst - expect(ast.type).toBe(AstNodeType.CELL_REFERENCE) - expect(ast.reference).toEqual(CellAddress.relative(0, 0, 1)) - }) - - it('function OFFSET can reference a different sheet', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [['sheet1']], - Sheet2: [['sheet2', '=OFFSET(Sheet1!A1, 0, 0)']], - }) - - expect(engine.getCellValue(adr('B1', engine.getSheetId('Sheet2')))).toEqual('sheet1') - }) -}) diff --git a/test/unit/parser/parser-caching.spec.ts b/test/unit/parser/parser-caching.spec.ts deleted file mode 100644 index 40310cd49d..0000000000 --- a/test/unit/parser/parser-caching.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {Config} from '../../../src/Config' -import {adr} from '../testUtils' -import {buildEmptyParserWithCaching} from './common' - -describe('ParserWithCaching - caching', () => { - it('use cache for similar formulas', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast1 = parser.parse('=A1', adr('A1')).ast - const ast2 = parser.parse('=A2', adr('A2')).ast - - expect(ast1).toEqual(ast2) - expect(parser.statsCacheUsed).toBe(1) - }) - - it("doesn't count cache for different formulas", () => { - const parser = buildEmptyParserWithCaching(new Config()) - - parser.parse('=A1', adr('A1')).ast - parser.parse('=A2+A3', adr('A1')).ast - - expect(parser.statsCacheUsed).toBe(0) - }) -}) diff --git a/test/unit/parser/parser-dependencies.spec.ts b/test/unit/parser/parser-dependencies.spec.ts deleted file mode 100644 index 940c87d762..0000000000 --- a/test/unit/parser/parser-dependencies.spec.ts +++ /dev/null @@ -1,121 +0,0 @@ -import {AbsoluteCellRange} from '../../../src/AbsoluteCellRange' -import {absolutizeDependencies} from '../../../src/absolutizeDependencies' -import {Config} from '../../../src/Config' -import {NamedExpressionDependency} from '../../../src/parser' -import {adr, expectArrayWithSameContent} from '../testUtils' -import {buildEmptyParserWithCaching} from './common' - -describe('Parsing collecting dependencies', () => { - it('works for CELL_REFERENCE with relative dependency', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const formulaAddress = adr('B2') - - const parseResult = parser.parse('=B2', formulaAddress) - const dependencies = absolutizeDependencies(parseResult.dependencies, formulaAddress) - - expect(dependencies).toEqual([adr('B2')]) - }) - - it('works with absolute dependencies', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const formulaAddress = adr('B2') - - const parseResult = parser.parse('=$B$2', formulaAddress) - const dependencies = absolutizeDependencies(parseResult.dependencies, formulaAddress) - - expect(dependencies.length).toEqual(1) - expect(dependencies[0]).toEqual(adr('B2')) - }) - - it('works for CELL_RANGE', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const formulaAddress = adr('A1') - - const parseResult = parser.parse('=B2:C4', formulaAddress) - const dependencies = absolutizeDependencies(parseResult.dependencies, formulaAddress) - - expect(dependencies).toEqual([ - new AbsoluteCellRange(adr('B2'), adr('C4')), - ]) - }) - - it('works inside parenthesis', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const formulaAddress = adr('A1') - - const parseResult = parser.parse('=(A1+B2)', formulaAddress) - const dependencies = absolutizeDependencies(parseResult.dependencies, formulaAddress) - - expectArrayWithSameContent([adr('A1'), adr('B2')], dependencies) - }) - - it('goes inside unary minus', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const formulaAddress = adr('A1') - - const parseResult = parser.parse('=-B2', formulaAddress) - const dependencies = absolutizeDependencies(parseResult.dependencies, formulaAddress) - - expect(dependencies).toEqual([ - adr('B2'), - ]) - }) - - it('goes inside plus operator', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const formulaAddress = adr('A1') - - const parseResult = parser.parse('=B2+C3', formulaAddress) - const dependencies = absolutizeDependencies(parseResult.dependencies, formulaAddress) - - expect(dependencies).toEqual([ - adr('B2'), - adr('C3'), - ]) - }) - - it('goes inside function call arguments', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const formulaAddress = adr('A1') - - const parseResult = parser.parse('=SUM(B2, C3)', formulaAddress) - const dependencies = absolutizeDependencies(parseResult.dependencies, formulaAddress) - - expect(dependencies).toEqual([ - adr('B2'), - adr('C3'), - ]) - }) - - it('OFFSET call is correctly found as dependency', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const formulaAddress = adr('B2') - - const parseResult = parser.parse('=OFFSET(D4, 0, 0)', formulaAddress) - const dependencies = absolutizeDependencies(parseResult.dependencies, formulaAddress) - expect(dependencies).toEqual([ - adr('D4'), - ]) - }) - - it('COLUMNS arguments are not dependencies', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const formulaAddress = adr('B2') - - const parseResult = parser.parse('=COLUMNS(A1:B3)', formulaAddress) - const dependencies = absolutizeDependencies(parseResult.dependencies, formulaAddress) - expect(dependencies).toEqual([]) - }) - - it('works for named expression dependencies', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const parseResult = parser.parse('=FOO+bar', adr('A1')) - - const dependencies = absolutizeDependencies(parseResult.dependencies, adr('A1')) - - expect(dependencies).toEqual([ - new NamedExpressionDependency('FOO'), - new NamedExpressionDependency('bar'), - ]) - }) -}) diff --git a/test/unit/parser/parser.spec.ts b/test/unit/parser/parser.spec.ts deleted file mode 100644 index c49494d0eb..0000000000 --- a/test/unit/parser/parser.spec.ts +++ /dev/null @@ -1,986 +0,0 @@ -import {ErrorType, HyperFormula} from '../../../src' -import {CellError} from '../../../src' -import {Config} from '../../../src/Config' -import {SheetMapping} from '../../../src/DependencyGraph' -import {buildTranslationPackage} from '../../../src/i18n' -import {enGB, plPL} from '../../../src/i18n/languages' -import {NoSheetWithNameError} from '../../../src/errors' -import { - AstNodeType, - buildCellErrorAst, - CellAddress, - CellRangeAst, - CellReferenceAst, - MinusOpAst, - MinusUnaryOpAst, - NamedExpressionAst, - NumberAst, - ParsingErrorType, - PlusOpAst, - PowerOpAst, - ProcedureAst, - StringAst, -} from '../../../src/parser' -import {columnIndexToLabel} from '../../../src/parser/addressRepresentationConverters' -import { - ArrayAst, - buildCellRangeAst, - buildCellReferenceAst, - buildColumnRangeAst, - buildErrorWithRawInputAst, - buildNumberAst, - buildRowRangeAst, - ColumnRangeAst, - ParenthesisAst, - RangeSheetReferenceType, RowRangeAst, -} from '../../../src/parser/Ast' -import {ColumnAddress} from '../../../src/parser/ColumnAddress' -import {RowAddress} from '../../../src/parser/RowAddress' -import {adr, unregisterAllLanguages} from '../testUtils' -import {buildEmptyParserWithCaching} from './common' - -describe('ParserWithCaching', () => { - beforeEach(() => { - unregisterAllLanguages() - HyperFormula.registerLanguage(plPL.langCode, plPL) - HyperFormula.registerLanguage(enGB.langCode, enGB) - }) - - it('integer literal', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=42', adr('A1')).ast - expect(ast).toEqual(buildNumberAst(42)) - }) - - it('negative integer literal', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=-42', adr('A1')).ast as MinusUnaryOpAst - expect(ast.type).toBe(AstNodeType.MINUS_UNARY_OP) - expect(ast.value).toEqual(buildNumberAst(42)) - }) - - it('string literal', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('="foobar"', adr('A1')).ast as StringAst - expect(ast.type).toBe(AstNodeType.STRING) - expect(ast.value).toBe('foobar') - }) - - it('plus operator on different nodes', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=1+A5', adr('A1')).ast as PlusOpAst - expect(ast.type).toBe(AstNodeType.PLUS_OP) - expect(ast.left.type).toBe(AstNodeType.NUMBER) - expect(ast.right.type).toBe(AstNodeType.CELL_REFERENCE) - }) - - it('minus operator', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=1-3', adr('A1')).ast as MinusOpAst - expect(ast.type).toBe(AstNodeType.MINUS_OP) - expect(ast.left.type).toBe(AstNodeType.NUMBER) - expect(ast.right.type).toBe(AstNodeType.NUMBER) - }) - - it('power operator', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=2^3', adr('A1')).ast as PowerOpAst - expect(ast.type).toBe(AstNodeType.POWER_OP) - expect(ast.left.type).toBe(AstNodeType.NUMBER) - expect(ast.right.type).toBe(AstNodeType.NUMBER) - }) - - it('power operator order', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=2*2^3', adr('A1')).ast as PowerOpAst - expect(ast.type).toBe(AstNodeType.TIMES_OP) - expect(ast.left.type).toBe(AstNodeType.NUMBER) - expect(ast.right.type).toBe(AstNodeType.POWER_OP) - }) - - it('joining nodes without braces', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const ast = parser.parse('=1 + 2 + 3', adr('A1')).ast as PlusOpAst - expect(ast.type).toBe(AstNodeType.PLUS_OP) - expect(ast.left.type).toBe(AstNodeType.PLUS_OP) - expect(ast.right.type).toBe(AstNodeType.NUMBER) - }) - - it('joining nodes with braces', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const ast = parser.parse('=1 + (2 + 3)', adr('A1')).ast as PlusOpAst - expect(ast.type).toBe(AstNodeType.PLUS_OP) - expect(ast.left.type).toBe(AstNodeType.NUMBER) - - const right = ast.right as ParenthesisAst - expect(right.type).toBe(AstNodeType.PARENTHESIS) - expect(right.expression.type).toBe(AstNodeType.PLUS_OP) - }) - - it('float literal', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const ast = parser.parse('=3.14', adr('A1')).ast - expect(ast).toEqual(buildNumberAst(3.14)) - }) - - it('float literal with different decimal separator', () => { - const parser = buildEmptyParserWithCaching(new Config({ - decimalSeparator: ',', - functionArgSeparator: ';' - }), new SheetMapping(buildTranslationPackage(enGB))) - const ast1 = parser.parse('=3,14', adr('A1')).ast - const ast2 = parser.parse('=03,14', adr('A1')).ast - const ast3 = parser.parse('=,14', adr('A1')).ast - - expect(ast1).toEqual(buildNumberAst(3.14)) - expect(ast2).toEqual(buildNumberAst(3.14)) - expect(ast3).toEqual(buildNumberAst(0.14)) - }) - - it('leading zeros of number literals', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const int = parser.parse('=01234', adr('A1')).ast as NumberAst - const float = parser.parse('=03.14', adr('A1')).ast as NumberAst - expect(int.type).toBe(AstNodeType.NUMBER) - expect(int.value).toBe(1234) - expect(float.type).toBe(AstNodeType.NUMBER) - expect(float.value).toBe(3.14) - }) - - it('allow to accept different lexer configs', () => { - const parser1 = buildEmptyParserWithCaching(new Config()) - const parser2 = buildEmptyParserWithCaching(new Config({functionArgSeparator: ';'}), new SheetMapping(buildTranslationPackage(enGB))) - - const ast1 = parser1.parse('=SUM(1, 2)', adr('A1')).ast as ProcedureAst - const ast2 = parser2.parse('=SUM(1; 2)', adr('A1')).ast as ProcedureAst - - expect(ast1.type).toBe(AstNodeType.FUNCTION_CALL) - expect(ast2.type).toBe(AstNodeType.FUNCTION_CALL) - expect(ast1).toEqual(ast2) - }) - - it('with default config should return error for non-breakable space', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const { ast, errors } = parser.parse('=\u00A042', adr('A1')) - - expect(ast.type).toBe(AstNodeType.ERROR) - expect(errors[0].type).toBe(ParsingErrorType.LexingError) - }) - - it('when set ignoreWhiteSpace = \'any\' should accept a non-breakable space', () => { - const parser = buildEmptyParserWithCaching(new Config({ ignoreWhiteSpace: 'any' })) - - const { ast } = parser.parse('=\u00A042', adr('A1')) - - expect(ast.type).toEqual(AstNodeType.NUMBER) - expect(ast.leadingWhitespace).toEqual('\u00A0') - }) - - it('error literal', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=#REF!', adr('A1')).ast - expect(ast).toEqual(buildCellErrorAst(new CellError(ErrorType.REF))) - }) - - it('error literals are case insensitive', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=#rEf!', adr('A1')).ast - expect(ast).toEqual(buildCellErrorAst(new CellError(ErrorType.REF))) - }) - - it('reference to address in nonexisting sheet returns cell reference ast', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - const ast = parser.parse('=Sheet2!A1', adr('A1')).ast - - expect(ast).toEqual(buildCellReferenceAst(CellAddress.relative(0, 0, 1))) - }) -}) - -describe('Functions', () => { - beforeEach(() => { - unregisterAllLanguages() - HyperFormula.registerLanguage(plPL.langCode, plPL) - HyperFormula.registerLanguage(enGB.langCode, enGB) - }) - - it('SUM function without args', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const ast = parser.parse('=SUM()', adr('A1')).ast as ProcedureAst - expect(ast.type).toBe(AstNodeType.FUNCTION_CALL) - expect(ast.procedureName).toBe('SUM') - expect(ast.args.length).toBe(0) - }) - - it('SUM function with args', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const ast = parser.parse('=SUM(1, A1)', adr('A1')).ast as ProcedureAst - expect(ast.type).toBe(AstNodeType.FUNCTION_CALL) - expect(ast.procedureName).toBe('SUM') - expect(ast.args[0].type).toBe(AstNodeType.NUMBER) - expect(ast.args[1].type).toBe(AstNodeType.CELL_REFERENCE) - }) - - it('SUM function with expression arg', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const ast = parser.parse('=SUM(1 / 2 + SUM(1,2))', adr('A1')).ast as ProcedureAst - expect(ast.type).toBe(AstNodeType.FUNCTION_CALL) - expect(ast.args.length).toBe(1) - expect(ast.args[0].type).toBe(AstNodeType.PLUS_OP) - - const arg = ast.args[0] as PlusOpAst - expect(arg.left.type).toBe(AstNodeType.DIV_OP) - expect(arg.right.type).toBe(AstNodeType.FUNCTION_CALL) - }) - - it('function without polish characters', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const ast = parser.parse('=żółćąęźśńŻÓŁĆĄĘŹŚŃ()', adr('A1')).ast as ProcedureAst - expect(ast.type).toBe(AstNodeType.FUNCTION_CALL) - expect(ast.procedureName).toBe('ŻÓŁĆĄĘŹŚŃŻÓŁĆĄĘŹŚŃ') - expect(ast.args.length).toBe(0) - }) - - it('function with dot separator', () => { - const parser = buildEmptyParserWithCaching(new Config({language: 'plPL'}), new SheetMapping(buildTranslationPackage(plPL))) - const ast = parser.parse('=NR.SER.OST.DN.MIEŚ()', adr('A1')).ast as ProcedureAst - expect(ast.type).toBe(AstNodeType.FUNCTION_CALL) - expect(ast.procedureName).toBe('EOMONTH') - expect(ast.args.length).toBe(0) - }) - - it('function name should be translated during parsing', () => { - const parser = buildEmptyParserWithCaching(new Config({language: 'plPL'}), new SheetMapping(buildTranslationPackage(plPL))) - const ast = parser.parse('=SUMA()', adr('A1')).ast as ProcedureAst - expect(ast.type).toBe(AstNodeType.FUNCTION_CALL) - expect(ast.procedureName).toBe('SUM') - expect(ast.args.length).toBe(0) - }) - - it('function with number', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const ast = parser.parse('=DEC2BIN(4)', adr('A1')).ast as ProcedureAst - expect(ast.type).toBe(AstNodeType.FUNCTION_CALL) - expect(ast.procedureName).toEqual('DEC2BIN') - }) - - it('should leave original name if procedure translation not known', () => { - const parser = buildEmptyParserWithCaching(new Config({language: 'plPL'}), new SheetMapping(buildTranslationPackage(plPL))) - const ast = parser.parse('=FOOBAR()', adr('A1')).ast as ProcedureAst - expect(ast.type).toBe(AstNodeType.FUNCTION_CALL) - expect(ast.procedureName).toBe('FOOBAR') - expect(ast.args.length).toBe(0) - }) - - it('should be case insensitive', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const ast = parser.parse('=sum(1)', adr('A1')).ast as ProcedureAst - expect(ast.type).toBe(AstNodeType.FUNCTION_CALL) - expect(ast.procedureName).toBe('SUM') - }) - - it('should be a valid function name', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - expect((parser.parse('=A()', adr('A1')).ast as ProcedureAst).procedureName).toEqual('A') - expect((parser.parse('=AA()', adr('A1')).ast as ProcedureAst).procedureName).toEqual('AA') - expect((parser.parse('=A.B()', adr('A1')).ast as ProcedureAst).procedureName).toEqual('A.B') - expect((parser.parse('=A.B.C()', adr('A1')).ast as ProcedureAst).procedureName).toEqual('A.B.C') - expect((parser.parse('=A_B()', adr('A1')).ast as ProcedureAst).procedureName).toEqual('A_B') - expect((parser.parse('=A42()', adr('A1')).ast as ProcedureAst).procedureName).toEqual('A42') - }) -}) - -describe('cell references and ranges', () => { - it('absolute cell reference', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=$B$3', adr('B2')).ast - - expect(ast).toEqual(buildCellReferenceAst(CellAddress.absolute(1, 2))) - }) - - it('relative cell reference', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=B3', adr('B2')).ast - - expect(ast).toEqual(buildCellReferenceAst(CellAddress.relative(0, 1))) - }) - - it('absolute column cell reference', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=$B3', adr('B2')).ast - - expect(ast).toEqual(buildCellReferenceAst(CellAddress.absoluteCol(1, 1))) - }) - - it('absolute row cell reference', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=B$3', adr('B2')).ast - - expect(ast).toEqual(buildCellReferenceAst(CellAddress.absoluteRow(0, 2))) - }) - - it('cell references should not be case sensitive', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=d1', adr('A1')).ast - - expect(ast).toEqual(buildCellReferenceAst(CellAddress.relative(3, 0))) - }) - - it('cell reference by default has sheet from the sheet it is written', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const ast = parser.parse('=D1', adr('A1', 1)).ast - - expect(ast).toEqual(buildCellReferenceAst(CellAddress.relative(3, 0))) - }) - - it('cell reference with sheet name', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const ast = parser.parse('=Sheet2!D1', adr('A1')).ast - - expect(ast).toEqual(buildCellReferenceAst(CellAddress.relative(3, 0, 1))) - }) - - it('attempting to fetch an unknown sheet throws error', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - - const sheetName = 'Sheet3' - - expect(() => { - sheetMapping.getSheetIdOrThrowError('Sheet3') - }).toThrow(new NoSheetWithNameError(sheetName)) - }) - - it('using unknown sheet gives cell reference ast', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=Sheet2!A1', adr('A1')).ast - - expect(ast).toEqual(buildCellReferenceAst(CellAddress.relative(0, 0, 0))) - }) - - it('sheet name with other characters', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet_zażółć_gęślą_jaźń') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const ast = parser.parse('=Sheet_zażółć_gęślą_jaźń!A1', adr('A1')).ast as CellReferenceAst - expect(ast.type).toBe(AstNodeType.CELL_REFERENCE) - expect(ast.reference.sheet).toBe(1) - }) - - it('sheet name is case insensitive', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const ast = parser.parse('=shEEt2!A1', adr('A1')).ast as CellReferenceAst - expect(ast.type).toBe(AstNodeType.CELL_REFERENCE) - expect(ast.reference.sheet).toBe(1) - }) - - it('sheet name with spaces', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet with spaces') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const ast = parser.parse("='Sheet with spaces'!A1", adr('A1')).ast as CellReferenceAst - expect(ast.type).toBe(AstNodeType.CELL_REFERENCE) - expect(ast.reference.sheet).toBe(1) - }) - - it('sheet name inside quotes', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const ast = parser.parse("='Sheet2'!A1", adr('A1')).ast as CellReferenceAst - expect(ast.type).toBe(AstNodeType.CELL_REFERENCE) - expect(ast.reference.sheet).toBe(1) - }) - - it('sheet name inside quotes with special characters', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('~`!@#$%^&*()_-+_=/|?{}[]\"') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const ast = parser.parse("='~`!@#$%^&*()_-+_=/|?{}[]\"'!A2", adr('A1')).ast as CellReferenceAst - expect(ast.type).toBe(AstNodeType.CELL_REFERENCE) - expect(ast.reference.sheet).toBe(1) - }) - - it('sheet name inside quotes with escaped quote', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet("Name'with'quotes") - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const ast = parser.parse("='Name''with''quotes'!A1", adr('A1')).ast as CellReferenceAst - expect(ast.type).toBe(AstNodeType.CELL_REFERENCE) - expect(ast.reference.sheet).toBe(1) - }) - - it('simple cell range', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=A1:B2', adr('A1')).ast as CellRangeAst - expect(ast.type).toBe(AstNodeType.CELL_RANGE) - }) - - it('cell range with both start and end sheets specified', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const ast = parser.parse('=Sheet2!A1:Sheet2!B2', adr('A1')).ast as CellRangeAst - - expect(ast.type).toBe(AstNodeType.CELL_RANGE) - expect(ast.start.sheet).toEqual(1) - expect(ast.end.sheet).toEqual(1) - }) - - it('cell range may have only sheet specified in start address but end of range is also absolute', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const ast = parser.parse('=Sheet2!A1:B2', adr('A1')).ast as CellRangeAst - - expect(ast.type).toBe(AstNodeType.CELL_RANGE) - expect(ast.start.sheet).toEqual(1) - expect(ast.end.sheet).toEqual(1) - }) - - it('cell range with absolute sheet only on end side is a parsing error', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const {errors} = parser.parse('=A1:Sheet2!B2', adr('A1')) - - expect(errors[0].type).toBe(ParsingErrorType.ParserError) - }) - - it('cell range with different start and end sheets', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - sheetMapping.addSheet('Sheet3') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const ast = parser.parse('=Sheet2!A1:Sheet3!B2', adr('A1')).ast as CellRangeAst - - expect(ast.type).toBe(AstNodeType.CELL_RANGE) - expect(ast.start.sheet).toEqual(1) - expect(ast.end.sheet).toEqual(2) - }) - - it('offset has relative sheet reference', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - const ast = parser.parse('=OFFSET(A1, 1, 2)', adr('A1')).ast as CellReferenceAst - - expect(ast.reference.sheet).toBe(undefined) - }) - - it('cell range with nonexsiting start sheet should return cell range ast', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const ast = parser.parse('=Sheet3!A1:Sheet2!B2', adr('A1')).ast - - expect(ast).toEqual(buildCellRangeAst(CellAddress.relative(0, 0, 1), CellAddress.relative(1, 1, 2), RangeSheetReferenceType.BOTH_ABSOLUTE)) - }) - - it('cell range with nonexsiting end sheet should return cell range ast', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - const ast = parser.parse('=Sheet2!A1:Sheet3!B2', adr('A1')).ast - - expect(ast).toEqual(buildCellRangeAst(CellAddress.relative(0, 0, 1), CellAddress.relative(1, 1, 2), RangeSheetReferenceType.BOTH_ABSOLUTE)) - }) - - it('cell reference beyond maximum row limit is #NAME', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - const maxRow = Config.defaultConfig.maxRows - - const maxRowAst = parser.parse(`=A${maxRow}`, adr('A1')).ast as CellReferenceAst - const maxRowPlusOneAst = parser.parse(`=A${maxRow + 1}`, adr('A1')).ast - - expect(maxRowAst.type).toEqual(AstNodeType.CELL_REFERENCE) - expect(maxRowAst.reference.row).toEqual(maxRow - 1) - expect(maxRowPlusOneAst).toEqual(buildErrorWithRawInputAst(`A${maxRow + 1}`, new CellError(ErrorType.NAME))) - }) - - it('cell reference beyond maximum column limit is #NAME', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - const maxColumns = Config.defaultConfig.maxColumns - - const maxColumnAst = parser.parse(`=${columnIndexToLabel(maxColumns - 1)}1`, adr('A1')).ast as CellReferenceAst - const maxColumnPlusOneAst = parser.parse(`=${columnIndexToLabel(maxColumns)}1`, adr('A1')).ast - - expect(maxColumnAst.type).toEqual(AstNodeType.CELL_REFERENCE) - expect(maxColumnAst.reference.col).toEqual(maxColumns - 1) - expect(maxColumnPlusOneAst).toEqual(buildErrorWithRawInputAst(`${columnIndexToLabel(maxColumns)}1`, new CellError(ErrorType.NAME))) - }) - - it('cell range start beyond maximum column/row limit is #NAME', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - const maxRow = Config.defaultConfig.maxRows - const maxColumns = Config.defaultConfig.maxColumns - - const ast1 = parser.parse(`=A${maxRow + 1}:B2`, adr('A1')).ast - const ast2 = parser.parse(`=${columnIndexToLabel(maxColumns)}1:B2`, adr('A1')).ast - - expect(ast1).toEqual(buildErrorWithRawInputAst(`A${maxRow + 1}:B2`, new CellError(ErrorType.NAME))) - expect(ast2).toEqual(buildErrorWithRawInputAst(`${columnIndexToLabel(maxColumns)}1:B2`, new CellError(ErrorType.NAME))) - }) - - it('cell range end beyond maximum column/row limit is #NAME', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - const maxRow = Config.defaultConfig.maxRows - const maxColumns = Config.defaultConfig.maxColumns - - const ast1 = parser.parse(`=A1:B${maxRow + 1}`, adr('A1')).ast - const ast2 = parser.parse(`=A1:${columnIndexToLabel(maxColumns)}1`, adr('A1')).ast - - expect(ast1).toEqual(buildErrorWithRawInputAst(`A1:B${maxRow + 1}`, new CellError(ErrorType.NAME))) - expect(ast2).toEqual(buildErrorWithRawInputAst(`A1:${columnIndexToLabel(maxColumns)}1`, new CellError(ErrorType.NAME))) - }) - - describe('reversed range', () => { - it('relative', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const notReversedAst = parser.parse('=A1:B2', adr('A1')).ast as CellRangeAst - - ['=B2:A1', '=B1:A2', '=A2:B1'].forEach(formula => { - const ast = parser.parse(formula, adr('A1')).ast as CellRangeAst - expect(ast).toEqual(notReversedAst) - }) - }) - - it('with absolute addressing', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - let notReversedAst = parser.parse('=$A1:B2', adr('A1')).ast as CellRangeAst - ['=$A2:B1', '=B1:$A2', '=B2:$A1'].forEach(formula => { - const ast = parser.parse(formula, adr('A1')).ast as CellRangeAst - expect(ast).toEqual(notReversedAst) - }) - - notReversedAst = parser.parse('=A$1:B2', adr('A1')).ast as CellRangeAst - ['=A2:B$1', '=B$1:A2', '=B2:A$1'].forEach(formula => { - const ast = parser.parse(formula, adr('A1')).ast as CellRangeAst - expect(ast).toEqual(notReversedAst) - }) - - notReversedAst = parser.parse('=A1:$B2', adr('A1')).ast as CellRangeAst - ['=A2:$B1', '=$B1:A2', '=$B2:A1'].forEach(formula => { - const ast = parser.parse(formula, adr('A1')).ast as CellRangeAst - expect(ast).toEqual(notReversedAst) - }) - - notReversedAst = parser.parse('=A1:B$2', adr('A1')).ast as CellRangeAst - ['=A$2:B1', '=B1:A$2', '=B$2:A1'].forEach(formula => { - const ast = parser.parse(formula, adr('A1')).ast as CellRangeAst - expect(ast).toEqual(notReversedAst) - }) - - notReversedAst = parser.parse('=$A$1:B2', adr('A1')).ast as CellRangeAst - ['=$A2:B$1', '=B$1:$A2', '=B2:$A$1'].forEach(formula => { - const ast = parser.parse(formula, adr('A1')).ast as CellRangeAst - expect(ast).toEqual(notReversedAst) - }) - - notReversedAst = parser.parse('=$A1:B$2', adr('A1')).ast as CellRangeAst - ['=$A$2:B1', '=B1:$A$2', '=B$2:$A1'].forEach(formula => { - const ast = parser.parse(formula, adr('A1')).ast as CellRangeAst - expect(ast).toEqual(notReversedAst) - }) - }) - - it('with sheets specified', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet0') - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - let ast = parser.parse('=Sheet1!B2:A1', adr('A1')).ast as CellRangeAst - expect(ast.type).toBe(AstNodeType.CELL_RANGE) - expect(ast.start.sheet).toEqual(1) - expect(ast.end.sheet).toEqual(1) - - const res = parser.parse('=B2:Sheet1!A1', adr('A1')) - expect(res.errors[0].type).toBe(ParsingErrorType.ParserError) - - ast = parser.parse('=Sheet1!B2:Sheet1!A1', adr('A1')).ast as CellRangeAst - expect(ast.type).toBe(AstNodeType.CELL_RANGE) - expect(ast.start.sheet).toEqual(1) - expect(ast.end.sheet).toEqual(1) - - ast = parser.parse('=Sheet1!B2:Sheet2!A1', adr('A1')).ast as CellRangeAst - expect(ast.type).toBe(AstNodeType.CELL_RANGE) - expect(ast.start.sheet).toEqual(1) - expect(ast.end.sheet).toEqual(2) - - ast = parser.parse('=Sheet2!B2:Sheet1!A1', adr('A1')).ast as CellRangeAst - expect(ast.type).toBe(AstNodeType.CELL_RANGE) - expect(ast.start.sheet).toEqual(1) - expect(ast.end.sheet).toEqual(2) - }) - - it('with OFFSET', () => { - const parser = buildEmptyParserWithCaching(new Config()) - const notReversedAst = parser.parse('=A1:B2', adr('A1')).ast as CellRangeAst - - ['=B2:OFFSET(A1, 0, 0)', '=OFFSET(B2, 0, 0):A1', '=OFFSET(B2, 0, 0):OFFSET(A1, 0, 0)', '=OFFSET(A2, 0, 0):OFFSET(B1, 0, 0)'].forEach(formula => { - const ast = parser.parse(formula, adr('A1')).ast as CellRangeAst - expect(ast).toEqual(notReversedAst) - }) - }) - }) -}) - -describe('Column ranges', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - it('column range', () => { - const ast = parser.parse('=C:D', adr('A1')).ast - expect(ast).toEqual(buildColumnRangeAst(ColumnAddress.relative(2), ColumnAddress.relative(3), RangeSheetReferenceType.RELATIVE)) - }) - - it('column range with sheet absolute', () => { - const ast = parser.parse('=Sheet1!C:D', adr('A1')).ast - expect(ast).toEqual(buildColumnRangeAst(ColumnAddress.relative(2, 0), ColumnAddress.relative(3, 0), RangeSheetReferenceType.START_ABSOLUTE)) - }) - - it('column range with both sheets absolute - same sheet', () => { - const ast = parser.parse('=Sheet1!C:Sheet1!D', adr('A1')).ast - expect(ast).toEqual(buildColumnRangeAst(ColumnAddress.relative(2, 0), ColumnAddress.relative(3, 0), RangeSheetReferenceType.BOTH_ABSOLUTE)) - }) - - it('column range with both sheets absolute - different sheet', () => { - const ast = parser.parse('=Sheet1!C:Sheet2!D', adr('A1')).ast - expect(ast).toEqual(buildColumnRangeAst(ColumnAddress.relative(2, 0), ColumnAddress.relative(3, 1), RangeSheetReferenceType.BOTH_ABSOLUTE)) - }) - - it('column range with absolute column address', () => { - const ast = parser.parse('=$C:D', adr('A1')).ast - expect(ast).toEqual(buildColumnRangeAst(ColumnAddress.absolute(2), ColumnAddress.relative(3), RangeSheetReferenceType.RELATIVE)) - }) - - it('column range with absolute sheet only on end side is a parsing error', () => { - const {errors} = parser.parse('=A:Sheet2!B', adr('A1')) - expect(errors[0].type).toBe(ParsingErrorType.ParserError) - }) - - it('column range beyond size limits is #NAME', () => { - const maxColumns = Config.defaultConfig.maxColumns - const ast1 = parser.parse(`=A:${columnIndexToLabel(maxColumns - 1)}`, adr('A1')).ast as ColumnRangeAst - const ast2 = parser.parse(`=A:${columnIndexToLabel(maxColumns)}`, adr('A1')).ast - const ast3 = parser.parse(`=${columnIndexToLabel(maxColumns)}:B`, adr('A1')).ast - - expect(ast1.type).toEqual(AstNodeType.COLUMN_RANGE) - expect(ast2).toEqual(buildErrorWithRawInputAst(`A:${columnIndexToLabel(maxColumns)}`, new CellError(ErrorType.NAME))) - expect(ast3).toEqual(buildErrorWithRawInputAst(`${columnIndexToLabel(maxColumns)}:B`, new CellError(ErrorType.NAME))) - }) - - it('reversed column range', () => { - let notReversedAst = parser.parse('=A:B', adr('A1')).ast - let reversedAst = parser.parse('=B:A', adr('A1')).ast - expect(reversedAst).toEqual(notReversedAst) - - notReversedAst = parser.parse('=$A:B', adr('A1')).ast - reversedAst = parser.parse('=B:$A', adr('A1')).ast - expect(reversedAst).toEqual(notReversedAst) - - notReversedAst = parser.parse('=A:$B', adr('A1')).ast - reversedAst = parser.parse('=$B:A', adr('A1')).ast - expect(reversedAst).toEqual(notReversedAst) - - reversedAst = parser.parse('=Sheet1!B:A', adr('A1')).ast as ColumnRangeAst - expect(reversedAst.type).toEqual(AstNodeType.COLUMN_RANGE) - expect(reversedAst.start.sheet).toEqual(0) - expect(reversedAst.end.sheet).toEqual(0) - expect(reversedAst.sheetReferenceType).toEqual(RangeSheetReferenceType.START_ABSOLUTE) - - const res = parser.parse('=B:Sheet1!A', adr('A1')) - expect(res.errors[0].type).toBe(ParsingErrorType.ParserError) - - reversedAst = parser.parse('=Sheet1!B:Sheet2!A', adr('A1')).ast as ColumnRangeAst - expect(reversedAst.type).toEqual(AstNodeType.COLUMN_RANGE) - expect(reversedAst.start.sheet).toEqual(0) - expect(reversedAst.end.sheet).toEqual(1) - expect(reversedAst.sheetReferenceType).toEqual(RangeSheetReferenceType.BOTH_ABSOLUTE) - - reversedAst = parser.parse('=Sheet1!B:Sheet1!A', adr('A1')).ast as ColumnRangeAst - expect(reversedAst.type).toEqual(AstNodeType.COLUMN_RANGE) - expect(reversedAst.start.sheet).toEqual(0) - expect(reversedAst.end.sheet).toEqual(0) - expect(reversedAst.sheetReferenceType).toEqual(RangeSheetReferenceType.BOTH_ABSOLUTE) - - reversedAst = parser.parse('=Sheet2!B:Sheet1!A', adr('A1')).ast as ColumnRangeAst - expect(reversedAst.type).toEqual(AstNodeType.COLUMN_RANGE) - expect(reversedAst.start.sheet).toEqual(0) - expect(reversedAst.end.sheet).toEqual(1) - expect(reversedAst.sheetReferenceType).toEqual(RangeSheetReferenceType.BOTH_ABSOLUTE) - }) -}) - -describe('Row ranges', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - - it('row range', () => { - const ast = parser.parse('=3:4', adr('A1')).ast - expect(ast).toEqual(buildRowRangeAst(RowAddress.relative(2), RowAddress.relative(3), RangeSheetReferenceType.RELATIVE)) - }) - - it('row range with sheet absolute', () => { - const ast = parser.parse('=Sheet1!3:4', adr('A1')).ast - expect(ast).toEqual(buildRowRangeAst(RowAddress.relative(2, 0), RowAddress.relative(3, 0), RangeSheetReferenceType.START_ABSOLUTE)) - }) - - it('row range with both sheets absolute - same sheet', () => { - const ast = parser.parse('=Sheet1!3:Sheet1!4', adr('A1')).ast - expect(ast).toEqual(buildRowRangeAst(RowAddress.relative(2, 0), RowAddress.relative(3, 0), RangeSheetReferenceType.BOTH_ABSOLUTE)) - }) - - it('row range with both sheets absolute - different sheet', () => { - const ast = parser.parse('=Sheet1!3:Sheet2!4', adr('A1')).ast - expect(ast).toEqual(buildRowRangeAst(RowAddress.relative(2, 0), RowAddress.relative(3, 1), RangeSheetReferenceType.BOTH_ABSOLUTE)) - }) - - it('row range with absolute row address', () => { - const ast = parser.parse('=$3:4', adr('A1')).ast - expect(ast).toEqual(buildRowRangeAst(RowAddress.absolute(2), RowAddress.relative(3), RangeSheetReferenceType.RELATIVE)) - }) - - it('row range with absolute sheet only on end side is a parsing error', () => { - const {errors} = parser.parse('=1:Sheet2!2', adr('A1')) - expect(errors[0].type).toBe(ParsingErrorType.ParserError) - }) - - it('reversed row range', () => { - let notReversedAst = parser.parse('=1:2', adr('A1')).ast - let reversedAst = parser.parse('=2:1', adr('A1')).ast - expect(reversedAst).toEqual(notReversedAst) - - notReversedAst = parser.parse('=$1:2', adr('A1')).ast - reversedAst = parser.parse('=2:$1', adr('A1')).ast - expect(reversedAst).toEqual(notReversedAst) - - notReversedAst = parser.parse('=1:$2', adr('A1')).ast - reversedAst = parser.parse('=$2:1', adr('A1')).ast - expect(reversedAst).toEqual(notReversedAst) - - reversedAst = parser.parse('=Sheet1!2:1', adr('A1')).ast as RowRangeAst - expect(reversedAst.type).toEqual(AstNodeType.ROW_RANGE) - expect(reversedAst.start.sheet).toEqual(0) - expect(reversedAst.end.sheet).toEqual(0) - expect(reversedAst.sheetReferenceType).toEqual(RangeSheetReferenceType.START_ABSOLUTE) - - const res = parser.parse('=2:Sheet1!1', adr('A1')) - expect(res.errors[0].type).toBe(ParsingErrorType.ParserError) - - reversedAst = parser.parse('=Sheet1!2:Sheet2!1', adr('A1')).ast as RowRangeAst - expect(reversedAst.type).toEqual(AstNodeType.ROW_RANGE) - expect(reversedAst.start.sheet).toEqual(0) - expect(reversedAst.end.sheet).toEqual(1) - expect(reversedAst.sheetReferenceType).toEqual(RangeSheetReferenceType.BOTH_ABSOLUTE) - - reversedAst = parser.parse('=Sheet1!2:Sheet1!1', adr('A1')).ast as RowRangeAst - expect(reversedAst.type).toEqual(AstNodeType.ROW_RANGE) - expect(reversedAst.start.sheet).toEqual(0) - expect(reversedAst.end.sheet).toEqual(0) - expect(reversedAst.sheetReferenceType).toEqual(RangeSheetReferenceType.BOTH_ABSOLUTE) - - reversedAst = parser.parse('=Sheet2!2:Sheet1!1', adr('A1')).ast as RowRangeAst - expect(reversedAst.type).toEqual(AstNodeType.ROW_RANGE) - expect(reversedAst.start.sheet).toEqual(0) - expect(reversedAst.end.sheet).toEqual(1) - expect(reversedAst.sheetReferenceType).toEqual(RangeSheetReferenceType.BOTH_ABSOLUTE) - }) -}) - -describe('Named expressions', () => { - it('should be a valid name for named expression', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - expect((parser.parse('=_A', adr('A1')).ast as NamedExpressionAst).expressionName).toEqual('_A') - expect((parser.parse('=A', adr('A1')).ast as NamedExpressionAst).expressionName).toEqual('A') - expect((parser.parse('=Aa', adr('A1')).ast as NamedExpressionAst).expressionName).toEqual('Aa') - expect((parser.parse('=B.', adr('A1')).ast as NamedExpressionAst).expressionName).toEqual('B.') - expect((parser.parse('=foo_bar', adr('A1')).ast as NamedExpressionAst).expressionName).toEqual('foo_bar') - expect((parser.parse('=A...', adr('A1')).ast as NamedExpressionAst).expressionName).toEqual('A...') - expect((parser.parse('=B___', adr('A1')).ast as NamedExpressionAst).expressionName).toEqual('B___') - }) - - it('named expression ast with leading whitespace', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('= true', adr('A1')).ast as NamedExpressionAst - - expect(ast.type).toBe(AstNodeType.NAMED_EXPRESSION) - expect(ast.expressionName).toBe('true') - expect(ast.leadingWhitespace).toBe(' ') - }) -}) - -describe('Matrices', () => { - it('simplest matrix', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('={1}', adr('A1')).ast as ArrayAst - expect(ast.type).toBe(AstNodeType.ARRAY) - expect(ast.args.length).toEqual(1) - expect(ast.args[0].length).toEqual(1) - }) - - it('row matrix', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('={1,2,3}', adr('A1')).ast as ArrayAst - expect(ast.type).toBe(AstNodeType.ARRAY) - expect(ast.args.length).toEqual(1) - expect(ast.args[0].length).toEqual(3) - }) - - it('column matrix', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('={1;2;3}', adr('A1')).ast as ArrayAst - expect(ast.type).toBe(AstNodeType.ARRAY) - expect(ast.args.length).toEqual(3) - expect(ast.args[0].length).toEqual(1) - expect(ast.args[1].length).toEqual(1) - expect(ast.args[2].length).toEqual(1) - }) - - it('square matrix', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('={1,2,3;4,5,6;7,8,9}', adr('A1')).ast as ArrayAst - expect(ast.type).toBe(AstNodeType.ARRAY) - expect(ast.args.length).toEqual(3) - expect(ast.args[0].length).toEqual(3) - expect(ast.args[1].length).toEqual(3) - expect(ast.args[2].length).toEqual(3) - }) - - it('longer matrix with extras', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('={SUM(1, 2, 3), 2, {1,2,3}}', adr('A1')).ast as ArrayAst - expect(ast.type).toBe(AstNodeType.ARRAY) - expect(ast.args.length).toEqual(1) - expect(ast.args[0].length).toEqual(3) - const ast2 = ast.args[0][2] as ArrayAst - expect(ast2.type).toBe(AstNodeType.ARRAY) - expect(ast2.args.length).toEqual(1) - expect(ast2.args[0].length).toEqual(3) - }) - - it('matrix in other expressions', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=1+{1,2,3}', adr('A1')).ast as PlusOpAst - const ast2 = ast.right as ArrayAst - expect(ast2.type).toBe(AstNodeType.ARRAY) - expect(ast2.args.length).toEqual(1) - expect(ast2.args[0].length).toEqual(3) - }) - - it('square matrix, other separators', () => { - const parser = buildEmptyParserWithCaching(new Config({arrayRowSeparator: '|', arrayColumnSeparator: ';'})) - - const ast = parser.parse('={1;2;3|4;5;6|7;8;9}', adr('A1')).ast as ArrayAst - expect(ast.type).toBe(AstNodeType.ARRAY) - expect(ast.args.length).toEqual(3) - expect(ast.args[0].length).toEqual(3) - expect(ast.args[1].length).toEqual(3) - expect(ast.args[2].length).toEqual(3) - }) -}) - -describe('Parsing errors', () => { - it('errors - lexing errors', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const input = ["='foo'", "=foo'bar", "=''''''", '=@'] - - input.forEach((formula) => { - const {ast, errors} = parser.parse(formula, adr('A1')) - expect(ast.type).toBe(AstNodeType.ERROR) - expect(errors[0].type).toBe(ParsingErrorType.LexingError) - }) - }) - - it('parsing error - not all input parsed', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {errors} = parser.parse('=1A1', adr('A1')) - expect(errors[0].type).toBe(ParsingErrorType.ParserError) - }) - - it('unknown error literal', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {ast, errors} = parser.parse('=#FOO!', adr('A1')) - expect(ast.type).toBe(AstNodeType.ERROR) - expect(errors[0].type).toBe(ParsingErrorType.ParserError) - }) -}) diff --git a/test/unit/parser/percent-operator.spec.ts b/test/unit/parser/percent-operator.spec.ts deleted file mode 100644 index 1492bd6b72..0000000000 --- a/test/unit/parser/percent-operator.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import {Config} from '../../../src/Config' -import {AstNodeType, MinusUnaryOpAst, PlusOpAst, PlusUnaryOpAst} from '../../../src/parser' -import {PercentOpAst, TimesOpAst} from '../../../src/parser/Ast' -import {adr} from '../testUtils' -import {buildEmptyParserWithCaching} from './common' - -describe('percent', () => { - it('should parse % as operator', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=1%', adr('A1')).ast as PercentOpAst - expect(ast.type).toBe(AstNodeType.PERCENT_OP) - expect(ast.value.type).toBe(AstNodeType.NUMBER) - }) - - it('% over unary minus', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=-1%', adr('A1')).ast as MinusUnaryOpAst - expect(ast.type).toBe(AstNodeType.MINUS_UNARY_OP) - expect(ast.value.type).toBe(AstNodeType.PERCENT_OP) - }) - - it('% over unary plus', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=+1%', adr('A1')).ast as PlusUnaryOpAst - expect(ast.type).toBe(AstNodeType.PLUS_UNARY_OP) - expect(ast.value.type).toBe(AstNodeType.PERCENT_OP) - }) - - it('% over addition op', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=42+1%', adr('A1')).ast as PlusOpAst - expect(ast.type).toBe(AstNodeType.PLUS_OP) - expect(ast.right.type).toBe(AstNodeType.PERCENT_OP) - }) - - it('% over multiplication op', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=42*1%', adr('A1')).ast as TimesOpAst - expect(ast.type).toBe(AstNodeType.TIMES_OP) - expect(ast.right.type).toBe(AstNodeType.PERCENT_OP) - }) - - it('% on the left', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=1%+42', adr('A1')).ast as PlusOpAst - expect(ast.type).toBe(AstNodeType.PLUS_OP) - expect(ast.left.type).toBe(AstNodeType.PERCENT_OP) - }) - - it('% after procedure', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=SUM(1,2)%', adr('A1')).ast as PercentOpAst - expect(ast.type).toBe(AstNodeType.PERCENT_OP) - expect(ast.value.type).toBe(AstNodeType.FUNCTION_CALL) - }) - - it('%% should not parse', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=100%%', adr('A1')).ast as PercentOpAst - expect(ast.type).toBe(AstNodeType.ERROR) - }) -}) diff --git a/test/unit/parser/range-offset.spec.ts b/test/unit/parser/range-offset.spec.ts deleted file mode 100644 index 859c48154d..0000000000 --- a/test/unit/parser/range-offset.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import {Config} from '../../../src/Config' -import {SheetMapping} from '../../../src/DependencyGraph' -import {buildTranslationPackage} from '../../../src/i18n' -import {enGB} from '../../../src/i18n/languages' -import {AstNodeType, CellRangeAst, ParsingErrorType} from '../../../src/parser' -import {RangeSheetReferenceType} from '../../../src/parser/Ast' -import {adr} from '../testUtils' -import {buildEmptyParserWithCaching} from './common' - -describe('Parser - range offset', () => { - it('OFFSET - usage with range', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const ast = parser.parse('=A1:OFFSET(A1, 1, 1, 1, 1)', adr('A1')).ast as CellRangeAst - const ast2 = parser.parse('=OFFSET(A1, 1, 1, 1, 1):OFFSET(B2, 1, 1, 1, 1)', adr('A1')).ast as CellRangeAst - const ast3 = parser.parse('=OFFSET(A1, 1, 1, 1, 1):B3', adr('A1')).ast as CellRangeAst - expect(ast.type).toBe(AstNodeType.CELL_RANGE) - expect(ast2.type).toBe(AstNodeType.CELL_RANGE) - expect(ast3.type).toBe(AstNodeType.CELL_RANGE) - }) - - it('OFFSET - range offset not allowed', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const {errors: errors1} = parser.parse('=A1:OFFSET(B2, 0, 0, 2, 2)', adr('A1')) - const {errors: errors2} = parser.parse('=OFFSET(A1,0,0,2,2):A2', adr('A1')) - - expect(errors1[0].type).toBe(ParsingErrorType.RangeOffsetNotAllowed) - expect(errors2[0].type).toBe(ParsingErrorType.RangeOffsetNotAllowed) - }) - - it('OFFSET - sheet reference in range with offset start is ABSOLUTE', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - const ast = parser.parse('=OFFSET(A1,0,0):OFFSET(B2,0,0)', adr('A1')).ast as CellRangeAst - - expect(ast.type).toEqual(AstNodeType.CELL_RANGE) - expect(ast.sheetReferenceType).toEqual(RangeSheetReferenceType.RELATIVE) - }) - - it('OFFSET - sheet reference in range with absolute start is START_ABSOLUTE', () => { - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - const parser = buildEmptyParserWithCaching(new Config(), sheetMapping) - const ast = parser.parse('=Sheet1!A1:OFFSET(B2, 0, 0)', adr('A1')).ast as CellRangeAst - - expect(ast.type).toEqual(AstNodeType.CELL_RANGE) - expect(ast.sheetReferenceType).toEqual(RangeSheetReferenceType.START_ABSOLUTE) - }) -}) diff --git a/test/unit/parser/unparse.spec.ts b/test/unit/parser/unparse.spec.ts deleted file mode 100644 index 60ec677362..0000000000 --- a/test/unit/parser/unparse.spec.ts +++ /dev/null @@ -1,612 +0,0 @@ -import {HyperFormula} from '../../../src' -import {Config} from '../../../src/Config' -import {SheetMapping} from '../../../src/DependencyGraph' -import {buildTranslationPackage} from '../../../src/i18n' -import {enGB, plPL} from '../../../src/i18n/languages' -import {NamedExpressions} from '../../../src/NamedExpressions' -import {AstNodeType, Unparser} from '../../../src/parser' -import {adr, unregisterAllLanguages} from '../testUtils' -import {buildEmptyParserWithCaching} from './common' - -describe('Unparse', () => { - const config = new Config({ maxRows: 10 }) - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - sheetMapping.addSheet('Sheet with spaces') - sheetMapping.addSheet("Sheet'With'Quotes") - const parser = buildEmptyParserWithCaching(config, sheetMapping) - const namedExpressions = new NamedExpressions() - const unparser = new Unparser(config, sheetMapping, namedExpressions) - - beforeEach(() => { - unregisterAllLanguages() - HyperFormula.registerLanguage(plPL.langCode, plPL) - HyperFormula.registerLanguage(enGB.langCode, enGB) - }) - - it('#unparse', () => { - const formula = '=1+SUM(1, 2, 3)*3' - const ast = parser.parse(formula, adr('A1')).ast - const unparsed = unparser.unparse(ast, adr('A1')) - expect(unparsed).toEqual(formula) - }) - - it('#unparse simple addreess', () => { - const formula = '=A1' - const ast = parser.parse(formula, adr('A1')).ast - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse simple addreess from other sheet', () => { - const formula = '=Sheet1!A1' - const ast = parser.parse(formula, adr('A1', 1)).ast - const unparsed = unparser.unparse(ast, adr('A1', 1)) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse absolute col', () => { - const formula = '=$A1' - const ast = parser.parse(formula, adr('A1')).ast - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse absolute row addreess', () => { - const formula = '=A$1' - const ast = parser.parse(formula, adr('A1')).ast - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse absolute address', () => { - const formula = '=$A$1' - const ast = parser.parse(formula, adr('A1')).ast - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse cell ref between strings', () => { - const formula = '="A5"+A4+"A6"' - const ast = parser.parse(formula, adr('A1')).ast - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse cell ref in string with escape', () => { - const formula = '="fdsaf\\"A5"' - const ast = parser.parse(formula, adr('A1')).ast - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse cell range from same sheet', () => { - const formula = '=$A$1:B$2' - const ast = parser.parse(formula, adr('A1')).ast - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse cell range from other sheet', () => { - const formula = '=Sheet1!$A$1:B$2' - const ast = parser.parse(formula, adr('A1', 1)).ast - const unparsed = unparser.unparse(ast, adr('A1', 1)) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse ops', () => { - const formula = '=-1+1-1*1/1^1&1=1<>1<1<=1>1<1' - const ast = parser.parse(formula, adr('A1')).ast - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse with unspecified error', () => { - const formula = '=1+' - const ast = parser.parse(formula, adr('A1')).ast - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=#ERROR!') - }) - - it('#unparse with known error', () => { - const formula = '=#REF!' - const ast = parser.parse(formula, adr('A1')).ast - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=#REF!') - }) - - it('#unparse error with data input', () => { - const cellReferenceExceedingMaxRowsLimit = '=A100' - const ast = parser.parse(cellReferenceExceedingMaxRowsLimit, adr('A1')).ast - - expect(ast.type).toEqual(AstNodeType.ERROR_WITH_RAW_INPUT) - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=A100') - }) - - it('#unparse with known error with translation', () => { - const config = new Config({language: 'plPL'}) - const parser = buildEmptyParserWithCaching(config, sheetMapping) - const unparser = new Unparser(config, sheetMapping, new NamedExpressions()) - const formula = '=#ADR!' - const ast = parser.parse(formula, adr('A1')).ast - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=#ADR!') - }) - - it('#unparse forgets about downcase', () => { - const formula = '=sum(1, 2, 3)' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=SUM(1, 2, 3)') - }) - - it('#unparse should not forget about spaces', () => { - const formula = '= 1 + sum( 1,2, 3) +A1 / 2 + bar' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('= 1 + SUM( 1,2, 3) +A1 / 2 + bar') - }) - - it('#unparse named expression', () => { - const formula = '=true' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=true') - }) - - it('#unparse named expression returns original form', () => { - const namedExpressions = new NamedExpressions() - namedExpressions.addNamedExpression('SomeWEIRD_name', undefined) - const unparser = new Unparser(config, sheetMapping, namedExpressions) - const formula = '=someWeird_Name' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=SomeWEIRD_name') - }) - - it('#unparse named expression use local version if available', () => { - const namedExpressions = new NamedExpressions() - namedExpressions.addNamedExpression('SomeWEIRD_name', undefined) - namedExpressions.addNamedExpression('SomeWEIRD_NAME', 0) - const unparser = new Unparser(config, sheetMapping, namedExpressions) - const formula = '=someWeird_Name' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=SomeWEIRD_NAME') - }) - - it('#unparse nonexisting named expression returns original input', () => { - const namedExpressions = new NamedExpressions() - const unparser = new Unparser(config, sheetMapping, namedExpressions) - const formula = '=someWeird_Name' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=someWeird_Name') - }) - - it('#unparse nonexisting named expression returns original input when global named expression is removed', () => { - const namedExpressions = new NamedExpressions() - namedExpressions.addNamedExpression('SomeWEIRD_name', undefined) - namedExpressions.remove('SomeWEIRD_name', undefined) - const unparser = new Unparser(config, sheetMapping, namedExpressions) - const formula = '=someWeird_Name' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=someWeird_Name') - }) - - it('#unparse forgets about OFFSET', () => { - const formula = '=OFFSET(C3, 1, 1)' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=D4') - }) - - it('#unparse doesnt forget about unnecessary parenthesis', () => { - const formula = '=(1+2)' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=(1+2)') - }) - - it('#unparse do not forgets about sheet reference', () => { - const formula = '=Sheet1!C3' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=Sheet1!C3') - }) - - it('#unparse necessary parenthesis from left subtree', () => { - const formula = '=(1+2)*3' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=(1+2)*3') - }) - - it('#unparse necessary parenthesis from right subtree', () => { - const formula = '=3*(1+2)' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=3*(1+2)') - }) - - it('#unparse doesnt use parenthesis for the same operations', () => { - const formula = '=4*3*2' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=4*3*2') - }) - - it('#unparse doesnt use parenthesis for different operations of same precedece', () => { - const formula1 = '=4/3*2' - const formula2 = '=4*3/2' - const ast1 = parser.parse(formula1, adr('A1')).ast - const ast2 = parser.parse(formula2, adr('A1')).ast - - const unparsed1 = unparser.unparse(ast1, adr('A1')) - const unparsed2 = unparser.unparse(ast2, adr('A1')) - - expect(unparsed1).toEqual(formula1) - expect(unparsed2).toEqual(formula2) - }) - - it('#unparse doesnt use parenthesis for functions or other non-operator node types', () => { - const formula1 = '=TRUE()*2' - const formula2 = '=2*TRUE()' - const ast1 = parser.parse(formula1, adr('A1')).ast - const ast2 = parser.parse(formula2, adr('A1')).ast - - const unparsed1 = unparser.unparse(ast1, adr('A1')) - const unparsed2 = unparser.unparse(ast2, adr('A1')) - - expect(unparsed1).toEqual(formula1) - expect(unparsed2).toEqual(formula2) - }) - - it('#unparse necessary parenthesis from subtree', () => { - const formula = '=-(3+4)' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse double unary minus', () => { - const formula = '=-(-(3+4))' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse doesnt use parenthesis for non-operator node types', () => { - const formula = '=-42' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse percent', () => { - const formula = '=42%' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse use language configuration', () => { - const configEN = new Config({language: enGB.langCode}) - const configPL = new Config({language: plPL.langCode}) - - const parser = buildEmptyParserWithCaching(configPL, sheetMapping) - - const unparserPL = new Unparser(configPL, sheetMapping, new NamedExpressions()) - const unparserEN = new Unparser(configEN, sheetMapping, new NamedExpressions()) - - const formula = '=SUMA(1, 2)' - - const ast = parser.parse(formula, adr('A1')).ast - - expect(unparserPL.unparse(ast, adr('A1'))).toEqual('=SUMA(1, 2)') - expect(unparserEN.unparse(ast, adr('A1'))).toEqual('=SUM(1, 2)') - }) - - it('unparsing sheet names in references sometimes have to wrap in quotes', () => { - const formula = "='Sheet with spaces'!A1" - const ast = parser.parse(formula, adr('A1', 1)).ast - const unparsed = unparser.unparse(ast, adr('A1', 1)) - - expect(unparsed).toEqual(formula) - }) - - it('unparsing sheet names in quotes should escape single quotes', () => { - const formula = "='Sheet''With''Quotes'!A1" - const ast = parser.parse(formula, adr('A1', 1)).ast - const unparsed = unparser.unparse(ast, adr('A1', 1)) - - expect(unparsed).toEqual(formula) - }) - - it('unparsing sheet names in ranges sometimes have to wrap in quotes', () => { - const formula = "='Sheet with spaces'!A1:B1" - const ast = parser.parse(formula, adr('A1')).ast - const unparsed = unparser.unparse(ast, adr('A1', 0)) - - expect(unparsed).toEqual(formula) - }) - - it('unparsing sheet name always returns its original name', () => { - const formula = '=shEET2!A1:B1' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1', 0)) - - expect(unparsed).toEqual('=Sheet2!A1:B1') - }) - - it('unparsing range with sheet name on both sides', () => { - const formula = '=Sheet1!A1:Sheet2!B1' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1', 0)) - - expect(unparsed).toEqual('=Sheet1!A1:Sheet2!B1') - }) - - it('unparsing function without translation should unparse to canonical name', () => { - const formula = '=FOOBAR(1, 2, 3)' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('=FOOBAR(1, 2, 3)') - }) - - it('unparsing numbers with decimal separator', () => { - const config = new Config({decimalSeparator: ',', functionArgSeparator: ';'}) - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - const parser = buildEmptyParserWithCaching(config, sheetMapping) - const unparser = new Unparser(config, sheetMapping, new NamedExpressions()) - const formula = '=1+1234,567' - - const ast = parser.parse(formula, adr('A1')).ast - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse column range', () => { - const formula = '=A:B' - const ast = parser.parse(formula, adr('A1', 1)).ast - const unparsed = unparser.unparse(ast, adr('A1', 1)) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse absolute column range', () => { - const formula = '=$A:B' - const ast = parser.parse(formula, adr('A1', 1)).ast - const unparsed = unparser.unparse(ast, adr('A1', 1)) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse column range from other sheet', () => { - const formula = '=Sheet1!$A:B' - const ast = parser.parse(formula, adr('A1', 1)).ast - const unparsed = unparser.unparse(ast, adr('A1', 1)) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse column range from other sheet - both sides', () => { - const formula = '=Sheet1!$A:Sheet1!B' - const ast = parser.parse(formula, adr('A1', 1)).ast - const unparsed = unparser.unparse(ast, adr('A1', 1)) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse row range', () => { - const formula = '=1:2' - const ast = parser.parse(formula, adr('A1', 1)).ast - const unparsed = unparser.unparse(ast, adr('A1', 1)) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse absolute row range', () => { - const formula = '=$1:2' - const ast = parser.parse(formula, adr('A1', 1)).ast - const unparsed = unparser.unparse(ast, adr('A1', 1)) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse row range from other sheet', () => { - const formula = '=Sheet1!$1:2' - const ast = parser.parse(formula, adr('A1', 1)).ast - const unparsed = unparser.unparse(ast, adr('A1', 1)) - - expect(unparsed).toEqual(formula) - }) - - it('#unparse row range from other sheet - both sides', () => { - const formula = '=Sheet1!$1:Sheet1!2' - const ast = parser.parse(formula, adr('A1', 1)).ast - const unparsed = unparser.unparse(ast, adr('A1', 1)) - - expect(unparsed).toEqual(formula) - }) -}) - -describe('whitespaces', () => { - const config = new Config() - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - sheetMapping.addSheet('Sheet2') - sheetMapping.addSheet('Sheet with spaces') - const parser = buildEmptyParserWithCaching(config, sheetMapping) - const unparser = new Unparser(config, sheetMapping, new NamedExpressions()) - - it('should unparse with original whitespaces', () => { - const formula = '= 1' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('should unparse string with original whitespaces', () => { - const formula = '= "foo"' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('should unparse reference with original whitespaces', () => { - const formula = '= A1+ Sheet2!A1' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('should unparse operators with original whitespaces', () => { - const formula = '= - 1 + 2 - 3 / 4 * 5 ^ 6 %' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('should unparse ranges with leading whitespaces only', () => { - const formula = '= A1 : A2' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('= A1:A2') - }) - - it('should unparse formula with leading and internal whitespace', () => { - const formula = '= SUM( A1, A2 )' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('should forget about spaces before func args separator', () => { - const formula = '= SUM( A1 , A2 )' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual('= SUM( A1, A2 )') - }) - - it('should unparse parenthesis with leading and internal whitespace', () => { - const formula = '=1 + ( A1 + A2 )' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('should unparse error literals with leading whitespaces', () => { - const formula = '= #DIV/0!' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('should unparse empty argument with whitespaces', () => { - const formula = '=PV(1,2,3, ,)' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('should unparse arrays with whitespaces', () => { - const formula = '= { 1, 2; 3, 4 }' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) - - it('when ignoreWhiteSpace = \'any\', should unparse a non-breakable space character', () => { - const config = new Config({ ignoreWhiteSpace: 'any' }) - const parser = buildEmptyParserWithCaching(config, sheetMapping) - const unparser = new Unparser(config, sheetMapping, new NamedExpressions()) - - const formula = '=\u00A01' - const ast = parser.parse(formula, adr('A1')).ast - - const unparsed = unparser.unparse(ast, adr('A1')) - - expect(unparsed).toEqual(formula) - }) -}) diff --git a/test/unit/parser/volatile-functions-detection.spec.ts b/test/unit/parser/volatile-functions-detection.spec.ts deleted file mode 100644 index 2482aabe17..0000000000 --- a/test/unit/parser/volatile-functions-detection.spec.ts +++ /dev/null @@ -1,63 +0,0 @@ -import {Config} from '../../../src/Config' -import {adr} from '../testUtils' -import {buildEmptyParserWithCaching} from './common' - -describe('ParserWithCaching - volatile functions detection', () => { - it('detects volatile functions', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const result = parser.parse('=RAND()', adr('A1')) - expect(result.hasVolatileFunction).toBe(true) - }) - - it('detects volatile functions inside other functions', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const result = parser.parse('=SUM(RAND())', adr('A1')) - expect(result.hasVolatileFunction).toBe(true) - }) - - it('detects volatile functions in unary operators', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const result = parser.parse('=-RAND()', adr('A1')) - expect(result.hasVolatileFunction).toBe(true) - }) - - it('detects volatile functions in right arg of binary operators', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const result = parser.parse('=42+RAND()', adr('A1')) - expect(result.hasVolatileFunction).toBe(true) - }) - - it('detects volatile functions in left arg of binary operators', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const result = parser.parse('=RAND()+42', adr('A1')) - expect(result.hasVolatileFunction).toBe(true) - }) - - it('not all functions are volatile', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const result = parser.parse('=SUM()', adr('A1')) - expect(result.hasVolatileFunction).toBe(false) - }) -}) - -describe('ParserWithCaching - structural change functions detection', () => { - it('detects volatile functions', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const result = parser.parse('=COLUMNS()', adr('A1')) - expect(result.hasStructuralChangeFunction).toBe(true) - }) - - it('not all functions are dependent on structure changes', () => { - const parser = buildEmptyParserWithCaching(new Config()) - - const result = parser.parse('=SUM()', adr('A1')) - expect(result.hasStructuralChangeFunction).toBe(false) - }) -}) diff --git a/test/unit/parser/white-spaces.spec.ts b/test/unit/parser/white-spaces.spec.ts deleted file mode 100644 index 39d8d3e9d9..0000000000 --- a/test/unit/parser/white-spaces.spec.ts +++ /dev/null @@ -1,163 +0,0 @@ -import {Config} from '../../../src/Config' -import {SheetMapping} from '../../../src/DependencyGraph' -import {buildTranslationPackage} from '../../../src/i18n' -import {enGB} from '../../../src/i18n/languages' -import {buildLexerConfig, FormulaLexer} from '../../../src/parser' -import {CellReference, EqualsOp, ProcedureName, RangeSeparator, RParen} from '../../../src/parser/LexerConfig' -import {expectArrayWithSameContent} from '../testUtils' -import {buildEmptyParserWithCaching} from './common' - -describe('tokenizeFormula', () => { - const config = new Config() - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - const lexer = new FormulaLexer(buildLexerConfig(config)) - - it('should not skip whitespaces', () => { - const tokens = lexer.tokenizeFormula('= A1 + 2').tokens - expect(tokens.length).toBe(7) - }) - - it('should forget about trailing whitespaces', () => { - const tokens = lexer.tokenizeFormula('=SUM(A1:A2) ').tokens - expect(tokens.map(token => token.tokenType.name)).not.toContain('WhiteSpace') - }) - - it('should skip whitespace inside range', () => { - const tokens = lexer.tokenizeFormula('=A1: A1').tokens - const tokenTypes = tokens.map(token => token.tokenType.name) - expectArrayWithSameContent(tokenTypes, ['EqualsOp', 'CellReference', 'RangeSeparator', 'CellReference']) - }) - - it('should skip whitespace inside range 2', () => { - const tokens = lexer.tokenizeFormula('=A1 :A1').tokens - const tokenTypes = tokens.map(token => token.tokenType.name) - expectArrayWithSameContent(tokenTypes, ['EqualsOp', 'CellReference', 'RangeSeparator', 'CellReference']) - }) - - it('should skip whitespace inside range 3', () => { - const tokens = lexer.tokenizeFormula('=A1 : A1').tokens - const tokenTypes = tokens.map(token => token.tokenType.name) - expectArrayWithSameContent(tokenTypes, ['EqualsOp', 'CellReference', 'RangeSeparator', 'CellReference']) - }) - - it('should not skip whitespaces before named expression', () => { - const tokens = lexer.tokenizeFormula('= A1 + TRUE').tokens - - const tokenTypes = tokens.map(token => token.tokenType.name) - expectArrayWithSameContent(tokenTypes, ['EqualsOp', 'WhiteSpace', 'CellReference', 'WhiteSpace', 'PlusOp', 'WhiteSpace', 'NamedExpression']) - }) - - it('should skip whitespace before function args separator', () => { - const tokens = lexer.tokenizeFormula('=SUM(A1 , A2)').tokens - const tokenTypes = tokens.map(token => token.tokenType.name) - - expectArrayWithSameContent(tokenTypes, ['EqualsOp', 'ProcedureName', 'CellReference', 'ArrayColSep', 'WhiteSpace', 'CellReference', 'RParen']) - }) - - it('should not skip whitespace when there is empty argument', () => { - const tokens = lexer.tokenizeFormula('=PV(A1 , ,A2)').tokens - const tokenTypes = tokens.map(token => token.tokenType.name) - - expect(tokenTypes).toEqual(['EqualsOp', 'ProcedureName', 'CellReference', 'ArrayColSep', 'WhiteSpace', 'ArrayColSep', 'CellReference', 'RParen']) - }) - - it('should treat space as whitespace', () => { - const tokens = lexer.tokenizeFormula('= 1').tokens - expect(tokens[1].tokenType.name).toEqual('WhiteSpace') - }) - - it('should treat tabulator (U+0009) as whitespace', () => { - const tokens = lexer.tokenizeFormula('=\t1').tokens - expect(tokens[1].tokenType.name).toEqual('WhiteSpace') - }) - - it('should treat carriage return (U+000D) as whitespace', () => { - const tokens = lexer.tokenizeFormula('=\r1').tokens - expect(tokens[1].tokenType.name).toEqual('WhiteSpace') - }) - - it('should treat line feed (U+000A) as whitespace', () => { - const tokens = lexer.tokenizeFormula('=\n1').tokens - expect(tokens[1].tokenType.name).toEqual('WhiteSpace') - }) - - it('should treat multiple whitespaces as one token', () => { - const tokens = lexer.tokenizeFormula('=\n\t\r 1').tokens - expect(tokens.length).toEqual(3) - expect(tokens[1].tokenType.name).toEqual('WhiteSpace') - expect(tokens[1].image).toEqual('\n\t\r ') - }) - - it('when set ignoreWhiteSpace = \'any\', should treat non-breaking space as a whitespace', () => { - const config = new Config({ ignoreWhiteSpace: 'any' }) - const lexer = new FormulaLexer(buildLexerConfig(config)) - - const tokens = lexer.tokenizeFormula('=\u00A042').tokens - expect(tokens[1].tokenType.name).toEqual('WhiteSpace') - }) -}) - -describe('processWhitespaces', () => { - const config = new Config() - const sheetMapping = new SheetMapping(buildTranslationPackage(enGB)) - sheetMapping.addSheet('Sheet1') - const parser = buildEmptyParserWithCaching(config, sheetMapping) - - it('should do nothing when no whitespaces', () => { - const tokens = parser.tokenizeFormula('=SUM(A1:A2)').tokens - const processed = parser.bindWhitespacesToTokens(tokens) - expect(processed.length).toBe(6) - expect(processed.map(processed => processed.leadingWhitespace).every(processed => processed === undefined)).toBe(true) - expectArrayWithSameContent( - [EqualsOp, ProcedureName, CellReference, RangeSeparator, CellReference, RParen], - processed.map(token => token.tokenType) - ) - }) - - it('should add leading whitespace to token', () => { - const tokens = parser.tokenizeFormula('= SUM(A1:A2)').tokens - const processed = parser.bindWhitespacesToTokens(tokens) - expect(processed.length).toBe(6) - - expect(processed[1].leadingWhitespace!.image).toBe(' ') - expectArrayWithSameContent( - [EqualsOp, ProcedureName, CellReference, RangeSeparator, CellReference, RParen], - processed.map(token => token.tokenType) - ) - }) - - it('should work for multiple whitespaces', () => { - const tokens = parser.tokenizeFormula('= SUM(A1:A2)').tokens - const processed = parser.bindWhitespacesToTokens(tokens) - expect(processed.length).toBe(6) - - expect(processed[1].leadingWhitespace!.image).toBe(' ') - expectArrayWithSameContent( - [EqualsOp, ProcedureName, CellReference, RangeSeparator, CellReference, RParen], - processed.map(token => token.tokenType) - ) - }) - - it('should work for whitespace at the beginning', () => { - const tokens = parser.tokenizeFormula(' =SUM(A1:A2)').tokens - const processed = parser.bindWhitespacesToTokens(tokens) - expect(processed.length).toBe(6) - - expect(processed[0].leadingWhitespace!.image).toBe(' ') - expectArrayWithSameContent( - [EqualsOp, ProcedureName, CellReference, RangeSeparator, CellReference, RParen], - processed.map(token => token.tokenType) - ) - }) - - it('should not include whitespaces directly on the list', () => { - const tokens = parser.tokenizeFormula('= SUM( A1:A2) ').tokens - const processed = parser.bindWhitespacesToTokens(tokens) - expect(processed.length).toBe(6) - expectArrayWithSameContent( - [EqualsOp, ProcedureName, CellReference, RangeSeparator, CellReference, RParen], - processed.map(token => token.tokenType) - ) - }) -}) diff --git a/test/unit/range-mapping.spec.ts b/test/unit/range-mapping.spec.ts deleted file mode 100644 index b3ac938f76..0000000000 --- a/test/unit/range-mapping.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {AbsoluteCellRange, AbsoluteColumnRange} from '../../src/AbsoluteCellRange' -import {RangeMapping, RangeVertex} from '../../src/DependencyGraph' -import {adr, colEnd, colStart} from './testUtils' - -describe('RangeMapping', () => { - it('range mapping when there is none', () => { - const mapping = new RangeMapping() - const start = adr('A1') - const end = adr('U50') - - expect(mapping.getRangeVertex(start, end)).toBe(undefined) - }) - - it('setting range mapping', () => { - const mapping = new RangeMapping() - const start = adr('A1') - const end = adr('U50') - const vertex = new RangeVertex(new AbsoluteCellRange(start, end)) - - mapping.addOrUpdateVertex(vertex) - - expect(mapping.getRangeVertex(start, end)).toBe(vertex) - }) - - it('set column range', () => { - const mapping = new RangeMapping() - const start = colStart('A') - const end = colEnd('U') - const vertex = new RangeVertex(new AbsoluteColumnRange(start.sheet, start.col, end.col)) - - mapping.addOrUpdateVertex(vertex) - - expect(mapping.getRangeVertex(start, end)).toBe(vertex) - }) -}) diff --git a/test/unit/range-vertex.spec.ts b/test/unit/range-vertex.spec.ts deleted file mode 100644 index c099254eaf..0000000000 --- a/test/unit/range-vertex.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {AbsoluteCellRange} from '../../src/AbsoluteCellRange' -import {Config} from '../../src/Config' -import {DateTimeHelper} from '../../src/DateTimeHelper' -import {CriterionCache, RangeVertex} from '../../src/DependencyGraph' -import {ArithmeticHelper} from '../../src/interpreter/ArithmeticHelper' -import {buildCriterionLambda, CriterionBuilder} from '../../src/interpreter/Criterion' -import {NumberLiteralHelper} from '../../src/NumberLiteralHelper' -import {adr} from './testUtils' - -describe('RangeVertex with cache', () => { - it('cache for criterion fuctions empty', () => { - const rangeVertex = new RangeVertex(new AbsoluteCellRange(adr('B2'), adr('B11'))) - - expect(rangeVertex.getCriterionFunctionValues('SUMIF,1,1').size).toBe(0) - }) - - it('cache for functions with criterion basic usage', () => { - const config = new Config() - const dateHelper = new DateTimeHelper(config) - const numberLiteralsHelper = new NumberLiteralHelper(config) - const arithmeticHelper = new ArithmeticHelper(config, dateHelper, numberLiteralsHelper) - const criterionBuilder = new CriterionBuilder(config) - - const rangeVertex = new RangeVertex(new AbsoluteCellRange(adr('B2'), adr('B11'))) - - const criterionString1 = '>=0' - - const criterion1 = buildCriterionLambda(criterionBuilder.parseCriterion(criterionString1, arithmeticHelper)!, arithmeticHelper) - - const criterionString2 = '=1' - - const criterion2 = buildCriterionLambda(criterionBuilder.parseCriterion(criterionString2, arithmeticHelper)!, arithmeticHelper) - - const criterionCache: CriterionCache = new Map() - - criterionCache.set(criterionString1, [10, [criterion1]]) - criterionCache.set(criterionString2, [20, [criterion2]]) - - rangeVertex.setCriterionFunctionValues('SUMIF,1,1', criterionCache) - - expect(rangeVertex.getCriterionFunctionValues('SUMIF,1,1').size).toBe(2) - expect(rangeVertex.getCriterionFunctionValue('SUMIF,1,1', criterionString1)).toEqual(10) - expect(rangeVertex.getCriterionFunctionValue('SUMIF,1,1', criterionString2)).toEqual(20) - }) -}) diff --git a/test/unit/ranges.spec.ts b/test/unit/ranges.spec.ts deleted file mode 100644 index 07a0db1316..0000000000 --- a/test/unit/ranges.spec.ts +++ /dev/null @@ -1,63 +0,0 @@ -import {ErrorType, HyperFormula} from '../../src' -import {adr, detailedError} from './testUtils' -import {ErrorMessage} from '../../src/error-message' - -describe('Ranges', () => { - describe('when operating on infinite ranges', () => { - describe('with array arithmetic on', () => { - it('returns a cell error, when operation results in an infinite column range not starting on index 0 (setCellContents)', () => { - const engine = HyperFormula.buildFromArray([[1, 2]], { useArrayArithmetic: true }) - engine.setCellContents(adr('B2'), '=A:A+B1') - - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.ERROR, 'Invalid range size')) - }) - - it('returns a cell error, when operation results in a range with infinite width and infinite height (setCellContents)', () => { - const engine = HyperFormula.buildFromArray([[1]], { useArrayArithmetic: true }) - engine.setCellContents(adr('B2'), '=A:A+1:1') - - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.ERROR, 'Invalid range size')) - }) - - it('returns a #SPILL error, when operation results in an infinite column range not starting on index 0', () => { - const engine = HyperFormula.buildFromArray([[1, 2], [null, '=A:A+B1']], { useArrayArithmetic: true }) - - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.SPILL, ErrorMessage.NoSpaceForArrayResult)) - }) - - it('returns a #SPILL error, when operation results in a range with infinite width and infinite height', () => { - const engine = HyperFormula.buildFromArray([[], [null, '=A:A+1:1']], { useArrayArithmetic: true }) - - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.SPILL, ErrorMessage.NoSpaceForArrayResult)) - }) - }) - - describe('with array arithmetic off', () => { - it('calculates the value according to the rules for array arithmetic mode disabled, when evaluating an operation on a column range and a single element (setCellContents)', () => { - const engine = HyperFormula.buildFromArray([[1, 2], [3]], { useArrayArithmetic: false }) - engine.setCellContents(adr('B2'), '=A:A+B1') - - expect(engine.getSheetValues(0)).toEqual([[1, 2], [3, 5]]) - }) - - it('calculates the value according to the rules for array arithmetic mode disabled, when evaluating an operation on a column range and a row range (setCellContents)', () => { - const engine = HyperFormula.buildFromArray([[1, 2], [3]], { useArrayArithmetic: false }) - engine.setCellContents(adr('B2'), '=A:A+1:1') - - expect(engine.getSheetValues(0)).toEqual([[1, 2], [3, 5]]) - }) - - it('returns a #SPILL error, when evaluating an operation on a column range and a single element', () => { - const engine = HyperFormula.buildFromArray([[1, 2], [null, '=A:A+B1']], { useArrayArithmetic: true }) - - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.SPILL, ErrorMessage.NoSpaceForArrayResult)) - }) - - it('returns a #SPILL error, when evaluating an operation on a column range and a row range', () => { - const engine = HyperFormula.buildFromArray([[], [null, '=A:A+1:1']], { useArrayArithmetic: true }) - - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.SPILL, ErrorMessage.NoSpaceForArrayResult)) - }) - }) - }) -}) diff --git a/test/unit/rebuild.spec.ts b/test/unit/rebuild.spec.ts deleted file mode 100644 index b66951a751..0000000000 --- a/test/unit/rebuild.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -import {ErrorType, HyperFormula} from '../../src' -import {ErrorMessage} from '../../src/error-message' -import {adr, detailedError, resetSpy} from './testUtils' -import {BuildEngineFactory} from '../../src/BuildEngineFactory' - -describe('Rebuilding engine', () => { - it('should preserve absolute named expression', () => { - const engine = HyperFormula.buildFromArray([['=FALSE']]) - engine.addNamedExpression('FALSE', '=FALSE()') - engine.rebuildAndRecalculate() - expect(engine.getCellValue(adr('A1'))).toEqual(false) - }) - - it('should preserve local named expression', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=FALSE']], - 'Sheet2': [['=FALSE']] - }) - engine.addNamedExpression('FALSE', '=FALSE()', 0) - engine.rebuildAndRecalculate() - expect(engine.getCellValue(adr('A1', 0))).toEqual(false) - expect(engine.getCellValue(adr('A1', 1))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.NamedExpressionName('FALSE'))) - }) - - it('named references should work after rebuild', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['42', '=FOO']], - }) - engine.addNamedExpression('FOO', '=Sheet1!$A$1') - engine.rebuildAndRecalculate() - - expect(engine.getCellValue(adr('B1', 0))).toEqual(42) - }) - - it('scopes are properly handled', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['42']], - 'Sheet2': [['42', '=FALSE']], - }, {}, [{name: 'FALSE', expression: false, scope: 1}]) - - engine.removeSheet(0) - engine.rebuildAndRecalculate() - expect(engine.getCellValue(adr('B1'))).toEqual(false) - }) - - it('rebuildAndRecalculate rebuilds the engine if the config is empty', () => { - const engine = HyperFormula.buildFromArray([[]], {}) - - const rebuildEngineSpy = spyOn(BuildEngineFactory, 'rebuildWithConfig') - resetSpy(rebuildEngineSpy) - - engine.rebuildAndRecalculate() - - expect(rebuildEngineSpy).toHaveBeenCalled() - }) - - it('doesn\'t throw after adding named expression (#1194)', () => { - const hf = HyperFormula.buildFromArray([['=42']], { - licenseKey: 'gpl-v3' - }) - - - hf.addNamedExpression('ABC', '=Sheet1!$A$1') - expect(() => hf.rebuildAndRecalculate()).not.toThrow() - }) -}) diff --git a/test/unit/row-range.spec.ts b/test/unit/row-range.spec.ts deleted file mode 100644 index 1c1e36666d..0000000000 --- a/test/unit/row-range.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {HyperFormula} from '../../src' -import {adr, rowEnd, rowStart} from './testUtils' - -describe('Row ranges', () => { - it('should work', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['=SUM(1:2)'] - ]) - - expect(engine.getCellValue(adr('A3'))).toEqual(3) - }) - - it('should create correct edges for infinite range when building graph', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(3:4)'], - ['=SUM(C3:D4)'], - ]) - - const rowRange = engine.rangeMapping.getRangeVertex(rowStart(3), rowEnd(4))! - - const c3 = engine.dependencyGraph.fetchCell(adr('C3')) - const c4 = engine.dependencyGraph.fetchCell(adr('C4')) - const d3 = engine.dependencyGraph.fetchCell(adr('D3')) - const d4 = engine.dependencyGraph.fetchCell(adr('D4')) - - expect(engine.graph.existsEdge(c3, rowRange)).toBe(true) - expect(engine.graph.existsEdge(c4, rowRange)).toBe(true) - expect(engine.graph.existsEdge(d3, rowRange)).toBe(true) - expect(engine.graph.existsEdge(d4, rowRange)).toBe(true) - }) - - it('should create correct edges for infinite range', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(3:5)'], - ['=SUM(4:7)'], - ]) - - engine.setCellContents(adr('B1'), '=SUM(Z4:Z8)') - - const rowRange35 = engine.rangeMapping.getRangeVertex(rowStart(3), rowEnd(5))! - const rowRange47 = engine.rangeMapping.getRangeVertex(rowStart(4), rowEnd(7))! - - const z4 = engine.dependencyGraph.fetchCell(adr('Z4')) - const z5 = engine.dependencyGraph.fetchCell(adr('Z5')) - const z6 = engine.dependencyGraph.fetchCell(adr('Z6')) - const z7 = engine.dependencyGraph.fetchCell(adr('Z7')) - const z8 = engine.dependencyGraph.fetchCell(adr('Z8')) - - expect(engine.graph.existsEdge(z4, rowRange35)).toBe(true) - expect(engine.graph.existsEdge(z5, rowRange35)).toBe(true) - expect(engine.graph.existsEdge(z6, rowRange35)).toBe(false) - - expect(engine.graph.existsEdge(z4, rowRange47)).toBe(true) - expect(engine.graph.existsEdge(z5, rowRange47)).toBe(true) - expect(engine.graph.existsEdge(z6, rowRange47)).toBe(true) - expect(engine.graph.existsEdge(z7, rowRange47)).toBe(true) - expect(engine.graph.existsEdge(z8, rowRange47)).toBe(false) - }) - - it('should correctly handle infinite row ranges when setting cell values (line 890)', () => { - const engine = HyperFormula.buildFromArray([ - ['=SUM(3:4)'], - ]) - - expect(engine.getCellValue(adr('A1'))).toBe(0) - - engine.setCellContents(adr('E3'), 100) - engine.setCellContents(adr('F4'), 200) - - expect(engine.getCellValue(adr('A1'))).toBe(300) - }) -}) diff --git a/test/unit/scoped-namedExpressions.spec.ts b/test/unit/scoped-namedExpressions.spec.ts deleted file mode 100644 index 01ab56d5ca..0000000000 --- a/test/unit/scoped-namedExpressions.spec.ts +++ /dev/null @@ -1,32 +0,0 @@ -import {HyperFormula} from '../../src' -import {adr} from './testUtils' - -describe('scoped named expressions', () => { - it('should be removed when sheet is removed', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[]], 'Sheet2': [[]]}) - engine.addNamedExpression('TRUE', true, 0) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - expect(engine._namedExpressions.getAllNamedExpressionsNames()).toEqual(['TRUE']) - engine.removeSheet(0) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - expect(engine._namedExpressions.getAllNamedExpressionsNames()).toEqual([]) - }) - - it('removal should work with undo of sheet', () => { - const engine = HyperFormula.buildFromArray([['=TRUE']]) - engine.addNamedExpression('TRUE', true, 0) - engine.removeSheet(0) - engine.undo() - expect(engine.getCellValue(adr('A1'))).toEqual(true) - }) - - it('removal should work with undo of named expression', () => { - const engine = HyperFormula.buildFromArray([['=TRUE']]) - engine.addNamedExpression('TRUE', true, 0) - engine.removeNamedExpression('TRUE', 0) - engine.undo() - expect(engine.getCellValue(adr('A1'))).toEqual(true) - }) -}) diff --git a/test/unit/serialization.spec.ts b/test/unit/serialization.spec.ts deleted file mode 100644 index 38abef07d7..0000000000 --- a/test/unit/serialization.spec.ts +++ /dev/null @@ -1,124 +0,0 @@ -import {HyperFormula} from '../../src' -import {CellValueDetailedType} from '../../src/Cell' -import {adr} from './testUtils' - -describe('serialization', () => { - it('should not loose sheet information on serialization', () => { - const engine1 = HyperFormula.buildFromArray([ - [1, '2', 'foo', true, '\'1', '33$', '12/01/15', '1%', '=FOO(', '#DIV/0!', new Date(1995, 11, 17)] - ]) - - expect(engine1.getCellSerialized(adr('A1'))).toEqual(1) - expect(engine1.getCellValueFormat(adr('A1'))).toEqual(undefined) - expect(engine1.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.NUMBER_RAW) - - expect(engine1.getCellSerialized(adr('B1'))).toEqual('2') - expect(engine1.getCellValueFormat(adr('B1'))).toEqual(undefined) - expect(engine1.getCellValueDetailedType(adr('B1'))).toEqual(CellValueDetailedType.NUMBER_RAW) - - expect(engine1.getCellSerialized(adr('C1'))).toEqual('foo') - expect(engine1.getCellValueFormat(adr('C1'))).toEqual(undefined) - expect(engine1.getCellValueDetailedType(adr('C1'))).toEqual(CellValueDetailedType.STRING) - - expect(engine1.getCellSerialized(adr('D1'))).toEqual(true) - expect(engine1.getCellValueFormat(adr('D1'))).toEqual(undefined) - expect(engine1.getCellValueDetailedType(adr('D1'))).toEqual(CellValueDetailedType.BOOLEAN) - - expect(engine1.getCellSerialized(adr('E1'))).toEqual('\'1') - expect(engine1.getCellValueFormat(adr('E1'))).toEqual(undefined) - expect(engine1.getCellValueDetailedType(adr('E1'))).toEqual(CellValueDetailedType.STRING) - - expect(engine1.getCellSerialized(adr('F1'))).toEqual('33$') - expect(engine1.getCellValueFormat(adr('F1'))).toEqual('$') - expect(engine1.getCellValueDetailedType(adr('F1'))).toEqual(CellValueDetailedType.NUMBER_CURRENCY) - - expect(engine1.getCellSerialized(adr('G1'))).toEqual('12/01/15') - expect(engine1.getCellValueFormat(adr('G1'))).toEqual('DD/MM/YY') - expect(engine1.getCellValueDetailedType(adr('G1'))).toEqual(CellValueDetailedType.NUMBER_DATE) - - expect(engine1.getCellSerialized(adr('H1'))).toEqual('1%') - expect(engine1.getCellValueFormat(adr('H1'))).toEqual(undefined) - expect(engine1.getCellValueDetailedType(adr('H1'))).toEqual(CellValueDetailedType.NUMBER_PERCENT) - - expect(engine1.getCellSerialized(adr('I1'))).toEqual('=FOO(') - expect(engine1.getCellValueFormat(adr('I1'))).toEqual(undefined) - expect(engine1.getCellValueDetailedType(adr('I1'))).toEqual(CellValueDetailedType.ERROR) - - expect(engine1.getCellSerialized(adr('J1'))).toEqual('#DIV/0!') - expect(engine1.getCellValueFormat(adr('J1'))).toEqual(undefined) - expect(engine1.getCellValueDetailedType(adr('J1'))).toEqual(CellValueDetailedType.ERROR) - - expect(engine1.getCellSerialized(adr('K1'))).toEqual(new Date(1995, 11, 17)) - expect(engine1.getCellValueFormat(adr('K1'))).toEqual('Date()') - expect(engine1.getCellValueDetailedType(adr('K1'))).toEqual(CellValueDetailedType.NUMBER_DATE) - - // serialize and "send" data to server - const serialized = engine1.getAllSheetsSerialized() - - // reload data and "restore" the previous state - const engine2 = HyperFormula.buildFromSheets(serialized) - - expect(engine2.getCellSerialized(adr('A1'))).toEqual(1) - expect(engine2.getCellValueFormat(adr('A1'))).toEqual(undefined) - expect(engine2.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.NUMBER_RAW) - - expect(engine2.getCellSerialized(adr('B1'))).toEqual('2') - expect(engine2.getCellValueFormat(adr('B1'))).toEqual(undefined) - expect(engine2.getCellValueDetailedType(adr('B1'))).toEqual(CellValueDetailedType.NUMBER_RAW) - - expect(engine2.getCellSerialized(adr('C1'))).toEqual('foo') - expect(engine2.getCellValueFormat(adr('C1'))).toEqual(undefined) - expect(engine2.getCellValueDetailedType(adr('C1'))).toEqual(CellValueDetailedType.STRING) - - expect(engine2.getCellSerialized(adr('D1'))).toEqual(true) - expect(engine2.getCellValueFormat(adr('D1'))).toEqual(undefined) - expect(engine2.getCellValueDetailedType(adr('D1'))).toEqual(CellValueDetailedType.BOOLEAN) - - expect(engine2.getCellSerialized(adr('E1'))).toEqual('\'1') - expect(engine2.getCellValueFormat(adr('E1'))).toEqual(undefined) - expect(engine2.getCellValueDetailedType(adr('E1'))).toEqual(CellValueDetailedType.STRING) - - expect(engine2.getCellSerialized(adr('F1'))).toEqual('33$') - expect(engine2.getCellValueFormat(adr('F1'))).toEqual('$') - expect(engine2.getCellValueDetailedType(adr('F1'))).toEqual(CellValueDetailedType.NUMBER_CURRENCY) - - expect(engine2.getCellSerialized(adr('G1'))).toEqual('12/01/15') - expect(engine2.getCellValueFormat(adr('G1'))).toEqual('DD/MM/YY') - expect(engine2.getCellValueDetailedType(adr('G1'))).toEqual(CellValueDetailedType.NUMBER_DATE) - - expect(engine1.getCellSerialized(adr('H1'))).toEqual('1%') - expect(engine1.getCellValueFormat(adr('H1'))).toEqual(undefined) - expect(engine1.getCellValueDetailedType(adr('H1'))).toEqual(CellValueDetailedType.NUMBER_PERCENT) - - expect(engine1.getCellSerialized(adr('I1'))).toEqual('=FOO(') - expect(engine1.getCellValueFormat(adr('I1'))).toEqual(undefined) - expect(engine1.getCellValueDetailedType(adr('I1'))).toEqual(CellValueDetailedType.ERROR) - - expect(engine1.getCellSerialized(adr('J1'))).toEqual('#DIV/0!') - expect(engine1.getCellValueFormat(adr('J1'))).toEqual(undefined) - expect(engine1.getCellValueDetailedType(adr('J1'))).toEqual(CellValueDetailedType.ERROR) - - expect(engine1.getCellSerialized(adr('K1'))).toEqual(new Date(1995, 11, 17)) - expect(engine1.getCellValueFormat(adr('K1'))).toEqual('Date()') - expect(engine1.getCellValueDetailedType(adr('K1'))).toEqual(CellValueDetailedType.NUMBER_DATE) - }) - - it('should return raw value for array formula spill cells', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3], - ['=TRANSPOSE(A1:C1)'], - ], { useArrayArithmetic: true }) - - expect(engine.getCellSerialized(adr('A2'))).toEqual('=TRANSPOSE(A1:C1)') - }) - - it('should return raw value for array formula non-top-left cells', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2, 3], - ['=TRANSPOSE(A1:C1)'], - ], { useArrayArithmetic: true }) - - expect(engine.getCellSerialized(adr('A3'))).toEqual(2) - expect(engine.getCellSerialized(adr('A4'))).toEqual(3) - }) -}) diff --git a/test/unit/small-build.spec.ts b/test/unit/small-build.spec.ts deleted file mode 100644 index 8df2b22ee1..0000000000 --- a/test/unit/small-build.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {HyperFormula} from '../../src' - -describe('Small tests that check evaluation order', () => { - it('passes #1', () => { - const engine = HyperFormula.buildFromArray([ - [500, '=(1-B2)*A1', '=(1-B2)*B1'], - ['=A1', 0.2, '=B2'], - ]) - expect(engine.getSheetValues(0)).toEqual([ - [500, 400, 320], - [500, 0.2, 0.2] - ]) - }) - - it('passes #2', () => { - const engine = HyperFormula.buildFromArray([ - [500, '=(1-B2)*A2', '=(1-C2)*B1'], - ['=A1', 0.2, '=B2'], - ]) - expect(engine.getSheetValues(0)).toEqual([ - [500, 400, 320], - [500, 0.2, 0.2] - ]) - }) -}) diff --git a/test/unit/small-setcellcontent.spec.ts b/test/unit/small-setcellcontent.spec.ts deleted file mode 100644 index 0ff5e2f163..0000000000 --- a/test/unit/small-setcellcontent.spec.ts +++ /dev/null @@ -1,32 +0,0 @@ -import {ErrorType, HyperFormula} from '../../src' -import {detailedErrorWithOrigin} from './testUtils' - -describe('should properly build', () => { - it('for this test', () => { - const engine = HyperFormula.buildEmpty() - engine.addSheet() - engine.setSheetContent(0, [ - ['=MAX(B1:B2)', '=MAX(A1:A2)'], - ['=MAX(B1:B2)', '=MAX(A1:A2)'], - ]) - expect(engine.getSheetValues(0)).toEqual( - [ - [detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!A1'), detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!B1')], - [detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!A2'), detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!B2')], - ] - ) - }) - - it('and for this', () => { - const engine = HyperFormula.buildFromArray([ - ['=MAX(B1:B2)', '=MAX(A1:A2)'], - ['=MAX(B1:B2)', '=MAX(A1:A2)'], - ]) - expect(engine.getSheetValues(0)).toEqual( - [ - [detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!A1'), detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!B1')], - [detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!A2'), detailedErrorWithOrigin(ErrorType.CYCLE, 'Sheet1!B2')], - ] - ) - }) -}) diff --git a/test/unit/statistics/statistics.spec.ts b/test/unit/statistics/statistics.spec.ts deleted file mode 100644 index 5ecdefb62c..0000000000 --- a/test/unit/statistics/statistics.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import {Statistics, StatType} from '../../../src/statistics' - -describe('Statistics', () => { - - it('reset - should return to base values', () => { - const statistics = new Statistics() - - const baseSnapshot = statistics.snapshot() - - statistics.start(StatType.BUILD_ENGINE_TOTAL) - statistics.end(StatType.BUILD_ENGINE_TOTAL) - statistics.start(StatType.VLOOKUP) - statistics.end(StatType.VLOOKUP) - statistics.start(StatType.PROCESS_DEPENDENCIES) - statistics.end(StatType.PROCESS_DEPENDENCIES) - statistics.incrementCriterionFunctionFullCacheUsed() - statistics.incrementCriterionFunctionPartialCacheUsed() - - const modifiedSnapshot = statistics.snapshot() - - statistics.reset() - - const resetSnapshot = statistics.snapshot() - - expect(baseSnapshot.size).toEqual(2) - expect(baseSnapshot.get(StatType.CRITERION_FUNCTION_FULL_CACHE_USED)).toEqual(0) - expect(baseSnapshot.get(StatType.CRITERION_FUNCTION_PARTIAL_CACHE_USED)).toEqual(0) - - expect(modifiedSnapshot.size).toEqual(5) - expect(modifiedSnapshot.has(StatType.CRITERION_FUNCTION_FULL_CACHE_USED)).toEqual(true) - expect(modifiedSnapshot.has(StatType.CRITERION_FUNCTION_PARTIAL_CACHE_USED)).toEqual(true) - expect(modifiedSnapshot.has(StatType.BUILD_ENGINE_TOTAL)).toEqual(true) - expect(modifiedSnapshot.has(StatType.VLOOKUP)).toEqual(true) - expect(modifiedSnapshot.has(StatType.PROCESS_DEPENDENCIES)).toEqual(true) - - expect(resetSnapshot).toEqual(baseSnapshot) - }) - - it('start - should throw if you try and start multiple of the same StatType', () => { - const statistics = new Statistics() - - statistics.start(StatType.BUILD_ENGINE_TOTAL) - statistics.start(StatType.VLOOKUP) - - expect(() => { - statistics.start(StatType.BUILD_ENGINE_TOTAL) - }).toThrowError(`Statistics ${StatType.BUILD_ENGINE_TOTAL} already started`) - - expect(() => { - statistics.start(StatType.VLOOKUP) - }).toThrowError(`Statistics ${StatType.VLOOKUP} already started`) - }) - - it('end - should throw if you try and end a StatType that has not been started', () => { - const statistics = new Statistics() - - statistics.start(StatType.BUILD_ENGINE_TOTAL) - statistics.start(StatType.VLOOKUP) - - expect(() => { - statistics.end(StatType.PROCESS_DEPENDENCIES) - }).toThrowError(`Statistics ${StatType.PROCESS_DEPENDENCIES} not started`) - - expect(() => { - statistics.end(StatType.ADJUSTING_ADDRESS_MAPPING) - }).toThrowError(`Statistics ${StatType.ADJUSTING_ADDRESS_MAPPING} not started`) - }) - -}) diff --git a/test/unit/temporary-formulas.spec.ts b/test/unit/temporary-formulas.spec.ts deleted file mode 100644 index 648f9a5061..0000000000 --- a/test/unit/temporary-formulas.spec.ts +++ /dev/null @@ -1,151 +0,0 @@ -import {HyperFormula} from '../../src' -import {NotAFormulaError} from '../../src/errors' - -describe('Temporary formulas - normalization', () => { - it('works', () => { - const engine = HyperFormula.buildFromArray([]) - - const normalizedFormula = engine.normalizeFormula('=SHEET1!A1+10') - - expect(normalizedFormula).toEqual('=Sheet1!A1+10') - }) - - it('fail with a typo', () => { - const engine = HyperFormula.buildFromArray([]) - - const normalizedFormula = engine.normalizeFormula('=SHET1!A1+10') - const normalizedFormula2 = engine.normalizeFormula('=SUM(SHET1!A1:A100)') - - expect(normalizedFormula).toEqual('=SHET1!A1+10') - expect(normalizedFormula2).toEqual('=SUM(SHET1!A1:A100)') - }) - - it('works with absolute addressing', () => { - const engine = HyperFormula.buildFromArray([]) - - const normalizedFormula = engine.normalizeFormula('=3*$a$1') - - expect(normalizedFormula).toEqual('=3*$A$1') - }) - - it('wont normalize sheet names of not existing sheets', () => { - const engine = HyperFormula.buildEmpty() - - const formula = '=ShEeT1!A1+10' - - expect(engine.normalizeFormula(formula)).toBe('=ShEeT1!A1+10') - }) - - it('throws an error if formulaString is malformed', () => { - const engine = HyperFormula.buildEmpty() - - expect(() => { - engine.normalizeFormula('=#FOO!') - }).toThrow(new NotAFormulaError()) - }) -}) - -describe('Temporary formulas - validation', () => { - it('ok for formulas', () => { - const engine = HyperFormula.buildFromArray([]) - - const formula = '=Sheet1!A1+10' - - expect(engine.validateFormula(formula)).toBe(true) - }) - - it('fail for simple values', () => { - const engine = HyperFormula.buildFromArray([]) - - expect(engine.validateFormula('42')).toBe(false) - expect(engine.validateFormula('some text')).toBe(false) - }) - - it('fail when not a formula', () => { - const engine = HyperFormula.buildFromArray([]) - - expect(engine.validateFormula('=SOME SYNTAX ERRORS')).toBe(false) - }) - - it('ok when literal error', () => { - const engine = HyperFormula.buildFromArray([]) - - expect(engine.validateFormula('=#N/A')).toBe(true) - }) - - it('validateFormula fails with an empty engine', () => { - const engine = HyperFormula.buildEmpty() - - const formula = '=Sheet1!A1+10' - - expect(engine.validateFormula(formula)).toBe(true) - }) -}) - -describe('Temporary formulas - calculation', () => { - it('basic usage', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ]) - - const result = engine.calculateFormula('=Sheet1!A1+10', 0) - - expect(result).toEqual(52) - }) - - it('formulas are executed in context of given sheet', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [['42']], - Sheet2: [['58']], - }) - - expect(engine.calculateFormula('=A1+10', 0)).toEqual(52) - expect(engine.calculateFormula('=A1+10', 1)).toEqual(68) - }) - - it('when sheet name does not exist', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ]) - - expect(() => { - engine.calculateFormula('=Sheet1!A1+10', 1) - }).toThrowError(/no sheet with id/) - }) - - it('SUM with range args', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['3', '4'] - ]) - expect(engine.calculateFormula('=SUM(A1:B2)', 0)).toEqual(10) - }) - - it('non-scalars work', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['1', '2'], - ]) - - const result = engine.calculateFormula('=TRANSPOSE(A1:B2)', 0) - - expect(result).toEqual([[1, 1], [2, 2]]) - }) - - it('more non-scalars', () => { - const engine = HyperFormula.buildFromArray([[0, 1]]) - expect(engine.calculateFormula('=ARRAYFORMULA(ISEVEN(A1:B2*3))', 0)).toEqual([[true, false], [true, true]]) - }) - - it('passing something which is not a formula doesnt work', () => { - const engine = HyperFormula.buildFromArray([]) - - expect(() => { - engine.calculateFormula('{=TRANSPOSE(A1:B2)}', 0) - }).toThrowError(/not a formula/) - - expect(() => { - engine.calculateFormula('42', 0) - }).toThrowError(/not a formula/) - }) -}) diff --git a/test/unit/testUtils.spec.ts b/test/unit/testUtils.spec.ts deleted file mode 100644 index b9155ca22e..0000000000 --- a/test/unit/testUtils.spec.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {Config} from '../../src/Config' -import {dateNumberToString} from './testUtils' - -describe('test utils', () => { - it('#dateNumberToString should return properly formatted date', () => { - expect(dateNumberToString(0, new Config())).toEqual('30/12/1899') - expect(dateNumberToString(2, new Config())).toEqual('01/01/1900') - expect(dateNumberToString(43465, new Config())).toEqual('31/12/2018') - }) -}) diff --git a/test/unit/testUtils.ts b/test/unit/testUtils.ts deleted file mode 100644 index df64a0f080..0000000000 --- a/test/unit/testUtils.ts +++ /dev/null @@ -1,281 +0,0 @@ -import {CellValue, DetailedCellError, ErrorType, HyperFormula, RawCellContent, Sheet, SheetDimensions} from '../../src' -import {AbsoluteCellRange, AbsoluteColumnRange, AbsoluteRowRange} from '../../src/AbsoluteCellRange' -import {CellError, SimpleCellAddress, simpleCellAddress} from '../../src/Cell' -import {Config} from '../../src/Config' -import {DateTimeHelper} from '../../src/DateTimeHelper' -import {ArrayFormulaVertex, ScalarFormulaVertex, Graph, RangeVertex} from '../../src/DependencyGraph' -import {GraphNode} from '../../src/DependencyGraph/Graph' -import {ErrorMessage} from '../../src/error-message' -import {defaultStringifyDateTime} from '../../src/format/format' -import {complex} from '../../src/interpreter/ArithmeticHelper' -import {ColumnIndex} from '../../src/Lookup/ColumnIndex' -import { - AstNodeType, - CellAddress, - CellRangeAst, - CellReferenceAst, - ErrorAst, - ProcedureAst, - simpleCellAddressToString, -} from '../../src/parser' -import {ColumnRangeAst, RowRangeAst} from '../../src/parser/Ast' -import {EngineComparator} from './graphComparator' - -export const extractReference = (engine: HyperFormula, address: SimpleCellAddress): CellAddress => { - return ((engine.addressMapping.getCell(address) as ScalarFormulaVertex).getFormula(engine.lazilyTransformingAstService) as CellReferenceAst).reference -} - -export const extractRange = (engine: HyperFormula, address: SimpleCellAddress): AbsoluteCellRange => { - const formula = (engine.addressMapping.getCell(address) as ScalarFormulaVertex).getFormula(engine.lazilyTransformingAstService) as ProcedureAst - const rangeAst = formula.args[0] as CellRangeAst - return new AbsoluteCellRange(rangeAst.start.toSimpleCellAddress(address), rangeAst.end.toSimpleCellAddress(address)) -} - -export const extractColumnRange = (engine: HyperFormula, address: SimpleCellAddress): AbsoluteColumnRange => { - const formula = (engine.addressMapping.getCell(address) as ScalarFormulaVertex).getFormula(engine.lazilyTransformingAstService) as ProcedureAst - const rangeAst = formula.args[0] as ColumnRangeAst - return AbsoluteColumnRange.fromColumnRange(rangeAst, address) -} - -export const extractRowRange = (engine: HyperFormula, address: SimpleCellAddress): AbsoluteRowRange => { - const formula = (engine.addressMapping.getCell(address) as ScalarFormulaVertex).getFormula(engine.lazilyTransformingAstService) as ProcedureAst - const rangeAst = formula.args[0] as RowRangeAst - return AbsoluteRowRange.fromRowRangeAst(rangeAst, address) -} - -export const extractMatrixRange = (engine: HyperFormula, address: SimpleCellAddress): AbsoluteCellRange => { - const formula = (engine.addressMapping.getCell(address) as ArrayFormulaVertex).getFormula(engine.lazilyTransformingAstService) as ProcedureAst - const rangeAst = formula.args[0] as CellRangeAst - return AbsoluteCellRange.fromCellRange(rangeAst, address) -} - -export const expectReferenceToHaveRefError = (engine: HyperFormula, address: SimpleCellAddress) => { - const errorAst = (engine.addressMapping.getCell(address) as ScalarFormulaVertex).getFormula(engine.lazilyTransformingAstService) as ErrorAst - expect(errorAst.type).toEqual(AstNodeType.ERROR) - expect(errorAst.error).toEqualError(new CellError(ErrorType.REF)) -} - -export const expectFunctionToHaveRefError = (engine: HyperFormula, address: SimpleCellAddress) => { - const formula = (engine.addressMapping.getCell(address) as ScalarFormulaVertex).getFormula(engine.lazilyTransformingAstService) as ProcedureAst - const errorAst = formula.args.find((arg) => arg !== undefined && arg.type === AstNodeType.ERROR) as ErrorAst - expect(errorAst.type).toEqual(AstNodeType.ERROR) - expect(errorAst.error).toEqualError(new CellError(ErrorType.REF)) -} - -export const rangeAddr = (range: AbsoluteCellRange) => { - const start = simpleCellAddressToString(() => '', range.start, 0) - const end = simpleCellAddressToString(() => '', range.end, 0) - return `${start}:${end}` -} - -export const verifyRangesInSheet = (engine: HyperFormula, sheet: number, ranges: string[]) => { - const rangeVerticesInMapping = Array.from(engine.rangeMapping.rangesInSheet(sheet)) - .map((vertex) => rangeAddr(vertex.range)) - - const rangeVerticesInGraph = [ ...engine.graph.getNodes()].filter(vertex => vertex instanceof RangeVertex) - .map(vertex => rangeAddr((vertex as RangeVertex).range)) - - expectNoDuplicates(rangeVerticesInGraph) - expectNoDuplicates(rangeVerticesInMapping) - expectArrayWithSameContent(rangeVerticesInGraph, ranges) - expectArrayWithSameContent(rangeVerticesInMapping, ranges) -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const expectNoDuplicates = (array: any[]) => { - expect(new Set(array).size === array.length).toBe(true) -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const expectArrayWithSameContent = (actualArray: any[], expectedArray: any[]) => { - expect(actualArray.length).toBe(expectedArray.length) - expectedArray.forEach(expectedItem => expect(actualArray).toContainEqual(expectedItem)) - actualArray.forEach(actualItem => expect(expectedArray).toContainEqual(actualItem)) -} - -export const expectToBeCloseForComplex = (engine: HyperFormula, cell: string, expected: string, precision?: number) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const coerce = (arg: CellValue): complex => engine.evaluator.interpreter.arithmeticHelper.coerceScalarToComplex(arg) - const actualVal: complex = coerce(engine.getCellValue(adr(cell))) - const expectedVal: complex = coerce(expected) - expect(expectedVal[0]).toBeCloseTo(actualVal[0], precision) - expect(expectedVal[1]).toBeCloseTo(actualVal[1], precision) -} - -export const verifyValues = (engine: HyperFormula) => { - const serialization = engine.getAllSheetsSerialized() - const engine2 = HyperFormula.buildFromSheets(serialization) - expect(engine.getAllSheetsValues()).toEqual(engine2.getAllSheetsValues()) -} - -export const rowStart = (input: number, sheet: number = 0): SimpleCellAddress => { - return simpleCellAddress(sheet, 0, input - 1) -} - -export const rowEnd = (input: number, sheet: number = 0): SimpleCellAddress => { - return simpleCellAddress(sheet, Number.POSITIVE_INFINITY, input - 1) -} - -export const colStart = (input: string, sheet: number = 0): SimpleCellAddress => { - - const result = /^(\$?)([A-Za-z]+)/.exec(input)! - return simpleCellAddress(sheet, colNumber(result[2]), 0) -} - -export const colEnd = (input: string, sheet: number = 0): SimpleCellAddress => { - - const result = /^(\$?)([A-Za-z]+)/.exec(input)! - return simpleCellAddress(sheet, colNumber(result[2]), Number.POSITIVE_INFINITY) -} - -export const adr = (stringAddress: string, sheet: number = 0): SimpleCellAddress => { - - const result = /^(\$([A-Za-z0-9_]+)\.)?(\$?)([A-Za-z]+)(\$?)([0-9]+)$/.exec(stringAddress)! - const row = Number(result[6]) - 1 - return simpleCellAddress(sheet, colNumber(result[4]), row) -} - -const colNumber = (input: string): number => { - if (input.length === 1) { - return input.toUpperCase().charCodeAt(0) - 65 - } else { - return input.split('').reduce((currentColumn, nextLetter) => { - return currentColumn * 26 + (nextLetter.toUpperCase().charCodeAt(0) - 64) - }, 0) - 1 - } -} - -export function detailedError(errorType: ErrorType, message?: string, config?: Config): DetailedCellError { - config = new Config(config) - const error = new CellError(errorType, message) - return new DetailedCellError(error, config.translationPackage.getErrorTranslation(errorType)) -} - -export function noSpace(): DetailedCellError { - return detailedError(ErrorType.SPILL, ErrorMessage.NoSpaceForArrayResult) -} - -export function detailedErrorWithOrigin(errorType: ErrorType, address: string, message?: string, config?: Config): DetailedCellError { - config = new Config(config) - const error = new CellError(errorType, message) - return new DetailedCellError(error, config.translationPackage.getErrorTranslation(errorType), address) -} - -export const expectEngineToBeTheSameAs = (actual: HyperFormula, expected: HyperFormula) => { - const comparator = new EngineComparator(expected, actual) - comparator.compare() -} - -export function dateNumberToString(dateNumber: CellValue, config: Config): string | DetailedCellError { - if (dateNumber instanceof DetailedCellError) { - return dateNumber - } - const dateTimeHelper = new DateTimeHelper(config) - const dateString = defaultStringifyDateTime(dateTimeHelper.numberToSimpleDateTime(dateNumber as number), config.dateFormats[0]) - return dateString ?? '' -} - -export function timeNumberToString(timeNumber: CellValue, config: Config): string | DetailedCellError { - if (timeNumber instanceof DetailedCellError) { - return timeNumber - } - const dateTimeHelper = new DateTimeHelper(config) - const timeString = defaultStringifyDateTime(dateTimeHelper.numberToSimpleDateTime(timeNumber as number), 'hh:mm:ss.sss') - return timeString ?? '' -} - -export function unregisterAllLanguages() { - for (const langCode of HyperFormula.getRegisteredLanguagesCodes()) { - HyperFormula.unregisterLanguage(langCode) - } -} - -export function expectVerticesOfTypes(engine: HyperFormula, types: any[][], sheet: number = 0) { - for (let row = 0; row < types.length; ++row) { - for (let col = 0; col < types[row].length; ++col) { - const expectedType = types[row][col] - const cell = engine.dependencyGraph.getCell(simpleCellAddress(sheet, col, row)) - if (expectedType === undefined) { - expect(cell === undefined).toBe(true) - } else { - expect(cell instanceof types[row][col]).toBe(true) - } - } - } -} - -export function columnIndexToSheet(columnIndex: ColumnIndex, width: number, height: number, sheet: number = 0): any[][] { - const result: any[][] = [] - for (let col = 0; col < width; ++col) { - const columnMap = columnIndex.getColumnMap(sheet, col) - for (const [value, {index}] of columnMap.entries()) { - columnIndex.ensureRecentData(sheet, col, value) - for (const row of index) { - result[row] = result[row] ?? [] - if (result[row][col] !== undefined) { - throw new Error(`ColumnIndex ambiguity. Expected ${JSON.stringify(result[row][col])}, but found ${JSON.stringify(value)} at row ${JSON.stringify(row)}, column ${JSON.stringify(col)}`) - } - result[row][col] = value - } - } - } - return normalizeSheet(result, {width, height}) -} - -function normalizeSheet(sheet: RawCellContent[][], dimensions: SheetDimensions): any[][] { - return Array.from(sheet, row => { - if (row) { - row.length = dimensions.width - return Array.from(row, v => v || null) - } - return Array(dimensions.width).fill(null) as RawCellContent[] - }) -} - -export function expectColumnIndexToMatchSheet(expected: Sheet, engine: HyperFormula, sheetId: number = 0) { - const columnIndex = engine.columnSearch as ColumnIndex - expect(columnIndex).toBeInstanceOf(ColumnIndex) - const exportedColumnIndex = columnIndexToSheet(columnIndex, engine.getSheetDimensions(sheetId).width, sheetId) - const dimensions = engine.getSheetDimensions(0) - expectArrayWithSameContent(normalizeSheet(expected, dimensions), normalizeSheet(exportedColumnIndex, dimensions)) -} - -export function resetSpy(spy: any): void { - try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - spy.mockClear() // clears mock in Jest env - } catch { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access - spy.calls.reset() // clears mock in Jasmine env - } -} - -/** - * Helper method. Expects the cell value pointed by cellAddress to: - * - be a date and - * - when stringified, be equal to expectedDateString - */ -export function expectCellValueToEqualDate(engine: HyperFormula, cellAddress: SimpleCellAddress, expectedDateString: string) { - expect(dateNumberToString(engine.getCellValue(cellAddress), new Config())).toEqual(expectedDateString) -} - -/** - * Returns number of edges in graph - * - */ -export function graphEdgesCount(graph: Graph): number { - return (graph as any).nodesSparseArray.reduce((acc: number, node: T, id: number) => - node ? acc + ((graph as any).fixEdgesArrayForNode(id) as number[]).length : acc - , 0) -} - -export function graphReversedAdjacentNodes(graph: Graph, node: Node): Node[] { - const id = node.idInGraph - - return (graph as any).nodesSparseArray.reduce((acc: number[], sourceNode: Node, sourceId: number) => - sourceNode && (graph as any).edgesSparseArray[sourceId].includes(id) - ? [ ...acc, sourceId ] - : acc - , []).map((resultId: number) => (graph as any).nodesSparseArray[resultId]) -} diff --git a/test/unit/tsconfig.json b/test/unit/tsconfig.json deleted file mode 100644 index 7425e2cf3b..0000000000 --- a/test/unit/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - /* - * This file allows to debug jest tests locally and resolve custom matchers properly. - * Moving or renaming it can cause local development painful. - * It should not affect jasmine tests. - */ - "compilerOptions": { - "module": "commonjs", - "outDir": "../../test-jest", - /* Make sure proper types are used */ - "types": [ - "jest", - "node" - ] - }, - "extends": "../../tsconfig", - "include": [ - "../../../src", - "../unit" -, "../performance" ], - "exclude": [] -} diff --git a/test/unit/type-inference.spec.ts b/test/unit/type-inference.spec.ts deleted file mode 100644 index c153804e26..0000000000 --- a/test/unit/type-inference.spec.ts +++ /dev/null @@ -1,350 +0,0 @@ -import {HyperFormula} from '../../src' -import {CellValueDetailedType} from '../../src/Cell' -import {adr} from './testUtils' - -describe('arithmetic operations', () => { - it('addition should correctly infer types', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1%', '1$', '01/01/1900', '12:00', '01/01/1900 12:00'], - ['=A1+A1', '=A1+B1', '=A1+C1', '=A1+D1', '=A1+E1', '=A1+F1'], - ['=B1+A1', '=B1+B1', '=B1+C1', '=B1+D1', '=B1+E1', '=B1+F1'], - ['=C1+A1', '=C1+B1', '=C1+C1', '=C1+D1', '=C1+E1', '=C1+F1'], - ['=D1+A1', '=D1+B1', '=D1+C1', '=D1+D1', '=D1+E1', '=D1+F1'], - ['=E1+A1', '=E1+B1', '=E1+C1', '=E1+D1', '=E1+E1', '=E1+F1'], - ['=F1+A1', '=F1+B1', '=F1+C1', '=F1+D1', '=F1+E1', '=F1+F1'], - ]) - expect(engine.getCellValueDetailedType(adr('A2'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('B2'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('C2'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('D2'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('E2'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('F2'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('A3'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('B3'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('C3'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('D3'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('E3'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('F3'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('A4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('B4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('C4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('D4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('E4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('F4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('A5'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('B5'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('C5'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('D5'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('E5'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('F5'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('A6'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('B6'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('C6'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('D6'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('E6'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('F6'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('A7'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('B7'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('C7'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('D7'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('E7'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('F7'))).toBe(CellValueDetailedType.NUMBER_RAW) - }) - - it('subtraction should correctly infer types', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1%', '1$', '01/01/1900', '12:00', '01/01/1900 12:00'], - ['=A1-A1', '=A1-B1', '=A1-C1', '=A1-D1', '=A1-E1', '=A1-F1'], - ['=B1-A1', '=B1-B1', '=B1-C1', '=B1-D1', '=B1-E1', '=B1-F1'], - ['=C1-A1', '=C1-B1', '=C1-C1', '=C1-D1', '=C1-E1', '=C1-F1'], - ['=D1-A1', '=D1-B1', '=D1-C1', '=D1-D1', '=D1-E1', '=D1-F1'], - ['=E1-A1', '=E1-B1', '=E1-C1', '=E1-D1', '=E1-E1', '=E1-F1'], - ['=F1-A1', '=F1-B1', '=F1-C1', '=F1-D1', '=F1-E1', '=F1-F1'], - ]) - expect(engine.getCellValueDetailedType(adr('A2'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('B2'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('C2'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('D2'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('E2'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('F2'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('A3'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('B3'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('C3'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('D3'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('E3'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('F3'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('A4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('B4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('C4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('D4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('E4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('F4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('A5'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('B5'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('C5'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('D5'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('E5'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('F5'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('A6'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('B6'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('C6'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('D6'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('E6'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('F6'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('A7'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('B7'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('C7'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('D7'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('E7'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('F7'))).toBe(CellValueDetailedType.NUMBER_RAW) - }) - - it('multiplication should correctly infer types', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1%', '1$', '01/01/1900', '12:00', '01/01/1900 12:00'], - ['=A1*A1', '=A1*B1', '=A1*C1', '=A1*D1', '=A1*E1', '=A1*F1'], - ['=B1*A1', '=B1*B1', '=B1*C1', '=B1*D1', '=B1*E1', '=B1*F1'], - ['=C1*A1', '=C1*B1', '=C1*C1', '=C1*D1', '=C1*E1', '=C1*F1'], - ['=D1*A1', '=D1*B1', '=D1*C1', '=D1*D1', '=D1*E1', '=D1*F1'], - ['=E1*A1', '=E1*B1', '=E1*C1', '=E1*D1', '=E1*E1', '=E1*F1'], - ['=F1*A1', '=F1*B1', '=F1*C1', '=F1*D1', '=F1*E1', '=F1*F1'], - ]) - expect(engine.getCellValueDetailedType(adr('A2'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('B2'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('C2'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('D2'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('E2'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('F2'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('A3'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('B3'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('C3'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('D3'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('E3'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('F3'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('A4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('B4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('C4'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('D4'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('E4'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('F4'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('A5'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('B5'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('C5'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('D5'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('E5'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('F5'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('A6'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('B6'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('C6'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('D6'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('E6'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('F6'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('A7'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('B7'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('C7'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('D7'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('E7'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('F7'))).toBe(CellValueDetailedType.NUMBER_RAW) - }) - - it('division should correctly infer types', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1%', '1$', '01/01/1900', '12:00', '01/01/1900 12:00'], - ['=A1/A1', '=A1/B1', '=A1/C1', '=A1/D1', '=A1/E1', '=A1/F1'], - ['=B1/A1', '=B1/B1', '=B1/C1', '=B1/D1', '=B1/E1', '=B1/F1'], - ['=C1/A1', '=C1/B1', '=C1/C1', '=C1/D1', '=C1/E1', '=C1/F1'], - ['=D1/A1', '=D1/B1', '=D1/C1', '=D1/D1', '=D1/E1', '=D1/F1'], - ['=E1/A1', '=E1/B1', '=E1/C1', '=E1/D1', '=E1/E1', '=E1/F1'], - ['=F1/A1', '=F1/B1', '=F1/C1', '=F1/D1', '=F1/E1', '=F1/F1'], - ]) - expect(engine.getCellValueDetailedType(adr('A2'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('B2'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('C2'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('D2'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('E2'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('F2'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('A3'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('B3'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('C3'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('D3'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('E3'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('F3'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('A4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('B4'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('C4'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('D4'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('E4'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('F4'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('A5'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('B5'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('C5'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('D5'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('E5'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('F5'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('A6'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('B6'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('C6'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('D6'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('E6'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('F6'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('A7'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('B7'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - expect(engine.getCellValueDetailedType(adr('C7'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('D7'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('E7'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('F7'))).toBe(CellValueDetailedType.NUMBER_RAW) - }) - - it('percent should correctly infer types', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1%', '1$', '01/01/1900', '12:00', '01/01/1900 12:00'], - ['=A1%', '=B1%', '=C1%', '=D1%', '=E1%', '=F1%'], - ]) - expect(engine.getCellValueDetailedType(adr('A2'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('B2'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('C2'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('D2'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('E2'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('F2'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - }) - - it('unary minus should correctly infer types', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1%', '1$', '01/01/1900', '12:00', '01/01/1900 12:00'], - ['=-A1', '=-B1', '=-C1', '=-D1', '=-E1', '=-F1'], - ]) - expect(engine.getCellValueDetailedType(adr('A2'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('B2'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('C2'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('D2'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('E2'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('F2'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - }) - - it('unary plus should correctly infer types', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '1%', '1$', '01/01/1900', '12:00', '01/01/1900 12:00'], - ['=+A1', '=+B1', '=+C1', '=+D1', '=+E1', '=+F1'], - ]) - expect(engine.getCellValueDetailedType(adr('A2'))).toBe(CellValueDetailedType.NUMBER_RAW) - expect(engine.getCellValueDetailedType(adr('B2'))).toBe(CellValueDetailedType.NUMBER_PERCENT) - expect(engine.getCellValueDetailedType(adr('C2'))).toBe(CellValueDetailedType.NUMBER_CURRENCY) - expect(engine.getCellValueDetailedType(adr('D2'))).toBe(CellValueDetailedType.NUMBER_DATE) - expect(engine.getCellValueDetailedType(adr('E2'))).toBe(CellValueDetailedType.NUMBER_TIME) - expect(engine.getCellValueDetailedType(adr('F2'))).toBe(CellValueDetailedType.NUMBER_DATETIME) - }) -}) - -describe('formatting info', () => { - it('should be preserved by unary minus', () => { - const engine = HyperFormula.buildFromArray([ - ['1$', '1', '1PLN'], - ['=-A1', '=-B1', '=C1'], - ], {currencySymbol: ['$', 'PLN']}) - expect(engine.getCellValueFormat(adr('A2'))).toBe('$') - expect(engine.getCellValueFormat(adr('B2'))).toBe(undefined) - expect(engine.getCellValueFormat(adr('C2'))).toBe('PLN') - }) - - it('should be preserved by unary plus', () => { - const engine = HyperFormula.buildFromArray([ - ['1$', '1', '1PLN'], - ['=+A1', '=+B1', '=+C1'], - ], {currencySymbol: ['$', 'PLN']}) - expect(engine.getCellValueFormat(adr('A2'))).toBe('$') - expect(engine.getCellValueFormat(adr('B2'))).toBe(undefined) - expect(engine.getCellValueFormat(adr('C2'))).toBe('PLN') - }) - - it('should be preserved by addition', () => { - const engine = HyperFormula.buildFromArray([ - ['1$', '1', '1PLN'], - ['=A1+A1', '=A1+B1', '=A1+C1'], - ['=B1+A1', '=B1+B1', '=B1+C1'], - ['=C1+A1', '=C1+B1', '=C1+C1'], - ], {currencySymbol: ['$', 'PLN']}) - expect(engine.getCellValueFormat(adr('A2'))).toBe('$') - expect(engine.getCellValueFormat(adr('B2'))).toBe('$') - expect(engine.getCellValueFormat(adr('C2'))).toBe('$') - expect(engine.getCellValueFormat(adr('A3'))).toBe('$') - expect(engine.getCellValueFormat(adr('B3'))).toBe(undefined) - expect(engine.getCellValueFormat(adr('C3'))).toBe('PLN') - expect(engine.getCellValueFormat(adr('A4'))).toBe('PLN') - expect(engine.getCellValueFormat(adr('B4'))).toBe('PLN') - expect(engine.getCellValueFormat(adr('C4'))).toBe('PLN') - }) - - it('should be preserved by subtraction', () => { - const engine = HyperFormula.buildFromArray([ - ['1$', '1', '1PLN'], - ['=A1-A1', '=A1-B1', '=A1-C1'], - ['=B1-A1', '=B1-B1', '=B1-C1'], - ['=C1-A1', '=C1-B1', '=C1-C1'], - ], {currencySymbol: ['$', 'PLN']}) - expect(engine.getCellValueFormat(adr('A2'))).toBe('$') - expect(engine.getCellValueFormat(adr('B2'))).toBe('$') - expect(engine.getCellValueFormat(adr('C2'))).toBe('$') - expect(engine.getCellValueFormat(adr('A3'))).toBe('$') - expect(engine.getCellValueFormat(adr('B3'))).toBe(undefined) - expect(engine.getCellValueFormat(adr('C3'))).toBe('PLN') - expect(engine.getCellValueFormat(adr('A4'))).toBe('PLN') - expect(engine.getCellValueFormat(adr('B4'))).toBe('PLN') - expect(engine.getCellValueFormat(adr('C4'))).toBe('PLN') - }) - - it('should be preserved by multiplication', () => { - const engine = HyperFormula.buildFromArray([ - ['1$', '1', '1PLN'], - ['=A1*A1', '=A1*B1', '=A1*C1'], - ['=B1*A1', '=B1*B1', '=B1*C1'], - ['=C1*A1', '=C1*B1', '=C1*C1'], - ], {currencySymbol: ['$', 'PLN']}) - expect(engine.getCellValueFormat(adr('A2'))).toBe(undefined) - expect(engine.getCellValueFormat(adr('B2'))).toBe('$') - expect(engine.getCellValueFormat(adr('C2'))).toBe(undefined) - expect(engine.getCellValueFormat(adr('A3'))).toBe('$') - expect(engine.getCellValueFormat(adr('B3'))).toBe(undefined) - expect(engine.getCellValueFormat(adr('C3'))).toBe('PLN') - expect(engine.getCellValueFormat(adr('A4'))).toBe(undefined) - expect(engine.getCellValueFormat(adr('B4'))).toBe('PLN') - expect(engine.getCellValueFormat(adr('C4'))).toBe(undefined) - }) - - it('should be preserved by division', () => { - const engine = HyperFormula.buildFromArray([ - ['1$', '1', '1PLN'], - ['=A1/A1', '=A1/B1', '=A1/C1'], - ['=B1/A1', '=B1/B1', '=B1/C1'], - ['=C1/A1', '=C1/B1', '=C1/C1'], - ], {currencySymbol: ['$', 'PLN']}) - expect(engine.getCellValueFormat(adr('A2'))).toBe(undefined) - expect(engine.getCellValueFormat(adr('B2'))).toBe('$') - expect(engine.getCellValueFormat(adr('C2'))).toBe(undefined) - expect(engine.getCellValueFormat(adr('A3'))).toBe('$') - expect(engine.getCellValueFormat(adr('B3'))).toBe(undefined) - expect(engine.getCellValueFormat(adr('C3'))).toBe('PLN') - expect(engine.getCellValueFormat(adr('A4'))).toBe(undefined) - expect(engine.getCellValueFormat(adr('B4'))).toBe('PLN') - expect(engine.getCellValueFormat(adr('C4'))).toBe(undefined) - }) -}) - -describe('Datetime formatting', () => { - it('should be correctly inferred by addition', () => { - const engine = HyperFormula.buildFromArray([ - ['01/01/1900', '12:00', '01/01/1900 12:00'], - ['=A1+A1', '=A1+B1', '=A1+C1'], - ['=B1+A1', '=B1+B1', '=B1+C1'], - ['=C1+A1', '=C1+B1', '=C1+C1'], - ]) - expect(engine.getCellValueFormat(adr('A2'))).toBe(undefined) - expect(engine.getCellValueFormat(adr('B2'))).toBe('DD/MM/YYYY hh:mm') - expect(engine.getCellValueFormat(adr('C2'))).toBe(undefined) - expect(engine.getCellValueFormat(adr('A3'))).toBe('DD/MM/YYYY hh:mm') - expect(engine.getCellValueFormat(adr('B3'))).toBe('hh:mm') - expect(engine.getCellValueFormat(adr('C3'))).toBe('DD/MM/YYYY hh:mm') - expect(engine.getCellValueFormat(adr('A4'))).toBe(undefined) - expect(engine.getCellValueFormat(adr('B4'))).toBe('DD/MM/YYYY hh:mm') - expect(engine.getCellValueFormat(adr('C4'))).toBe(undefined) - }) -}) diff --git a/test/unit/undo-redo.spec.ts b/test/unit/undo-redo.spec.ts deleted file mode 100644 index 14927f1534..0000000000 --- a/test/unit/undo-redo.spec.ts +++ /dev/null @@ -1,2575 +0,0 @@ -/* eslint-disable jest/expect-expect */ -import {ErrorType, HyperFormula, NoOperationToRedoError, NoOperationToUndoError} from '../../src' -import {AbsoluteCellRange} from '../../src/AbsoluteCellRange' -import {ErrorMessage} from '../../src/error-message' -import {adr, detailedError, expectEngineToBeTheSameAs} from './testUtils' -import {UndoRedo, AddSheetUndoEntry} from '../../src/UndoRedo' -import {Config} from '../../src/Config' -import {DependencyGraph} from '../../src/DependencyGraph' -import {Statistics} from '../../src/statistics' -import {FunctionRegistry} from '../../src/interpreter/FunctionRegistry' -import {LazilyTransformingAstService} from '../../src/LazilyTransformingAstService' -import {NamedExpressions} from '../../src/NamedExpressions' -import {Operations} from '../../src/Operations' -import {buildColumnSearchStrategy} from '../../src/Lookup/SearchStrategy' -import {CellContentParser} from '../../src/CellContentParser' -import {DateTimeHelper} from '../../src/DateTimeHelper' -import {NumberLiteralHelper} from '../../src/NumberLiteralHelper' -import {ParserWithCaching} from '../../src/parser' -import {ArraySizePredictor} from '../../src/ArraySize' - -describe('Undo - removing rows', () => { - it('works for empty row', () => { - const sheet = [ - ['1'], - [null], // remove - ['3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeRows(0, [1, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('works for simple values', () => { - const sheet = [ - ['1'], - ['2'], // remove - ['3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeRows(0, [1, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('works with formula in removed row', () => { - const sheet = [ - ['1'], - ['=SUM(A1)'], // remove - ['3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeRows(0, [1, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores dependent cell formulas', () => { - const sheet = [ - ['=A2'], - ['42'], // remove - ['3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeRows(0, [1, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('formulas are built correctly when there was a pause in computation', () => { - const sheet = [ - ['=A2'], - ['42'], // remove - ['3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.suspendEvaluation() - engine.removeRows(0, [1, 1]) - - engine.undo() - engine.resumeEvaluation() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores ranges when removing rows', () => { - const sheet = [ - ['=SUM(A2:A3)'], - ['2'], // remove - ['3'], // remove - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeRows(0, [1, 2]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('dummy operation removeRows should also be undoable', () => { - const sheet = [ - ['1'] - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeRows(0, [1000, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('works for more removal segments', () => { - const sheet = [ - ['1'], - ['2'], - ['3'], - ['4'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeRows(0, [1, 1], [3, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) -}) - -describe('Undo - adding rows', () => { - it('restores original state after adding single row', () => { - const sheet = [ - ['1'], // add after that - ['3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.addRows(0, [1, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('dummy operation addRows should also be undoable', () => { - const sheet = [ - ['1'] - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.addRows(0, [1000, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('works for more addition segments', () => { - const sheet = [ - ['1'], - ['2'], - ['3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.addRows(0, [1, 1], [2, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) -}) - -describe('Undo - moving rows', () => { - it('restores original row order after move', () => { - const sheet = [ - [0], [1], [2], [3], [4], [5], [6], [7], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.moveRows(0, 1, 3, 7) - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores original row order when moving rows backward', () => { - const sheet = [ - [0], [1], [2], [3], [4], [5], [6], [7], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.moveRows(0, 4, 3, 2) - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores range formula after moving rows forward', () => { - const engine = HyperFormula.buildFromArray([ - [1, null], - [2, '=SUM(A1:A2)'], - ]) - engine.moveRows(0, 1, 1, 3) - engine.undo() - - expect(engine.getCellFormula(adr('B2'))).toBe('=SUM(A1:A2)') - }) - - it('should restore range when moving other way', () => { - const engine = HyperFormula.buildFromArray([ - [1, null], - [2, '=SUM(A1:A2)'], - ]) - - engine.moveRows(0, 2, 1, 1) - engine.undo() - - expect(engine.getCellFormula(adr('B2'))).toBe('=SUM(A1:A2)') - }) -}) - -describe('Undo - moving columns', () => { - it('restores original column order after move', () => { - const sheet = [ - [0, 1, 2, 3, 4, 5, 6, 7], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.moveColumns(0, 1, 3, 7) - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores original column order when moving columns backward', () => { - const sheet = [ - [0, 1, 2, 3, 4, 5, 6, 7], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.moveColumns(0, 4, 3, 2) - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores range formula after moving columns forward', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2], - [null, '=SUM(A1:B1)'], - ]) - engine.moveColumns(0, 1, 1, 3) - engine.undo() - - expect(engine.getCellFormula(adr('B2'))).toBe('=SUM(A1:B1)') - }) - - it('should restore range when moving to left', () => { - const engine = HyperFormula.buildFromArray([ - [1, 2], - [null, '=SUM(A1:B1)'], - ]) - - engine.moveColumns(0, 2, 1, 1) - engine.undo() - - expect(engine.getCellFormula(adr('B2'))).toBe('=SUM(A1:B1)') - }) -}) - -describe('Undo - adding columns', () => { - it('restores original state after adding single column', () => { - const sheet = [ - ['1', /* */ '3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.addColumns(0, [1, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('dummy operation addColumns should also be undoable', () => { - const sheet = [ - ['1'] - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.addColumns(0, [1000, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores state after adding multiple column segments', () => { - const sheet = [ - ['1', '2', '3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.addColumns(0, [1, 1], [2, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) -}) - -describe('Undo - removing columns', () => { - it('works for empty column', () => { - const sheet = [ - ['1', null, '3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeColumns(0, [1, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores column with simple values', () => { - const sheet = [ - ['1', '2', '3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeColumns(0, [1, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('works with formula in removed columns', () => { - const sheet = [ - ['1', '=SUM(A1)', '3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeColumns(0, [1, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores dependent cell formulas after column removal', () => { - const sheet = [ - ['=A2', '42', '3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeColumns(0, [1, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('builds formulas correctly with suspended evaluation during column removal', () => { - const sheet = [ - ['=A2', '42', '3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.suspendEvaluation() - engine.removeColumns(0, [1, 1]) - - engine.undo() - engine.resumeEvaluation() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores ranges when removing columns', () => { - const sheet = [ - ['=SUM(B1:C1)', '2', '3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeColumns(0, [1, 2]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('dummy operation removeColumns should also be undoable', () => { - const sheet = [ - ['1'] - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeColumns(0, [1000, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores state after removing multiple column segments', () => { - const sheet = [ - ['1', '2', '3', '4'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeColumns(0, [1, 1], [3, 1]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) -}) - -describe('Undo - removing sheet', () => { - it('works for empty sheet', () => { - const engine = HyperFormula.buildFromArray([]) - engine.removeSheet(0) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([])) - }) - - it('works with restoring simple values', () => { - const sheet = [ - ['1'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeSheet(0) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('works with restoring formulas', () => { - const sheet = [ - ['=42'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeSheet(0) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores cross-sheet formula dependencies after sheet removal', () => { - const sheets = { - Sheet1: [['=Sheet2!A1']], - Sheet2: [['42']], - } - const engine = HyperFormula.buildFromSheets(sheets) - engine.removeSheet(1) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(sheets)) - }) - - it('builds formulas correctly with suspended evaluation during sheet removal', () => { - const sheets = { - Sheet1: [['=Sheet2!A1']], - Sheet2: [['42']], - } - const engine = HyperFormula.buildFromSheets(sheets) - engine.suspendEvaluation() - engine.removeSheet(1) - - engine.undo() - engine.resumeEvaluation() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(sheets)) - }) - - it('restores sheet correctly after multiple undo/redo cycles', () => { - const sheets = { - Sheet1: [['1', '2']], - Sheet2: [['3', '4']], - } - const engine = HyperFormula.buildFromSheets(sheets) - engine.removeSheet(1) - - engine.undo() - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(sheets)) - - engine.redo() - - expect(engine.getSheetNames()).toEqual(['Sheet1']) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(sheets)) - - engine.redo() - - expect(engine.getSheetNames()).toEqual(['Sheet1']) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(sheets)) - - engine.redo() - - expect(engine.getSheetNames()).toEqual(['Sheet1']) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(sheets)) - }) - - it('restores sheet and cross-sheet references after row removal', () => { - const sheets = { - Sheet1: [['1'], ['2'], ['=Sheet2!A1']], - Sheet2: [['42']], - } - const engine = HyperFormula.buildFromSheets(sheets) - engine.removeSheet(1) - engine.undo() - - expect(engine.getSheetNames()).toEqual(['Sheet1', 'Sheet2']) - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('A2'))).toBe(2) - expect(engine.getCellValue(adr('A3'))).toBe(42) - expect(engine.getCellValue(adr('A1', 1))).toBe(42) - }) - - it('restores scoped named expressions', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [['=MyName']], - Sheet2: [['1']], - }) - engine.addNamedExpression('MyName', '=42', 0) - engine.removeSheet(0) - engine.undo() - - expect(engine.getCellValue(adr('A1'))).toBe(42) - }) -}) - -describe('Undo - renaming sheet', () => { - it('undo previous operation if name not changes', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[1]]}) - engine.setCellContents(adr('A1'), [[2]]) - engine.renameSheet(0, 'Sheet1') - - engine.undo() - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getSheetName(0)).toBe('Sheet1') - }) - - it('undo rename sheet', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[1]]}) - engine.renameSheet(0, 'Foo') - - engine.undo() - - expect(engine.getSheetName(0)).toBe('Sheet1') - }) - - it('undo rename with case change only', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[1]]}) - engine.renameSheet(0, 'SHEET1') - - expect(engine.getSheetName(0)).toBe('SHEET1') - - engine.undo() - - expect(engine.getSheetName(0)).toBe('Sheet1') - }) - - it('undo rename preserves cell values', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[42], ['=A1*2']]}) - engine.renameSheet(0, 'NewName') - engine.undo() - - expect(engine.getSheetName(0)).toBe('Sheet1') - expect(engine.getCellValue(adr('A1'))).toBe(42) - expect(engine.getCellValue(adr('A2'))).toBe(84) - }) - - it('undo rename with suspended evaluation', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[1]]}) - engine.suspendEvaluation() - engine.renameSheet(0, 'Foo') - engine.undo() - engine.resumeEvaluation() - - expect(engine.getSheetName(0)).toBe('Sheet1') - }) - - it('undo rename that merged with placeholder sheet', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=OldName!A1', '=NewName!A1']], - 'OldName': [[42]], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const oldNameId = engine.getSheetId('OldName')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - - engine.renameSheet(oldNameId, 'NewName') - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(42) - - engine.undo() - - expect(engine.getSheetName(oldNameId)).toBe('OldName') - expect(engine.getCellFormula(adr('A1', sheet1Id))).toBe('=OldName!A1') - expect(engine.getCellFormula(adr('B1', sheet1Id))).toBe('=NewName!A1') - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - }) - - it('undo rename with range reference updates formula (merged with placeholder sheet)', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=SUM(OldName!A1:B2)', '=SUM(NewName!A1:B2)']], - 'OldName': [[10, 20], [30, 40]], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const oldNameId = engine.getSheetId('OldName')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(100) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - - engine.renameSheet(oldNameId, 'NewName') - - expect(engine.getCellFormula(adr('A1', sheet1Id))).toBe('=SUM(NewName!A1:B2)') - expect(engine.getCellFormula(adr('B1', sheet1Id))).toBe('=SUM(NewName!A1:B2)') - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(100) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(100) - - engine.undo() - - expect(engine.getCellFormula(adr('A1', sheet1Id))).toBe('=SUM(OldName!A1:B2)') - expect(engine.getCellFormula(adr('B1', sheet1Id))).toBe('=SUM(NewName!A1:B2)') - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(100) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - }) - - it('restores the dependency graph structure on undo', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [ - ['=OldName!A1', '=NewName!A1', '=SUM(OldName!A1:B2)', '=SUM(NewName!A1:B2)'], - ['=A1*2', '=B1+10', '=C1+A1', '=D1+B1'], - ], - 'OldName': [[1, 2], [3, 4]], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const oldNameId = engine.getSheetId('OldName')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(10) - expect(engine.getCellValue(adr('D1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - expect(engine.getCellValue(adr('A2', sheet1Id))).toBe(2) - expect(engine.getCellValue(adr('B2', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - expect(engine.getCellValue(adr('C2', sheet1Id))).toBe(11) - expect(engine.getCellValue(adr('D2', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - - engine.renameSheet(oldNameId, 'NewName') - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(10) - expect(engine.getCellValue(adr('D1', sheet1Id))).toBe(10) - expect(engine.getCellValue(adr('A2', sheet1Id))).toBe(2) - expect(engine.getCellValue(adr('B2', sheet1Id))).toBe(11) - expect(engine.getCellValue(adr('C2', sheet1Id))).toBe(11) - expect(engine.getCellValue(adr('D2', sheet1Id))).toBe(11) - - engine.undo() - - expect(engine.getCellFormula(adr('A1', sheet1Id))).toBe('=OldName!A1') - expect(engine.getCellFormula(adr('B1', sheet1Id))).toBe('=NewName!A1') - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(10) - expect(engine.getCellValue(adr('D1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - expect(engine.getCellValue(adr('A2', sheet1Id))).toBe(2) - expect(engine.getCellValue(adr('B2', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - expect(engine.getCellValue(adr('C2', sheet1Id))).toBe(11) - expect(engine.getCellValue(adr('D2', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - - engine.setCellContents(adr('A1', oldNameId), 100) - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(100) - expect(engine.getCellValue(adr('C1', sheet1Id))).toBe(109) - expect(engine.getCellValue(adr('A2', sheet1Id))).toBe(200) - expect(engine.getCellValue(adr('C2', sheet1Id))).toBe(209) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - expect(engine.getCellValue(adr('D1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - expect(engine.getCellValue(adr('B2', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - expect(engine.getCellValue(adr('D2', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - }) - - it('multiple undo/redo cycles for rename', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[1]]}) - engine.renameSheet(0, 'Renamed') - engine.undo() - - expect(engine.getSheetName(0)).toBe('Sheet1') - - engine.redo() - - expect(engine.getSheetName(0)).toBe('Renamed') - - engine.undo() - - expect(engine.getSheetName(0)).toBe('Sheet1') - - engine.redo() - - expect(engine.getSheetName(0)).toBe('Renamed') - }) - - it('undo multiple sequential renames', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[1]]}) - engine.renameSheet(0, 'Name1') - engine.renameSheet(0, 'Name2') - engine.renameSheet(0, 'Name3') - - engine.undo() - - expect(engine.getSheetName(0)).toBe('Name2') - - engine.undo() - - expect(engine.getSheetName(0)).toBe('Name1') - - engine.undo() - - expect(engine.getSheetName(0)).toBe('Sheet1') - }) - - it('undo rename combined with cell content changes', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[1]]}) - engine.setCellContents(adr('A1'), 10) - engine.renameSheet(0, 'NewName') - engine.setCellContents(adr('A1'), 100) - engine.undo() - - expect(engine.getCellValue(adr('A1'))).toBe(10) - expect(engine.getSheetName(0)).toBe('NewName') - - engine.undo() - - expect(engine.getSheetName(0)).toBe('Sheet1') - - engine.undo() - - expect(engine.getCellValue(adr('A1'))).toBe(1) - }) - - it('undo rename in batch mode', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[1]], 'Sheet2': [[2]]}) - engine.batch(() => { - engine.renameSheet(0, 'NewName1') - engine.renameSheet(1, 'NewName2') - }) - - engine.undo() - - expect(engine.getSheetName(0)).toBe('Sheet1') - expect(engine.getSheetName(1)).toBe('Sheet2') - }) - - it('undo rename with chained dependencies across sheets', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=Sheet2!A1+2']], - 'Sheet2': [['=OldName!A1*2']], - 'OldName': [[42]], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const sheet2Id = engine.getSheetId('Sheet2')! - const oldNameId = engine.getSheetId('OldName')! - - expect(engine.getCellValue(adr('A1', sheet2Id))).toBe(84) - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(86) - - engine.renameSheet(oldNameId, 'NewName') - - expect(engine.getCellFormula(adr('A1', sheet2Id))).toBe('=NewName!A1*2') - - engine.undo() - - expect(engine.getCellFormula(adr('A1', sheet2Id))).toBe('=OldName!A1*2') - expect(engine.getCellValue(adr('A1', sheet2Id))).toBe(84) - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(86) - }) - - it('undo rename with named expressions', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=MyValue']], - 'OldName': [[99]], - }, {}, [ - { name: 'MyValue', expression: '=OldName!$A$1' } - ]) - const sheet1Id = engine.getSheetId('Sheet1')! - const oldNameId = engine.getSheetId('OldName')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(99) - - engine.renameSheet(oldNameId, 'NewName') - - expect(engine.getNamedExpressionFormula('MyValue')).toBe('=NewName!$A$1') - - engine.undo() - - expect(engine.getNamedExpressionFormula('MyValue')).toBe('=OldName!$A$1') - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(99) - }) - - it('undo rename after row removal', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [[1], [2], ['=OldName!A1']], - 'OldName': [[42]], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const oldNameId = engine.getSheetId('OldName')! - - engine.removeRows(sheet1Id, [0, 1]) - engine.renameSheet(oldNameId, 'NewName') - - expect(engine.getCellValue(adr('A2', sheet1Id))).toBe(42) - expect(engine.getCellFormula(adr('A2', sheet1Id))).toBe('=NewName!A1') - - engine.undo() - - expect(engine.getCellFormula(adr('A2', sheet1Id))).toBe('=OldName!A1') - expect(engine.getCellValue(adr('A2', sheet1Id))).toBe(42) - - engine.undo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(1) - expect(engine.getCellValue(adr('A2', sheet1Id))).toBe(2) - expect(engine.getCellValue(adr('A3', sheet1Id))).toBe(42) - }) - - it('undo rename sheet that merged with placeholder restores placeholder', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=PlaceholderName!A1']], - 'OldName': [[42]], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const oldNameId = engine.getSheetId('OldName')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - - engine.renameSheet(oldNameId, 'PlaceholderName') - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - expect(engine.getSheetName(oldNameId)).toBe('PlaceholderName') - - engine.undo() - - expect(engine.getSheetName(oldNameId)).toBe('OldName') - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - }) - - it('redo rename sheet that merged with placeholder works correctly', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=PlaceholderName!A1']], - 'OldName': [[42]], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const oldNameId = engine.getSheetId('OldName')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - - engine.renameSheet(oldNameId, 'PlaceholderName') - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - - engine.undo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - - engine.redo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - expect(engine.getSheetName(oldNameId)).toBe('PlaceholderName') - }) - - it('multiple undo/redo cycles with placeholder sheet merge', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=GhostSheet!A1']], - 'RealSheet': [[100]], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const realSheetId = engine.getSheetId('RealSheet')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - - engine.renameSheet(realSheetId, 'GhostSheet') - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(100) - - engine.undo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - expect(engine.getSheetName(realSheetId)).toBe('RealSheet') - - engine.redo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(100) - expect(engine.getSheetName(realSheetId)).toBe('GhostSheet') - - engine.undo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - expect(engine.getSheetName(realSheetId)).toBe('RealSheet') - - engine.redo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(100) - expect(engine.getSheetName(realSheetId)).toBe('GhostSheet') - - engine.undo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - - engine.redo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(100) - }) - - it('undo rename with range reference to placeholder sheet', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=SUM(PlaceholderName!A1:B2)']], - 'OldName': [[1, 2], [3, 4]], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const oldNameId = engine.getSheetId('OldName')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - - engine.renameSheet(oldNameId, 'PlaceholderName') - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(10) - - engine.undo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - expect(engine.getSheetName(oldNameId)).toBe('OldName') - - engine.redo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(10) - }) -}) - -describe('Undo - setting cell content', () => { - it('restores simple numeric values', () => { - const sheet = [ - ['3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.setCellContents(adr('A1'), '100') - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores empty cell state', () => { - const sheet = [ - [null], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.setCellContents(adr('A1'), '100') - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores formula cell content', () => { - const sheet = [ - ['=42'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.setCellContents(adr('A1'), '100') - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('undoes multiple cell contents as one operation', () => { - const sheet = [ - ['3', '4'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.setCellContents(adr('A1'), [['5', '6']]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) -}) - -describe('Undo - adding sheet', () => { - it('removes named sheet on undo', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addSheet('SomeSheet') - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([])) - }) - - it('removes auto-generated sheet on undo', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addSheet() - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([])) - }) - - it('restores cross-sheet reference error after undo', () => { - const engine = HyperFormula.buildFromArray([['=NewSheet!A1']]) - engine.addSheet('NewSheet') - engine.setCellContents({sheet: 1, col: 0, row: 0}, '42') - - expect(engine.getCellValue(adr('A1'))).toBe(42) - expect(engine.getCellValue(adr('A1', 1))).toBe(42) - - engine.undo() - engine.undo() - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - }) - - it('builds formulas correctly with suspended evaluation during sheet addition', () => { - const engine = HyperFormula.buildFromArray([['=NewSheet!A1']]) - engine.suspendEvaluation() - engine.addSheet('NewSheet') - engine.setCellContents({sheet: 1, col: 0, row: 0}, '42') - - engine.undo() - engine.undo() - engine.resumeEvaluation() - - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - }) -}) - -describe('Undo - clearing sheet', () => { - it('handles undo on already empty sheet', () => { - const engine = HyperFormula.buildFromArray([]) - engine.clearSheet(0) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray([])) - }) - - it('restores simple values after clearing', () => { - const sheet = [ - ['1'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.clearSheet(0) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores formulas after clearing', () => { - const sheet = [ - ['=42'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.clearSheet(0) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) -}) - -describe('Undo - setting sheet contents', () => { - it('restores original sheet content', () => { - const sheet = [['13']] - const engine = HyperFormula.buildFromArray(sheet) - engine.setSheetContent(0, [['42']]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('also clears sheet when undoing', () => { - const sheet = [ - ['1'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.setSheetContent(0, [['42', '43']]) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) -}) - -describe('Undo - moving cells', () => { - it('restores cell to original position', () => { - const sheet = [ - ['foo'], - [null], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2')) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores overwritten data at target location', () => { - const sheet = [ - ['foo'], - ['42'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2')) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('restores dependent cell formulas after cell move', () => { - const sheet = [ - ['=A2'], - ['42'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2')) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('builds formulas correctly with suspended evaluation during cell move', () => { - const sheet = [ - ['=A2'], - ['3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.suspendEvaluation() - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2')) - - engine.undo() - engine.resumeEvaluation() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('removes global named expression promoted during move', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [], - 'Sheet2': [] - }) - engine.addNamedExpression('foo', 'bar', 0) - engine.setCellContents(adr('A1'), '=foo') - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A1', 1)) - - engine.undo() - - expect(engine.getNamedExpressionValue('foo')).toBeUndefined() - }) - - it('remove global named expression even if it was added after formula', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=foo']], - 'Sheet2': [] - }) - engine.addNamedExpression('foo', 'bar', 0) - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A1', 1)) - - engine.undo() - - expect(engine.getNamedExpressionValue('foo', 0)).toBe('bar') - expect(engine.getNamedExpressionValue('foo')).toBeUndefined() - }) -}) - -describe('Undo - cut-paste', () => { - it('restores source and target cells after cut-paste', () => { - const sheet = [ - ['foo'], - ['bar'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A2')) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('does not roll back clipboard on undo', () => { - const sheet = [ - ['foo'], - ['bar'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A2')) - engine.undo() - - expect(engine.isClipboardEmpty()).toBe(true) - }) - - it('removes global named expression promoted during cut-paste', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [], - 'Sheet2': [] - }) - engine.addNamedExpression('foo', 'bar', 0) - engine.setCellContents(adr('A1'), '=foo') - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A1', 1)) - - engine.undo() - - expect(engine.getNamedExpressionValue('foo', 0)).toBe('bar') - expect(engine.getNamedExpressionValue('foo')).toBeUndefined() - }) -}) - -describe('Undo - copy-paste', () => { - it('restores original content after copy-paste', () => { - const sheet = [ - ['foo'], - ['bar'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A2')) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('removes global named expression promoted during copy-paste', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [], - 'Sheet2': [] - }) - engine.addNamedExpression('foo', 'bar', 0) - engine.setCellContents(adr('A1'), '=foo') - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A1', 1)) - - engine.undo() - - expect(engine.getNamedExpressionValue('foo', 0)).toBe('bar') - expect(engine.getNamedExpressionValue('foo')).toBeUndefined() - }) -}) - -describe('Undo - add named expression', () => { - it('removes named expression and restores error', () => { - const engine = HyperFormula.buildFromArray([ - ['=foo'] - ]) - - engine.addNamedExpression('foo', 'foo') - - engine.undo() - - expect(engine.listNamedExpressions().length).toBe(0) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.NamedExpressionName('foo'))) - }) -}) - -describe('Undo - remove named expression', () => { - it('restores removed named expression', () => { - const engine = HyperFormula.buildFromArray([ - ['=foo'] - ]) - - engine.addNamedExpression('foo', 'foo') - engine.removeNamedExpression('foo') - - engine.undo() - - expect(engine.listNamedExpressions().length).toBe(1) - expect(engine.getCellValue(adr('A1'))).toBe('foo') - }) -}) - -describe('Undo - change named expression', () => { - it('restores original expression value', () => { - const engine = HyperFormula.buildFromArray([ - ['=foo'] - ]) - - engine.addNamedExpression('foo', 'foo') - engine.changeNamedExpression('foo', 'bar') - - engine.undo() - - expect(engine.listNamedExpressions().length).toBe(1) - expect(engine.getCellValue(adr('A1'))).toBe('foo') - }) -}) - -describe('Undo', () => { - it('throws error when undo stack is empty', () => { - const engine = HyperFormula.buildEmpty() - - expect(() => { - engine.undo() - }).toThrow(new NoOperationToUndoError()) - }) - - it('undo recomputes and return changes', () => { - const engine = HyperFormula.buildFromArray([ - ['3', '=A1'], - ]) - engine.setCellContents(adr('A1'), '100') - - const changes = engine.undo() - - expect(engine.getCellValue(adr('B1'))).toBe(3) - expect(changes.length).toBe(2) - }) - - it('operations in batch mode are one undo', () => { - const sheet = [ - ['1', '2'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.batch(() => { - engine.setCellContents(adr('A1'), '10') - engine.setCellContents(adr('A2'), '20') - }) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - - expect(engine.isThereSomethingToUndo()).toBe(false) - }) - - it('operations in batch mode are undone in correct order', () => { - const sheet = [ - ['1'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.batch(() => { - engine.setCellContents(adr('A1'), '10') - engine.removeRows(0, [0, 1]) - }) - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) - - it('keeps elements within limit', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ], {undoLimit: 3}) - engine.setCellContents(adr('A1'), '2') - engine.setCellContents(adr('A1'), '3') - engine.setCellContents(adr('A1'), '4') - engine.setCellContents(adr('A1'), '5') - - engine.undo() - engine.undo() - engine.undo() - - expect(engine.isThereSomethingToUndo()).toBe(false) - }) - - it('undo limit works with infinity', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ], {undoLimit: Infinity}) - engine.setCellContents(adr('A1'), '2') - engine.setCellContents(adr('A1'), '3') - engine.setCellContents(adr('A1'), '4') - - expect(engine.isThereSomethingToUndo()).toBe(true) - }) - - it('restore AST after irreversible operation', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('E1'), '=SUM(A1:C1)') - engine.addColumns(0, [3, 1]) - engine.removeColumns(0, [0, 1]) - - expect(() => engine.undo()).not.toThrowError() - expect(engine.getCellFormula(adr('F1'))).toBe('=SUM(A1:C1)') - }) -}) - -describe('UndoRedo', () => { - it('redo operation is pushed back on undo stack (undo-redo-undo)', () => { - const sheet = [ - ['1'], - ['2', '=A1'], // remove - ['3'], - ] - const engine = HyperFormula.buildFromArray(sheet) - engine.removeRows(0, [1, 1]) - engine.undo() - engine.redo() - - engine.undo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromArray(sheet)) - }) -}) - -describe('UndoRedo - #isThereSomethingToUndo', () => { - it('returns false when undo stack is empty', () => { - const engine = HyperFormula.buildEmpty() - - expect(engine.isThereSomethingToUndo()).toBe(false) - }) - - it('when there is some operation to undo', () => { - const engine = HyperFormula.buildFromArray([]) - engine.removeRows(0, [1, 1]) - - expect(engine.isThereSomethingToUndo()).toBe(true) - }) - - it('when the undo stack has been cleared', () => { - const engine = HyperFormula.buildFromArray([]) - engine.removeRows(0, [1, 1]) - - expect(engine.isThereSomethingToUndo()).toBe(true) - engine.clearUndoStack() - - expect(engine.isThereSomethingToUndo()).toBe(false) - }) -}) - -describe('UndoRedo - #isThereSomethingToRedo', () => { - it('when there is no operation to redo', () => { - const engine = HyperFormula.buildEmpty() - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) - - it('when there is some operation to redo', () => { - const engine = HyperFormula.buildFromArray([]) - engine.removeRows(0, [1, 1]) - engine.undo() - - expect(engine.isThereSomethingToRedo()).toBe(true) - }) - - it('when the redo stack has been cleared', () => { - const engine = HyperFormula.buildFromArray([]) - engine.removeRows(0, [1, 1]) - engine.undo() - - expect(engine.isThereSomethingToRedo()).toBe(true) - engine.clearRedoStack() - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('UndoRedo - at the Operations layer', () => { - let undoRedo: UndoRedo - - beforeEach(() => { - const config = new Config() - const stats = new Statistics() - const namedExpressions = new NamedExpressions() - const functionRegistry = new FunctionRegistry(config) - const lazilyTransformingAstService = new LazilyTransformingAstService(stats) - const dependencyGraph = DependencyGraph.buildEmpty(lazilyTransformingAstService, config, functionRegistry, namedExpressions, stats) - const columnSearch = buildColumnSearchStrategy(dependencyGraph, config, stats) - const dateTimeHelper = new DateTimeHelper(config) - const numberLiteralHelper = new NumberLiteralHelper(config) - const cellContentParser = new CellContentParser(config, dateTimeHelper, numberLiteralHelper) - const parser = new ParserWithCaching( - config, - functionRegistry, - dependencyGraph.sheetReferenceRegistrar.ensureSheetRegistered.bind(dependencyGraph.sheetReferenceRegistrar) - ) - const arraySizePredictor = new ArraySizePredictor(config, functionRegistry) - const operations = new Operations(config, dependencyGraph, columnSearch, cellContentParser, parser, stats, lazilyTransformingAstService, namedExpressions, arraySizePredictor) - undoRedo = new UndoRedo(config, operations) - }) - - it('commitBatchMode should throw when a batch is not in progress', () => { - expect(() => { - undoRedo.commitBatchMode() - }).toThrowError("Batch mode wasn't started") - }) - - it('clearUndoStack should clear out all undo entries', () => { - expect(undoRedo.isUndoStackEmpty()).toBe(true) - undoRedo.saveOperation(new AddSheetUndoEntry('Sheet 1', 1)) - undoRedo.saveOperation(new AddSheetUndoEntry('Sheet 2', 2)) - - expect(undoRedo.isUndoStackEmpty()).toBe(false) - - undoRedo.clearUndoStack() - - expect(undoRedo.isUndoStackEmpty()).toBe(true) - }) - - it('undo should throw when there is nothing on the undo stack', () => { - expect(() => { - undoRedo.undo() - }).toThrowError('Attempted to undo without operation on stack') - }) - - it('redo should throw when there is nothing on the redo stack', () => { - expect(() => { - undoRedo.redo() - }).toThrowError('Attempted to redo without operation on stack') - }) -}) - -describe('Redo - removing rows', () => { - it('re-removes empty row after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - [null], // remove - ['3'], - ]) - engine.removeRows(0, [1, 1]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('re-removes row with values and formulas after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2', '=A1'], // remove - ['3'], - ]) - engine.removeRows(0, [1, 1]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('re-removes multiple row segments after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - ['4'], - ]) - engine.removeRows(0, [1, 1], [3, 1]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('dummy removeRows operation is redoable', () => { - const engine = HyperFormula.buildFromArray([ - ['1'] - ]) - engine.removeRows(0, [1000, 1]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('removeRows clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.removeRows(0, [1000, 1]) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('Redo - adding rows', () => { - it('re-adds row after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], // add after that - ['3'], - ]) - engine.addRows(0, [1, 1]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('dummy addRows operation is redoable', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ]) - engine.addRows(0, [1000, 1]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('re-adds multiple row segments after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], - ]) - engine.addRows(0, [1, 1], [2, 1]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('addRows clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.addRows(0, [1000, 1]) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('Redo - moving rows', () => { - it('re-applies row move after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ['2'], - ['3'], // move first row before this one - ]) - engine.moveRows(0, 0, 1, 2) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('moveRows clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.moveRows(0, 0, 1, 2) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('Redo - moving columns', () => { - it('re-applies column move after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ]) - engine.moveColumns(0, 0, 1, 2) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('moveColumns clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.moveColumns(0, 0, 1, 2) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('Redo - moving cells', () => { - it('re-applies cell move after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['42'], - ['45'], - ]) - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2')) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('moveCells clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2')) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('Redo - setting cell content', () => { - it('re-applies simple value change after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['3'], - ]) - engine.setCellContents(adr('A1'), '100') - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('re-applies cell clearing after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['3'], - ]) - engine.setCellContents(adr('A1'), null) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('re-applies formula value change after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['3'], - ]) - engine.setCellContents(adr('A1'), '=42') - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('redoes multiple cell contents as one operation', () => { - const engine = HyperFormula.buildFromArray([ - ['3', '4'], - ]) - engine.setCellContents(adr('A1'), [['5', '6']]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('setCellContents clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.setCellContents(adr('A1'), 78) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('Redo - removing sheet', () => { - it('re-removes sheet after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['1'] - ]) - engine.removeSheet(0) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('removeSheet clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.removeSheet(0) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) - - it('redo with cross-sheet formula dependencies', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [['=Sheet2!A1']], - Sheet2: [['42']], - }) - engine.removeSheet(1) - engine.undo() - - expect(engine.getSheetNames()).toEqual(['Sheet1', 'Sheet2']) - - engine.redo() - - expect(engine.getSheetNames()).toEqual(['Sheet1']) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - }) - - it('removes sheet correctly after multiple undo/redo cycles', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [['1']], - Sheet2: [['2']], - }) - engine.removeSheet(1) - - engine.undo() - engine.redo() - engine.undo() - engine.redo() - engine.undo() - engine.redo() - - expect(engine.getSheetNames()).toEqual(['Sheet1']) - }) - - it('redo removes scoped named expressions', () => { - const engine = HyperFormula.buildFromSheets({ - Sheet1: [['=MyName']], - }) - engine.addNamedExpression('MyName', '=42', 0) - engine.removeSheet(0) - engine.undo() - engine.redo() - - expect(engine.listNamedExpressions().length).toBe(0) - }) -}) - -describe('Redo - adding sheet', () => { - it('re-adds named sheet after undo', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addSheet('SomeSheet') - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expect(engine.getSheetName(1)).toBe('SomeSheet') - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('re-adds auto-named sheet after undo', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addSheet() - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expect(engine.getSheetName(1)).toBe('Sheet2') - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('addSheet clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.addSheet() - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) - - it('restores cross-sheet reference after redo', () => { - const engine = HyperFormula.buildFromArray([['=NewSheet!A1']]) - engine.addSheet('NewSheet') - engine.undo() - engine.redo() - - expect(engine.getSheetName(1)).toBe('NewSheet') - }) - - it('builds formulas correctly with suspended evaluation during redo addSheet', () => { - const engine = HyperFormula.buildFromArray([['=NewSheet!A1']]) - engine.addSheet('NewSheet') - const snapshot = engine.getAllSheetsSerialized() - - engine.undo() - engine.suspendEvaluation() - engine.redo() - engine.resumeEvaluation() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) -}) - -describe('Redo - renaming sheet', () => { - it('re-applies sheet rename after undo', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[1]]}) - engine.renameSheet(0, 'Foo') - engine.undo() - - engine.redo() - - expect(engine.getSheetName(0)).toBe('Foo') - }) - - it('renameSheet clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.renameSheet(0, 'Foo') - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) - - it('redo rename with case change only', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[1]]}) - engine.renameSheet(0, 'SHEET1') - engine.undo() - engine.redo() - - expect(engine.getSheetName(0)).toBe('SHEET1') - }) - - it('redo rename preserves cell values', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[42], ['=A1*2']]}) - engine.renameSheet(0, 'NewName') - engine.undo() - engine.redo() - - expect(engine.getSheetName(0)).toBe('NewName') - expect(engine.getCellValue(adr('A1'))).toBe(42) - expect(engine.getCellValue(adr('A2'))).toBe(84) - }) - - it('redo rename with suspended evaluation', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[1]]}) - engine.renameSheet(0, 'Foo') - engine.undo() - engine.suspendEvaluation() - engine.redo() - engine.resumeEvaluation() - - expect(engine.getSheetName(0)).toBe('Foo') - }) - - it('redo rename that merged with placeholder sheet', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=OldName!A1', '=NewName!A1']], - 'OldName': [[42]], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const oldNameId = engine.getSheetId('OldName')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - - engine.renameSheet(oldNameId, 'NewName') - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(42) - - engine.undo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - - engine.redo() - - expect(engine.getSheetName(oldNameId)).toBe('NewName') - expect(engine.getCellFormula(adr('A1', sheet1Id))).toBe('=NewName!A1') - expect(engine.getCellFormula(adr('B1', sheet1Id))).toBe('=NewName!A1') - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(42) - }) - - it('redo rename with range reference updates formula (merged with placeholder sheet)', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=SUM(OldName!A1:B2)', '=SUM(NewName!A1:B2)']], - 'OldName': [[10, 20], [30, 40]], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const oldNameId = engine.getSheetId('OldName')! - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(100) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - - engine.renameSheet(oldNameId, 'NewName') - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(100) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(100) - - engine.undo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(100) - expect(engine.getCellValue(adr('B1', sheet1Id))).toEqualError(detailedError(ErrorType.REF, ErrorMessage.SheetRef)) - - engine.redo() - - expect(engine.getCellFormula(adr('A1', sheet1Id))).toBe('=SUM(NewName!A1:B2)') - expect(engine.getCellFormula(adr('B1', sheet1Id))).toBe('=SUM(NewName!A1:B2)') - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(100) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(100) - }) - - it('redo multiple sequential renames', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[1]]}) - engine.renameSheet(0, 'Name1') - engine.renameSheet(0, 'Name2') - engine.renameSheet(0, 'Name3') - engine.undo() - engine.undo() - engine.undo() - engine.redo() - - expect(engine.getSheetName(0)).toBe('Name1') - - engine.redo() - - expect(engine.getSheetName(0)).toBe('Name2') - - engine.redo() - - expect(engine.getSheetName(0)).toBe('Name3') - }) - - it('redo rename combined with cell content changes', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[1]]}) - engine.setCellContents(adr('A1'), 10) - engine.renameSheet(0, 'NewName') - engine.setCellContents(adr('A1'), 100) - engine.undo() - engine.undo() - engine.undo() - engine.redo() - - expect(engine.getCellValue(adr('A1'))).toBe(10) - - engine.redo() - - expect(engine.getSheetName(0)).toBe('NewName') - - engine.redo() - - expect(engine.getCellValue(adr('A1'))).toBe(100) - }) - - it('redo rename in batch mode', () => { - const engine = HyperFormula.buildFromSheets({'Sheet1': [[1]], 'Sheet2': [[2]]}) - engine.batch(() => { - engine.renameSheet(0, 'NewName1') - engine.renameSheet(1, 'NewName2') - }) - engine.undo() - engine.redo() - - expect(engine.getSheetName(0)).toBe('NewName1') - expect(engine.getSheetName(1)).toBe('NewName2') - }) - - it('redo rename with chained dependencies across sheets', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=Sheet2!A1+2']], - 'Sheet2': [['=NewName!A1*2']], - 'OldName': [[42]], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const sheet2Id = engine.getSheetId('Sheet2')! - const oldNameId = engine.getSheetId('OldName')! - - engine.renameSheet(oldNameId, 'NewName') - engine.undo() - engine.redo() - - expect(engine.getCellValue(adr('A1', sheet2Id))).toBe(84) - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(86) - }) - - it('redo rename with named expressions referencing placeholder', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=MyValue']], - 'OldName': [[99]], - }, {}, [ - { name: 'MyValue', expression: '=NewName!$A$1' } - ]) - const sheet1Id = engine.getSheetId('Sheet1')! - const oldNameId = engine.getSheetId('OldName')! - engine.renameSheet(oldNameId, 'NewName') - engine.undo() - engine.redo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(99) - }) - - it('redo rename after undo of combined operations', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=NewName!A1']], - 'OldName': [[42]], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const oldNameId = engine.getSheetId('OldName')! - engine.renameSheet(oldNameId, 'NewName') - engine.setCellContents(adr('A1', oldNameId), 100) - engine.undo() - engine.undo() - engine.redo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(42) - - engine.redo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(100) - }) - - it('redo rename with multiple cells referencing placeholder', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [['=NewName!A1', '=NewName!B1']], - 'Sheet2': [['=NewName!A1+10', '=NewName!B1+20']], - 'OldName': [[5, 7]], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const sheet2Id = engine.getSheetId('Sheet2')! - const oldNameId = engine.getSheetId('OldName')! - engine.renameSheet(oldNameId, 'NewName') - engine.undo() - engine.redo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(5) - expect(engine.getCellValue(adr('B1', sheet1Id))).toBe(7) - expect(engine.getCellValue(adr('A1', sheet2Id))).toBe(15) - expect(engine.getCellValue(adr('B1', sheet2Id))).toBe(27) - }) - - it('redo rename with column and row ranges', () => { - const engine = HyperFormula.buildFromSheets({ - 'Sheet1': [ - ['=SUM(NewName!A:A)'], - ['=SUM(NewName!1:2)'], - ], - 'OldName': [ - [1, 2], - [3, 4], - ], - }) - const sheet1Id = engine.getSheetId('Sheet1')! - const oldNameId = engine.getSheetId('OldName')! - engine.renameSheet(oldNameId, 'NewName') - engine.undo() - engine.redo() - - expect(engine.getCellValue(adr('A1', sheet1Id))).toBe(4) - expect(engine.getCellValue(adr('A2', sheet1Id))).toBe(10) - }) -}) - -describe('Redo - clearing sheet', () => { - it('re-clears sheet after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['1'] - ]) - engine.clearSheet(0) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('clearSheet clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.clearSheet(0) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('Redo - adding columns', () => { - it('re-adds column after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '3'], - ]) - engine.addColumns(0, [1, 1]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('dummy addColumns operation is redoable', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ]) - engine.addColumns(0, [1000, 1]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('re-adds multiple column segments after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3'], - ]) - engine.addColumns(0, [1, 1], [2, 1]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('addColumns clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.addColumns(0, [1000, 1]) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('Redo - removing column', () => { - it('re-removes empty column after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['1', null, '3'], - ]) - engine.removeColumns(0, [1, 1]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('re-removes column with values and formulas after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ['=B1'] - ]) - engine.removeColumns(0, [0, 1]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('re-removes multiple column segments after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2', '3', '4'], - ]) - engine.removeColumns(0, [1, 1], [3, 1]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('dummy removeColumns operation is redoable', () => { - const engine = HyperFormula.buildFromArray([ - ['1'] - ]) - engine.removeColumns(0, [1000, 1]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('removeColumns clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.removeColumns(0, [1000, 1]) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('Redo - cut-paste', () => { - it('re-applies cut-paste after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['foo'], - ['bar'], - ]) - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A2')) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('cut does not clear redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - - expect(engine.isThereSomethingToRedo()).toBe(true) - }) - - it('cut-paste clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.cut(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A2')) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('Redo - copy-paste', () => { - it('re-applies copy-paste after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['foo', 'baz'], - ['bar', 'faz'], - ]) - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 2, 2)) - engine.paste(adr('C3')) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('copy does not clear redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - - expect(engine.isThereSomethingToRedo()).toBe(true) - }) - - it('copy-paste clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.copy(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1)) - engine.paste(adr('A2')) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('Redo - setting sheet contents', () => { - it('re-applies sheet content change after undo', () => { - const engine = HyperFormula.buildFromArray([['13']]) - engine.setSheetContent(0, [['42']]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('clears extra cells when redoing setSheetContent', () => { - const engine = HyperFormula.buildFromArray([['13', '14']]) - engine.setSheetContent(0, [['42']]) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) - - it('setSheetContent clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.setSheetContent(0, [['42']]) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('Redo - add named expression', () => { - it('re-adds named expression after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['=foo'] - ]) - - engine.addNamedExpression('foo', 'foo') - engine.undo() - - engine.redo() - - expect(engine.listNamedExpressions().length).toBe(1) - expect(engine.getCellValue(adr('A1'))).toBe('foo') - }) - - it('addNamedExpression clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.addNamedExpression('foo', 'foo') - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('Redo - remove named expression', () => { - it('re-removes named expression after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['=foo'] - ]) - - engine.addNamedExpression('foo', 'foo') - engine.removeNamedExpression('foo') - engine.undo() - - engine.redo() - - expect(engine.listNamedExpressions().length).toBe(0) - expect(engine.getCellValue(adr('A1'))).toEqualError(detailedError(ErrorType.NAME, ErrorMessage.NamedExpressionName('foo'))) - }) - - it('removeNamedExpression clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addNamedExpression('foo', 'foo') - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.removeNamedExpression('foo') - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('Redo - change named expression', () => { - it('re-applies expression change after undo', () => { - const engine = HyperFormula.buildFromArray([ - ['=foo'] - ]) - - engine.addNamedExpression('foo', 'foo') - engine.changeNamedExpression('foo', 'bar') - engine.undo() - - engine.redo() - - expect(engine.listNamedExpressions().length).toBe(1) - expect(engine.getCellValue(adr('A1'))).toBe('bar') - }) - - it('changeNamedExpression clears redo stack', () => { - const engine = HyperFormula.buildFromArray([]) - engine.addNamedExpression('foo', 'foo') - engine.setCellContents(adr('A1'), 42) - engine.undo() - - engine.changeNamedExpression('foo', 'foo') - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) -}) - -describe('Redo - batch mode', () => { - it('multiple batched operations are one redo', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '2'], - ]) - engine.batch(() => { - engine.setCellContents(adr('A1'), '10') - engine.setCellContents(adr('A2'), '20') - }) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - - expect(engine.isThereSomethingToRedo()).toBe(false) - }) - - it('operations in batch mode are re-done in correct order', () => { - const engine = HyperFormula.buildFromArray([ - ['1'], - ]) - engine.batch(() => { - engine.setCellContents(adr('A1'), '10') - engine.removeRows(0, [0, 1]) - }) - const snapshot = engine.getAllSheetsSerialized() - engine.undo() - - engine.redo() - - expectEngineToBeTheSameAs(engine, HyperFormula.buildFromSheets(snapshot)) - }) -}) - -describe('Redo', () => { - it('throws error when redo stack is empty', () => { - const engine = HyperFormula.buildEmpty() - - expect(() => { - engine.redo() - }).toThrow(new NoOperationToRedoError()) - }) - - it('redo recomputes and return changes', () => { - const engine = HyperFormula.buildFromArray([ - ['3', '=A1'], - ]) - engine.setCellContents(adr('A1'), '100') - engine.undo() - - const changes = engine.redo() - - expect(engine.getCellValue(adr('B1'))).toBe(100) - expect(changes.length).toBe(2) - }) -}) diff --git a/test/unit/unsupported-types.spec.ts b/test/unit/unsupported-types.spec.ts deleted file mode 100644 index 8062994e64..0000000000 --- a/test/unit/unsupported-types.spec.ts +++ /dev/null @@ -1,118 +0,0 @@ -import {HyperFormula} from '../../src' -import {adr} from './testUtils' - -const BigIntSupported = (function(): boolean { - try { - const bigint = BigInt(1) - return typeof bigint === 'bigint' - } catch (e) { - return false - } -})() - -describe('unsupported types should result in error', () => { - it('should give parsing error #1', () => { - // eslint-disable-next-line - // @ts-ignore - expect(() => HyperFormula.buildFromArray([[[]]]) - ).toThrowError('Unable to parse value: []') - }) - it('should give parsing error #2', () => { - // eslint-disable-next-line - // @ts-ignore - expect(() => HyperFormula.buildFromArray([[{}]]) - ).toThrowError('Unable to parse value: {}') - }) - it('should give parsing error #3', () => { - // eslint-disable-next-line - // @ts-ignore - expect(() => HyperFormula.buildFromArray([[() => {}]])) - .toThrowError(/^Unable to parse value\: "(\(\) => \{\s*\}|function \(\) \{\s*\})"$/) - }) - it('should give parsing error #4', () => { - expect(() => HyperFormula.buildFromSheets({ - // eslint-disable-next-line - // @ts-ignore - Sheet1: [[() => {}]], - // eslint-disable-next-line - // @ts-ignore - Sheet2: [[() => {}]] - })) - .toThrowError(/^Unable to parse value\: "(\(\) \=\> \{\s*\}|function \(\) \{\s*\})"$/) - }) - it('should give parsing error #5', () => { - // eslint-disable-next-line - // @ts-ignore - expect(() => HyperFormula.buildFromArray([[Symbol()]]) - ).toThrowError('Unable to parse value: \"Symbol()\"') - }) - it('should give parsing error #6', () => { - // eslint-disable-next-line - // @ts-ignore - expect(() => HyperFormula.buildFromArray([[/abcd/]]) - ).toThrowError('Unable to parse value: \"RegExp(/abcd/)\"') - }) - it('should give parsing error #7', () => { - // eslint-disable-next-line - // @ts-ignore - expect(() => HyperFormula.buildFromArray([[{sym: Symbol()}]]) - ).toThrowError('Unable to parse value: {\n' + - ' \"sym\": \"Symbol()\"\n' + - '}') - }) - it('should give parsing error #9', () => { - // eslint-disable-next-line - // @ts-ignore - expect(() => HyperFormula.buildFromArray([[Symbol('a')]]) - ).toThrowError('Unable to parse value: \"Symbol(a)\"') - }) - it('should give parsing error #10', () => { - // eslint-disable-next-line - // @ts-ignore - expect(() => HyperFormula.buildFromArray([[[Symbol(/abcd/)]]]) - ).toThrowError('Unable to parse value: [\n' + - ' \"Symbol(/abcd/)\"\n' + - ']') - }) - it('should give parsing error #11', () => { - if (BigIntSupported) { - // eslint-disable-next-line - // @ts-ignore - expect(() => HyperFormula.buildFromArray([[BigInt(9007199254740991)]]) - ).toThrowError('Unable to parse value: \"BigInt(9007199254740991)\"') - } - }) - it('should give parsing error for setCellContents', () => { - const sheet = [ - [], - ] - const engine = HyperFormula.buildFromArray(sheet) - // eslint-disable-next-line - // @ts-ignore - expect(() => engine.setCellContents(adr('A1'), () => {})) - .toThrowError(/^Unable to parse value\: "(\(\) \=\> \{\s*\}|function \(\) \{\s*\})"$/) - // eslint-disable-next-line - // @ts-ignore - expect(() => engine.setSheetContent(0, [[() => {}]])) - .toThrowError(/^Unable to parse value\: "(\(\) \=\> \{\s*\}|function \(\) \{\s*\})"$/) - }) - - it('should give error when not an array', () => { - const sheet = [ - [], - ] - const engine = HyperFormula.buildFromArray(sheet) - // eslint-disable-next-line - // @ts-ignore - expect(() => engine.setSheetContent(0, 1) - ).toThrowError('Invalid arguments, expected an array of arrays.') - // eslint-disable-next-line - // @ts-ignore - expect(() => engine.setSheetContent(0, [1]) - ).toThrowError('Invalid arguments, expected an array of arrays.') - // eslint-disable-next-line - // @ts-ignore - expect(() => engine.setCellContents(adr('A1'), [1])) - .toThrowError('Invalid arguments, expected an array of arrays or a raw cell value.') - }) -}) diff --git a/test/unit/update-config.spec.ts b/test/unit/update-config.spec.ts deleted file mode 100644 index 15e0925e27..0000000000 --- a/test/unit/update-config.spec.ts +++ /dev/null @@ -1,125 +0,0 @@ -import {HyperFormula, ErrorType} from '../../src' -import {ErrorMessage} from '../../src/error-message' -import {plPL} from '../../src/i18n/languages' -import {adr, detailedError, resetSpy} from './testUtils' -import {BuildEngineFactory} from '../../src/BuildEngineFactory' - -describe('update config', () => { - it('simple reload preserves all values', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=A1', '=SUM(A1:B1)'], - ['#DIV/0!', '=B2', '=F('] - ]) - engine.updateConfig({}) - - expect(engine.getCellValue(adr('A1'))).toBe(1) - expect(engine.getCellValue(adr('B1'))).toBe(1) - expect(engine.getCellValue(adr('C1'))).toBe(2) - expect(engine.getCellValue(adr('A2'))).toEqualError(detailedError(ErrorType.DIV_BY_ZERO)) - expect(engine.getCellValue(adr('B2'))).toEqualError(detailedError(ErrorType.CYCLE)) - expect(engine.getCellValue(adr('C2'))).toEqualError(detailedError(ErrorType.ERROR, ErrorMessage.ParseError)) - }) - it('simple reload preserves formulas', () => { - const engine = HyperFormula.buildFromArray([ - ['1', '=A1', '=SUM(A1:B1)'], - ['#DIV/0!', '=B2', '=F('] - ]) - engine.updateConfig({}) - - expect(engine.getCellFormula(adr('B1'))).toBe('=A1') - expect(engine.getCellFormula(adr('C1'))).toBe('=SUM(A1:B1)') - expect(engine.getCellFormula(adr('B2'))).toBe('=B2') - expect(engine.getCellFormula(adr('C2'))).toBe('=F(') - }) - - it('simple reload preserves values', () => { - const engine = HyperFormula.buildFromArray([ - ['1.00000000000001', '1', '=A1-B1'], - ], {smartRounding: false}) - expect(engine.getCellValue(adr('C1'))).toBeCloseTo(0.00000000000001) - - engine.updateConfig({smartRounding: true}) - - expect(engine.getCellValue(adr('C1'))).toEqual(0) - }) - it('language reload', () => { - HyperFormula.registerLanguage('plPL', plPL) - const engine = HyperFormula.buildFromArray([ - ['=FOO()', '=SUM()', '=SUMA()', 'SUM()', '=SUM('], - ]) - engine.updateConfig({language: 'plPL'}) - - expect(engine.getCellFormula(adr('A1'))).toBe('=FOO()') - expect(engine.getCellFormula(adr('B1'))).toBe('=SUMA()') - expect(engine.getCellFormula(adr('C1'))).toBe('=SUMA()') - expect(engine.getCellFormula(adr('D1'))).toBe(undefined) - expect(engine.getCellFormula(adr('E1'))).toBe('=SUM(') - }) - - it('simple reload preserves namedexpressions', () => { - const engine = HyperFormula.buildFromArray([ - ['=TRUE', '=FALSE'], - ]) - engine.addNamedExpression('TRUE', true) - engine.addNamedExpression('FALSE', false) - engine.updateConfig({}) - - expect(engine.getCellValue(adr('A1'))).toBe(true) - expect(engine.getCellValue(adr('B1'))).toBe(false) - }) - - it('doesn\'t rebuild the engine when new config is the same as the old one', () => { - const config = { useArrayArithmetic: true } - const engine = HyperFormula.buildFromArray([[]], config) - - const rebuildEngineSpy = spyOn(BuildEngineFactory, 'rebuildWithConfig') - resetSpy(rebuildEngineSpy) - - engine.updateConfig(config) - expect(rebuildEngineSpy).not.toHaveBeenCalled() - }) - - it('rebuilds the engine when new config adds new timeFormat to the same instance of timeFormats array', () => { - const config = { timeFormats: ['hh:mm:ss.sss'] } - const engine = HyperFormula.buildFromArray([[]], config) - - const rebuildEngineSpy = spyOn(BuildEngineFactory, 'rebuildWithConfig') - resetSpy(rebuildEngineSpy) - - config.timeFormats.push('hh:mm:ss') - engine.updateConfig(config) - expect(rebuildEngineSpy).toHaveBeenCalled() - }) - - it('rebuilds the engine when new config adds new currencySymbol to the same instance of currencySymbol array', () => { - const config = { currencySymbol: ['$'] } - const engine = HyperFormula.buildFromArray([[]], config) - - const rebuildEngineSpy = spyOn(BuildEngineFactory, 'rebuildWithConfig') - resetSpy(rebuildEngineSpy) - - config.currencySymbol.push('€') - engine.updateConfig(config) - expect(rebuildEngineSpy).toHaveBeenCalled() - }) - - it('doesn\'t rebuild the engine when new config contains only the default config values', () => { - const engine = HyperFormula.buildFromArray([[]], {}) - - const rebuildEngineSpy = spyOn(BuildEngineFactory, 'rebuildWithConfig') - resetSpy(rebuildEngineSpy) - - engine.updateConfig({ useColumnIndex: false, functionArgSeparator: ',', undoLimit: 20 }) - expect(rebuildEngineSpy).not.toHaveBeenCalled() - }) - - it('doesn\'t throw after adding named expression (#1194)', () => { - const hf = HyperFormula.buildFromArray([['=42']], { - licenseKey: 'gpl-v3' - }) - - - hf.addNamedExpression('ABC', '=Sheet1!$A$1') - expect(() => hf.updateConfig({ maxRows: 101 })).not.toThrow() - }) -}) diff --git a/test/unit/volatile-functions.spec.ts b/test/unit/volatile-functions.spec.ts deleted file mode 100644 index f0980bcdb6..0000000000 --- a/test/unit/volatile-functions.spec.ts +++ /dev/null @@ -1,59 +0,0 @@ -import {HyperFormula} from '../../src' -import {AbsoluteCellRange} from '../../src/AbsoluteCellRange' -import {adr} from './testUtils' - -describe('Interpreter - function RAND', () => { - it('works', () => { - const engine = HyperFormula.buildFromArray([ - ['=RAND()', '42'], - ]) - const valueBeforeRecomputation = engine.getCellValue(adr('A1')) - - engine.setCellContents(adr('B1'), '35') - - expect(engine.getCellValue(adr('A1'))).not.toEqual(valueBeforeRecomputation) - }) - - it('cell which is dependent on volatile formula is also recomputed', () => { - const engine = HyperFormula.buildFromArray([ - ['=RAND()', '42', '=A1'], - ]) - const valueBeforeRecomputation = engine.getCellValue(adr('C1')) - - engine.setCellContents(adr('B1'), '35') - - expect(engine.getCellValue(adr('C1'))).not.toEqual(valueBeforeRecomputation) - }) - - it('formula can be recognized as volatile even if entered later', () => { - const engine = HyperFormula.buildFromArray([ - ['=A2+A3', '42'], - ]) - - engine.setCellContents(adr('A1'), '=RAND()') - - const a1 = engine.addressMapping.getCell(adr('A1'))! - expect(engine.dependencyGraph.verticesToRecompute()).toEqual([a1]) - }) - - it('volatile vertices should not be recomputed after removing from graph', () => { - const engine = HyperFormula.buildFromArray([ - ['=RAND()', '42'], - ]) - - engine.setCellContents(adr('A1'), '35') - - expect(engine.dependencyGraph.verticesToRecompute()).toEqual([]) - }) - - it('volatile formula after moving is still volatile', () => { - const engine = HyperFormula.buildFromArray([ - ['=RAND()', '42'], - ]) - - engine.moveCells(AbsoluteCellRange.spanFrom(adr('A1'), 1, 1), adr('A2')) - - const a2 = engine.addressMapping.getCell(adr('A2'))! - expect(engine.dependencyGraph.verticesToRecompute()).toEqual([a2]) - }) -}) diff --git a/tsconfig.test.json b/tsconfig.test.json deleted file mode 100644 index 0fced1bfdb..0000000000 --- a/tsconfig.test.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "outDir": "./test-jasmine", - /* Make sure proper types are used */ - "types": ["jasmine", "node", "webpack-env"], - "sourceMap": true - }, - "extends": "./tsconfig", - "include": ["src", "test"], - /* Exclude files that are specific for jest setup */ - "exclude": ["test/unit/_setupFiles/jest"] -}