diff --git a/.editorconfig b/.editorconfig index 56ab1f2..697a547 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,10 +5,8 @@ charset = utf-8 end_of_line = lf indent_size = 2 indent_style = space -# Setting this to true makes VSCode break typing flow. The following ticket mentions that Intellisense dropdowns -# disappear, and that is presumably fixed. However, if you aren't, then the cursor still jumps and typing flow is still -# broken. -# -# See https://github.com/editorconfig/editorconfig-vscode/issues/40 -# insert_final_newline = true +insert_final_newline = true trim_trailing_whitespace = true + +[*.py] +indent_size = 4 diff --git a/.gitattributes b/.gitattributes index 6313b56..fcadb2c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -* text=auto eol=lf +* text eol=lf diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 30ee981..28a3ecb 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -3,6 +3,7 @@ name: Build and Test on: pull_request: paths-ignore: + - CODEOWNERS - LICENSE - README.md push: @@ -27,19 +28,31 @@ jobs: ref: ${{ github.head_ref }} token: ${{ steps.generate-token.outputs.token }} - - name: Setup NodeJS - uses: actions/setup-node@v3 + - name: Setup Python + uses: actions/setup-python@v5 with: - node-version: 20 + python-version: '3.13' + + - name: Install poetry + run: python -m pip install poetry==2.1.1 + + - name: Configure poetry + run: poetry config virtualenvs.in-project true + + - name: Cache the virtualenv + uses: actions/cache@v4 + with: + path: ./.venv + key: ${{ runner.os }}-venv-${{ hashFiles('**/poetry.lock') }} - name: Install dependencies - run: npm ci + run: poetry install - - name: Fix - run: npm run fix + - name: Run format + run: poetry run format - - name: Build - run: npm run build + - name: Run lint + run: poetry run lint - name: Commit if: ${{ github.ref != 'refs/heads/main' }} @@ -52,4 +65,4 @@ jobs: - name: Test if: ${{ steps.commit.outputs.committed == 'false' }} - run: npm run test + run: poetry run test diff --git a/.gitignore b/.gitignore index a73f32e..9bda36a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,11 @@ +__pycache__/ +.env/ +.mypy_cache/ +.venv/ +.vscode/ build/ -coverage/ dist/ -node_modules/ -*.tgz -.env +*.egg-info/ +*.pyc +*.pyo diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 480be40..0000000 --- a/.prettierignore +++ /dev/null @@ -1,18 +0,0 @@ -build/ -coverage/ -dist/ -node_modules/ - -# Ignore all the dotfiles in the repository root. -*.* - -# Ignore specific file patterns in the repository. -*.snap -LICENSE - -# Unless they are a certain file type or explicitly included. -package.json -tsconfig.json -!*.js -!*.mjs -!*.ts diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index d7207a4..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "typescript.tsdk": "node_modules\\typescript\\lib" -} \ No newline at end of file diff --git a/README.md b/README.md index 3990444..a494795 100644 --- a/README.md +++ b/README.md @@ -2,39 +2,8 @@ Problems and solutions for LeetCode. +## Development -## Priority Queue - -Several problems require access to a priority queue; however, TypeScript itself does not have such a built-in data structure. - - -### LeetCode - -LeetCode provides support for [heaps](https://support.leetcode.com/hc/en-us/articles/360011833974-What-are-the-environments-for-the-programming-languages) via [datastructures-js/priority-queue](https://github.com/datastructures-js/priority-queue) at version `5.4.0`. This is the library used, because this repository is for LeetCode submissions. - -A couple of gotchas for version `5.4.0` that don't exist in later versions: - -- LeetCode uses `require` style imports and not `import`. Practically that means `MaxPriorityQueue` and `MinPriorityQueue` do not accept parameterized types. -- Also, `MaxPriorityQueue` and `MinPriorityQueue` are exported as values. To get the type, do `InstanceType`. -- Primitives are wrapped. That is, if you do `heap.enqueue(1)`, then you should `heap.dequeue().element` to get `1` back. -- Objects are not wrapped. That is, if you do `heap.enqueue(foo)`, then you should do `heap.dequeue()` to get `foo` back, if `foo` is an object. - - -### CoderPad - -CoderPad has no official support for priority queues. - - -### CodeSignal - -CodeSignal's official documentation does not claim support for priority queues. However, there is [documentation](https://learn.codesignal.com/preview/lessons/3525/heaps-and-priority-queues-in-javascript?utm_source=chatgpt.com) suggesting that [heap-js](https://github.com/ignlg/heap-js) is available. - - -### Other - -There are a few other options. - -- Insert into an array and sort it afterwards. See [simple-heap.ts](src/heap/simple-heap.ts). -- Use binary search to insert into an array and do not sort. See [bs-search-heap.ts](src/heap/bs-search-heap.ts). -- Roll your own heap during the interview. You beast. -- Give up. +1. Install poetry: `curl -sSL https://install.python-poetry.org | python` +1. Install dependencies: `poetry install` +1. Run tests: `poetry run test` diff --git a/eslint.config.mjs b/eslint.config.mjs deleted file mode 100644 index 3f0f6e8..0000000 --- a/eslint.config.mjs +++ /dev/null @@ -1,153 +0,0 @@ -import eslint from '@eslint/js'; -import tselint from 'typescript-eslint'; -import uimports from 'eslint-plugin-unused-imports'; - -export default tselint.config({ - extends: [eslint.configs.recommended, tselint.configs.recommended], - files: ['src/**/*.ts', 'test/**/*.ts', 'script/**/*.js'], - languageOptions: { - parserOptions: { - project: './tsconfig.json', - tsconfigRootDir: import.meta.dirname, - ecmaVersion: 2020, - sourceType: 'module' - } - }, - plugins: { - 'unused-imports': uimports - }, - rules: { - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - // Permits the violation of naming conventions for unused variables with a leading underscore. - // - // See https://typescript-eslint.io/rules/naming-convention/ - '@typescript-eslint/naming-convention': [ - 'error', - { - selector: 'variable', - format: ['camelCase', 'PascalCase', 'snake_case'], - leadingUnderscore: 'allow', - modifiers: ['unused'] - }, - { - selector: 'parameter', - format: ['camelCase', 'PascalCase', 'snake_case'], - leadingUnderscore: 'allow' - } - ], - // Enabled because one should not assign the result of a void function. - // - // See https://typescript-eslint.io/rules/no-confusing-void-expression/ - '@typescript-eslint/no-confusing-void-expression': 'error', - '@typescript-eslint/no-meaningless-void-operator': 'error', - // Disabled because LeetCode imports datastructures-js/priority-queue via require and not import. - '@typescript-eslint/no-require-imports': 'off', - '@typescript-eslint/no-this-alias': 'off', - '@typescript-eslint/no-unused-expressions': 'error', - '@typescript-eslint/no-unused-vars': 'off', - '@typescript-eslint/no-useless-constructor': 'error', - '@typescript-eslint/no-use-before-define': 'off', - '@typescript-eslint/no-var-requires': 'off', - // Disabled because there's no reason to enable this except to be pedantic. Code organization desires may dictate - // that methods not using this should still be methods. For example, there are situations where it's desirable to - // define only instance methods for ease of use, or you would prefer that the caller not have to know or care that - // a method actually uses instance variables or not. - // - // In other situations, nothing is stopping you from declaring a function outside of a class, which is functionally - // the same as a static method. - // - // See https://eslint.org/docs/latest/rules/class-methods-use-this - 'class-methods-use-this': 'off', - // Disabled because this rule cannot detect if a dependency is only for development. For example, if you have a - // postbuild.js and you include it in tsconfig.json, then this plugin will incorrectly assume that any packages - // required there should be in dependencies. - // - // In that situation, postbuild.js is included in tsconfig.json so that JavaScript files (even ones not included - // in the final build) should be linted according to @typescript-eslint. - // - // See https://stackoverflow.com/questions/61956555/why-is-typescript-eslint-parser-including-files-outside-of-those-configured-in - // See https://www.typescriptlang.org/tsconfig#include - 'import/no-extraneous-dependencies': 'off', - // Disabled because named exports are more explicit. - // - // See https://github.com/airbnb/javascript/issues/1365 - // See https://blog.neufund.org/why-we-have-banned-default-exports-and-you-should-do-the-same-d51fdc2cf2ad - 'import/prefer-default-export': 'off', - 'jest/no-disabled-tests': 'off', - 'max-classes-per-file': 'off', - // Disabled because continue ESLint specifically is concerned about continue being used with labels. Using this - // with labels is akin to a goto statement, which makes code hard to reason about. Proper use of the continue - // statement makes code easier to read. - // - // The ESLint recommendations around avoiding continue involve using if statements, which do actively degrade code - // readability. ESLint, for whatever reason, recommends this even when labels are not required. Good readability, - // in contrast, would call for using continue statements with guard clauses. - // - // See https://eslint.org/docs/latest/rules/no-continue - // See https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html - 'no-continue': 'off', - // Disabled because prettier can fix this. - 'no-irregular-whitespace': 'off', - // Disabled because it is handled by @typescript-eslint rules. - 'no-shadow': 'off', - // Disabled for convenience. Enable again if this ends up hurting the project. - // - // See https://eslint.org/docs/latest/rules/no-plusplus - 'no-plusplus': 'off', - 'no-restricted-syntax': [ - 'error', - { - selector: 'ForInStatement', - message: 'for..in loops iterate over the prototype chain, which is virtually never what you want' - }, - { - selector: 'LabeledStatement', - message: 'labels are a form of goto; using them makes code confusing and hard to maintain' - }, - { - selector: 'WithStatement', - message: 'with is disallowed in strict mode because it makes code impossible to predict and optimize' - } - ], - // Disabled because we do want to have underscore prefixed identifiers to indicate a variable is ignored. - // - // See https://eslint.org/docs/latest/rules/no-underscore-dangle - 'no-underscore-dangle': 'off', - 'no-unused-expressions': 'off', - 'no-unused-vars': 'off', - 'no-use-before-define': 'off', - 'no-useless-constructor': 'off', - // Disabled because `let [a] = arr;` or `[a] = arr` is just silly sometimes. - // - // See https://eslint.org/docs/latest/rules/prefer-destructuring - 'prefer-destructuring': [ - 'error', - { - object: false, - array: false - } - ], - 'unused-imports/no-unused-imports': 'error', - 'unused-imports/no-unused-vars': [ - 'warn', - { - args: 'all', - argsIgnorePattern: '^_', - caughtErrorsIgnorePattern: '^_', - vars: 'all', - varsIgnorePattern: '^_' - } - ], - // Enabled because it is useful for some problems. ESLint disables this by default because it assumes that a single - // & or | is a mistyped && or ||. - // - // See https://eslint.org/docs/latest/rules/no-bitwise - 'no-bitwise': 'off', - // Disabled because some solutions can be implemented more naturally with a while (true) loop. - 'no-constant-condition': 'off', - // Disabled because some solutions run more optimally if you do mutate the inputs. This will trigger even if you - // reassign a field in an object, which makes it more convenient if turned off. - 'no-param-reassign': 'off' - } -}); diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 3c29ec0..0000000 --- a/package-lock.json +++ /dev/null @@ -1,5442 +0,0 @@ -{ - "name": "@retiman/leetcode", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@retiman/leetcode", - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@datastructures-js/priority-queue": "5.4.0" - }, - "devDependencies": { - "@eslint/js": "^9.17.0", - "@tsconfig/recommended": "^1.0.8", - "@types/jest": "^29.5.14", - "@types/node": "^22.10.2", - "editorconfig": "^2.0.0", - "eslint": "^9.17.0", - "eslint-plugin-unused-imports": "^4.1.4", - "fs-extra": "^11.2.0", - "jest": "^29.7.0", - "prettier": "^3.4.2", - "ts-jest": "^29.2.5", - "ts-node": "^10.9.2", - "typescript": "^5.7.2", - "typescript-eslint": "^8.18.2" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/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, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/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, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/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 - }, - "node_modules/@babel/code-frame/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, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/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, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", - "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", - "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helpers": "^7.23.2", - "@babel/parser": "^7.23.0", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", - "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/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, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/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, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/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 - }, - "node_modules/@babel/highlight/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, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/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, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.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, - "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, - "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", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@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, - "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, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@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, - "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, - "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, - "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, - "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, - "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, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@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, - "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.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "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 - }, - "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, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@datastructures-js/heap": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@datastructures-js/heap/-/heap-3.2.0.tgz", - "integrity": "sha512-FcU5ZAyb+VIOZz1HABsJUsbJi2ZyUDO7aoe97hq4d3tK3z8nMgwdxf5bO0gafR0ExFi18YTspntqHLzt4XOgnA==", - "license": "MIT" - }, - "node_modules/@datastructures-js/priority-queue": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@datastructures-js/priority-queue/-/priority-queue-5.4.0.tgz", - "integrity": "sha512-x+EkL8tjbwBMCUyVIwsOiKnWiOvMiFAIrnWQihcO42sUZ8uc4sRsJgHAS164wnibM2kVgQUJcfvhIdssBOzxRg==", - "license": "MIT", - "dependencies": { - "@datastructures-js/heap": "^3.2.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", - "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.5", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/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/@eslint/core": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", - "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/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/@eslint/js": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", - "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", - "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", - "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "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", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/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, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/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==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/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==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/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, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/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==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@one-ini/wasm": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", - "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", - "dev": true - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@tsconfig/recommended": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/recommended/-/recommended-1.0.8.tgz", - "integrity": "sha512-TotjFaaXveVUdsrXCdalyF6E5RyG6+7hHHQVZonQtdlk1rJZ1myDIvPUUKPhoYv+JAzThb2lQJh9+9ZfF46hsA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/babel__core": { - "version": "7.20.3", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.3.tgz", - "integrity": "sha512-54fjTSeSHwfan8AyHWrKbfBWiEUrNTZsUwPTDSNaaP1QDQIZbeNUg3a59E9D+375MzUw/x1vx2/0F5LBz+AeYA==", - "dev": true, - "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.6.6", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.6.tgz", - "integrity": "sha512-66BXMKb/sUWbMdBNdMvajU7i/44RkrA3z/Yt1c7R5xejt8qh84iU54yUWCtm0QwGJlDcf/gg4zd/x4mpLAlb/w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.3.tgz", - "integrity": "sha512-ciwyCLeuRfxboZ4isgdNZi/tkt06m8Tw6uGbBSBgWrnnZGNXiEyM27xc/PjXGQLqlZ6ylbgHMnm7ccF9tCkOeQ==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.3.tgz", - "integrity": "sha512-Lsh766rGEFbaxMIDH7Qa+Yha8cMVI3qAK6CHt3OR0YfxOIn5Z54iHiyDRycHrBqeIiqGa20Kpsv1cavfBKkRSw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.8.tgz", - "integrity": "sha512-NhRH7YzWq8WiNKVavKPBmtLYZHxNY19Hh+az28O/phfp68CF45pMFud+ZzJ8ewnxnC5smIdF3dqFeiSUQ5I+pw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-zONci81DZYCZjiLe0r6equvZut0b+dBRPBN5kBDjsONnutYNtJMoWQ9uR2RkL1gLG9NMTzvf+29e5RFfPbeKhQ==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.2.tgz", - "integrity": "sha512-8toY6FgdltSdONav1XtUHl4LN1yTmLza+EuDazb/fEmRNCwjyqNVIQWs2IfC74IqjHkREs/nQ2FWq5kZU9IC0w==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.3.tgz", - "integrity": "sha512-1nESsePMBlf0RPRffLZi5ujYh7IH1BWL4y9pr+Bn3cJBdxz+RTP8bUFljLz9HvzhhOSWKdyBZ4DIivdL6rvgZg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.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", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", - "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.2.tgz", - "integrity": "sha512-g7CK9nHdwjK2n0ymT2CW698FuWJRIx+RP6embAzZ2Qi8/ilIrA1Imt2LVSeHUzKvpoi7BhmmQcXz95eS0f2JXw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.29", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.29.tgz", - "integrity": "sha512-nacjqA3ee9zRF/++a3FUY1suHTFKZeHba2n8WeDw9cCVdmzmHpIxyzOJBcpHvvEmS8E9KqWlSnWHUkOrkhWcvA==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.2", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.2.tgz", - "integrity": "sha512-5qcvofLPbfjmBfKaLfj/+f+Sbd6pN4zl7w7VSVI5uz7m9QZTuB2aZAa2uo1wHFBNN2x6g/SoTkXmd8mQnQF2Cw==", - "dev": true - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.2.tgz", - "integrity": "sha512-YJFSfbd0CJjy14r/EvWapYgV4R5CHzptssoag2M7y3Ra7XNta6GPAJPPP5KGB9j14viYXyrzRO5GkX7CRfo8/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.18.2", - "@typescript-eslint/visitor-keys": "8.18.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.2.tgz", - "integrity": "sha512-AB/Wr1Lz31bzHfGm/jgbFR0VB0SML/hd2P1yxzKDM48YmP7vbyJNHRExUE/wZsQj2wUCvbWH8poNHFuxLqCTnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "8.18.2", - "@typescript-eslint/utils": "8.18.2", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.2.tgz", - "integrity": "sha512-Z/zblEPp8cIvmEn6+tPDIHUbRu/0z5lqZ+NvolL5SvXWT5rQy7+Nch83M0++XzO0XrWRFWECgOAyE8bsJTl1GQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.2.tgz", - "integrity": "sha512-WXAVt595HjpmlfH4crSdM/1bcsqh+1weFRWIa9XMTx/XHZ9TCKMcr725tLYqWOgzKdeDrqVHxFotrvWcEsk2Tg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.18.2", - "@typescript-eslint/visitor-keys": "8.18.2", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.2.tgz", - "integrity": "sha512-Cr4A0H7DtVIPkauj4sTSXVl+VBWewE9/o40KcF3TV9aqDEOWoXF3/+oRXNby3DYzZeCATvbdksYsGZzplwnK/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.18.2", - "@typescript-eslint/types": "8.18.2", - "@typescript-eslint/typescript-estree": "8.18.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.2.tgz", - "integrity": "sha512-zORcwn4C3trOWiCqFQP1x6G3xTRyZ1LYydnj51cRnJ6hxBlr/cKPckk+PKPUw/fXmvfKTcw7bwY3w9izgx5jZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.18.2", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "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", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", - "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", - "dev": true - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.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, - "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, - "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-istanbul/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@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-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "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", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "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, - "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, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001554", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001554.tgz", - "integrity": "sha512-A2E3U//MBwbJVzebddm1YfNp7Nud5Ip+IPn4BozBmn4KqVX7AvluoIDFWjsv5OkGnKUXQVmMSoMKLa3ScCblcQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "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, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "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, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "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, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "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 - }, - "node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "dev": true, - "engines": { - "node": ">=16" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "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==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/editorconfig": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-2.0.0.tgz", - "integrity": "sha512-s1NQ63WQ7RNXH6Efb2cwuyRlfpbtdZubvfNe4vCuoyGPewNPY7vah8JUSOFBiJ+jr99Qh8t0xKv0oITc1dclgw==", - "dev": true, - "dependencies": { - "@one-ini/wasm": "0.1.1", - "commander": "^11.0.0", - "minimatch": "9.0.2", - "semver": "^7.5.3" - }, - "bin": { - "editorconfig": "bin/editorconfig" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.568", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.568.tgz", - "integrity": "sha512-3TCOv8+BY6Ltpt1/CmGBMups2IdKOyfEmz4J8yIS4xLSeMm0Rf+psSaxLuswG9qMKt+XbNbmADybtXGpTFlbDg==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "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", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", - "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.9.0", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.17.0", - "@eslint/plugin-kit": "^0.2.3", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.1", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-unused-imports": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz", - "integrity": "sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0", - "eslint": "^9.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/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, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.14.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "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_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/expect/node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/expect/node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/expect/node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.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", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "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, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/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" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", - "dev": true, - "license": "ISC" - }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "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, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/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, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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==", - "dev": true - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "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, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", - "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "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, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "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, - "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.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jake/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/jake/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/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "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 - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "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", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "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 - }, - "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, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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 - }, - "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 - }, - "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, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "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, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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 - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "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 - }, - "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, - "engines": { - "node": ">= 8" - } - }, - "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, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.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, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.2.tgz", - "integrity": "sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "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 - }, - "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 - }, - "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", - "dev": true - }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "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, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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, - "engines": { - "node": ">=6" - } - }, - "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": ">=6" - } - }, - "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==", - "dev": true, - "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" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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, - "engines": { - "node": ">=8" - } - }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "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, - "engines": { - "node": ">=8" - } - }, - "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 - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/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==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/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==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "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, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/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==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "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, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "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==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "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/pure-rand": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", - "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "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" - } - ] - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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, - "engines": { - "node": ">=8" - } - }, - "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", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "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==", - "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" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/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 - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "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==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/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, - "engines": { - "node": ">=8" - } - }, - "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==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/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, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "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==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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, - "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", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/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, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "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 - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", - "dev": true, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/ts-jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", - "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "^2.1.0", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.6.3", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "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, - "engines": { - "node": ">=4" - } - }, - "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.18.2.tgz", - "integrity": "sha512-KuXezG6jHkvC3MvizeXgupZzaG5wjhU3yE8E7e6viOvAvD9xAWYp8/vy0WULTGe9DYDWcQu7aW03YIV3mSitrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.18.2", - "@typescript-eslint/parser": "8.18.2", - "@typescript-eslint/utils": "8.18.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.2.tgz", - "integrity": "sha512-adig4SzPLjeQ0Tm+jvsozSGiCliI2ajeURDGHjZ2llnA+A67HihCQ+a3amtPhUakd1GlwHxSRvzOZktbEvhPPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.18.2", - "@typescript-eslint/type-utils": "8.18.2", - "@typescript-eslint/utils": "8.18.2", - "@typescript-eslint/visitor-keys": "8.18.2", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.18.2.tgz", - "integrity": "sha512-y7tcq4StgxQD4mDr9+Jb26dZ+HTZ/SkfqpXSiqeUXZHxOUyjWDKsmwKhJ0/tApR08DgOhrFAoAhyB80/p3ViuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.18.2", - "@typescript-eslint/types": "8.18.2", - "@typescript-eslint/typescript-estree": "8.18.2", - "@typescript-eslint/visitor-keys": "8.18.2", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" - } - }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT" - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/v8-to-istanbul": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", - "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "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, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/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==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 8acc737..0000000 --- a/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "@retiman/leetcode", - "version": "1.0.0", - "license": "MIT", - "author": "Min Huang", - "email": "min.huang@alumni.usc.edu", - "description": "Leetcode problems with solutions", - "homepage": "https://github.com/retiman/leetcode#README.md", - "repository": { - "type": "git", - "url": "git+https://github.com/retiman/leetcode.git" - }, - "scripts": { - "build": "tsc", - "clean": "node script/clean.mjs", - "fix": "npm run lint:fix && npm run format:fix", - "format": "prettier --check .", - "format:fix": "prettier --write .", - "lint": "eslint .", - "lint:fix": "eslint --fix .", - "snapshot:update": "npm run test -- -u", - "test": "jest --config test/jest.config.mjs", - "all": "npm run clean && npm run fix && npm run build && npm run test" - }, - "main": "dist/index.js", - "directories": { - "src": "src", - "test": "test" - }, - "dependencies": { - "@datastructures-js/priority-queue": "5.4.0" - }, - "devDependencies": { - "@eslint/js": "^9.17.0", - "@tsconfig/recommended": "^1.0.8", - "@types/jest": "^29.5.14", - "@types/node": "^22.10.2", - "editorconfig": "^2.0.0", - "eslint": "^9.17.0", - "eslint-plugin-unused-imports": "^4.1.4", - "fs-extra": "^11.2.0", - "jest": "^29.7.0", - "prettier": "^3.4.2", - "ts-jest": "^29.2.5", - "ts-node": "^10.9.2", - "typescript": "^5.7.2", - "typescript-eslint": "^8.18.2" - } -} diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..7788c6f --- /dev/null +++ b/poetry.lock @@ -0,0 +1,349 @@ +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. + +[[package]] +name = "black" +version = "25.1.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.9" +files = [ + {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, + {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, + {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, + {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, + {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, + {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, + {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, + {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, + {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, + {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, + {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, + {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, + {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, + {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, + {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, + {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, + {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, + {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, + {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, + {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, + {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, + {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "click" +version = "8.1.8" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "fastdiff" +version = "0.3.0" +description = "A fast native implementation of diff algorithm with a pure python fallback" +optional = false +python-versions = "*" +files = [ + {file = "fastdiff-0.3.0-py2.py3-none-any.whl", hash = "sha256:ca5f61f6ddf5a1564ddfd98132ad28e7abe4a88a638a8b014a2214f71e5918ec"}, + {file = "fastdiff-0.3.0.tar.gz", hash = "sha256:4dfa09c47832a8c040acda3f1f55fc0ab4d666f0e14e6951e6da78d59acd945a"}, +] + +[package.dependencies] +wasmer = ">=1.0.0" +wasmer-compiler-cranelift = ">=1.0.0" + +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + +[[package]] +name = "isort" +version = "6.0.1" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.9.0" +files = [ + {file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"}, + {file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"}, +] + +[package.extras] +colors = ["colorama"] +plugins = ["setuptools"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + +[[package]] +name = "packaging" +version = "24.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, +] + +[package.extras] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pyright" +version = "1.1.397" +description = "Command line wrapper for pyright" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyright-1.1.397-py3-none-any.whl", hash = "sha256:2e93fba776e714a82b085d68f8345b01f91ba43e1ab9d513e79b70fc85906257"}, + {file = "pyright-1.1.397.tar.gz", hash = "sha256:07530fd65a449e4b0b28dceef14be0d8e0995a7a5b1bb2f3f897c3e548451ce3"}, +] + +[package.dependencies] +nodeenv = ">=1.6.0" +typing-extensions = ">=4.1" + +[package.extras] +all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"] +dev = ["twine (>=3.4.1)"] +nodejs = ["nodejs-wheel-binaries"] + +[[package]] +name = "pytest" +version = "8.3.5" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, + {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "ruff" +version = "0.9.10" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"}, + {file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"}, + {file = "ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8"}, + {file = "ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029"}, + {file = "ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1"}, + {file = "ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69"}, + {file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"}, +] + +[[package]] +name = "snapshottest" +version = "1.0.0a1" +description = "Snapshot testing for pytest, unittest, Django, and Nose" +optional = false +python-versions = "*" +files = [ + {file = "snapshottest-1.0.0a1-py3-none-any.whl", hash = "sha256:fff0e1da3825c32d001018777c3b56d1eae1c850fc5b3418618da3d7f2cd152f"}, + {file = "snapshottest-1.0.0a1.tar.gz", hash = "sha256:6ef848ee4d6621baff79df6a36bb1da4d7eddf5013dc6b9ca9c361bc42c605b9"}, +] + +[package.dependencies] +fastdiff = ">=0.1.4,<1" +termcolor = "*" + +[package.extras] +nose = ["nose"] +pytest = ["pytest"] +test = ["django (>=1.10.6)", "nose", "pytest (>=4.6)", "pytest-cov"] + +[[package]] +name = "termcolor" +version = "2.5.0" +description = "ANSI color formatting for output in terminal" +optional = false +python-versions = ">=3.9" +files = [ + {file = "termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8"}, + {file = "termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "wasmer" +version = "1.1.0" +description = "Python extension to run WebAssembly binaries" +optional = false +python-versions = "*" +files = [ + {file = "wasmer-1.1.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:c2af4b907ae2dabcac41e316e811d5937c93adf1f8b05c5d49427f8ce0f37630"}, + {file = "wasmer-1.1.0-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:ab1ae980021e5ec0bf0c6cdd3b979b1d15a5f3eb2b8a32da8dcb1156e4a1e484"}, + {file = "wasmer-1.1.0-cp310-none-win_amd64.whl", hash = "sha256:d0d93aec6215893d33e803ef0a8d37bf948c585dd80ba0e23a83fafee820bc03"}, + {file = "wasmer-1.1.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:1e63d16bd6e2e2272d8721647831de5c537e0bb08002ee6d7abf167ec02d5178"}, + {file = "wasmer-1.1.0-cp37-cp37m-manylinux_2_24_x86_64.whl", hash = "sha256:85e6a5bf44853e8e6a12e947ee3412da9e84f7ce49fc165ba5dbd293e9c5c405"}, + {file = "wasmer-1.1.0-cp37-none-win_amd64.whl", hash = "sha256:a182a6eca9b46d895b4985fc822fab8da3d2f84fab74ca27e55a7430a7fcf336"}, + {file = "wasmer-1.1.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:214d9a3cfb577ea9449eb2b5f13adceae34c55365e4c3d930066beb86a7f67bc"}, + {file = "wasmer-1.1.0-cp38-cp38-manylinux_2_24_x86_64.whl", hash = "sha256:b9e5605552bd7d2bc6337519b176defe83bc69b98abf3caaaefa4f7ec231d18a"}, + {file = "wasmer-1.1.0-cp38-none-win_amd64.whl", hash = "sha256:20b5190112e2e94a8947967f2bc683c9685855d0f34130d8434c87a55216a3bd"}, + {file = "wasmer-1.1.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:ee442f0970f40ec5e32011c92fd753fb2061da0faa13de13fafc730c31be34e3"}, + {file = "wasmer-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aa112198b743cff2e391230436813fb4b244a24443e37866522b7197e3a034da"}, + {file = "wasmer-1.1.0-cp39-cp39-manylinux_2_24_x86_64.whl", hash = "sha256:c0b37117f6d3ff51ee96431c7d224d99799b08d174e30fcd0fcd7e2e3cb8140c"}, + {file = "wasmer-1.1.0-cp39-none-win_amd64.whl", hash = "sha256:a0a4730ec4907a4cb0d9d4a77ea2608c2c814f22a22b73fc80be0f110e014836"}, + {file = "wasmer-1.1.0-py3-none-any.whl", hash = "sha256:2caf8c67feae9cd4246421551036917811c446da4f27ad4c989521ef42751931"}, +] + +[[package]] +name = "wasmer-compiler-cranelift" +version = "1.1.0" +description = "The Cranelift compiler for the `wasmer` package (to compile WebAssembly module)" +optional = false +python-versions = "*" +files = [ + {file = "wasmer_compiler_cranelift-1.1.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:9869910179f39696a020edc5689f7759257ac1cce569a7a0fcf340c59788baad"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:405546ee864ac158a4107f374dfbb1c8d6cfb189829bdcd13050143a4bd98f28"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp310-none-win_amd64.whl", hash = "sha256:bdf75af9ef082e6aeb752550f694273340ece970b65099e0746db0f972760d11"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:7d9c782b7721789b16e303b7e70c59df370896dd62b77e2779e3a44b4e1aa20c"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp37-cp37m-manylinux_2_24_x86_64.whl", hash = "sha256:ff7dd5bd69030b63521c24583bf0f5457cd2580237340b91ce35370f72a4a1cc"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp37-none-win_amd64.whl", hash = "sha256:447285402e366a34667a674db70458c491acd6940b797c175c0b0027f48e64bb"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:55a524985179f6b7b88ac973e8fac5a2574d3b125a966fba75fedd5a2525e484"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp38-cp38-manylinux_2_24_x86_64.whl", hash = "sha256:bd03db5a916ead51b442c66acad38847dfe127cf90b2019b1680f1920c4f8d06"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp38-none-win_amd64.whl", hash = "sha256:157d87cbd1d04adbad55b50cb4bedc28e444caf74797fd96df17390667e58699"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:ff25fc99ebafa04a6c271d08a90d17b927930e3019a2b333c7cfb48ba32c6f71"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9697ae082317a56776df8ff7df8c922eac38488ef38d3478fe5f0ca144c185ab"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp39-cp39-manylinux_2_24_x86_64.whl", hash = "sha256:2a4349b1ddd727bd46bc5ede741839dcfc5153c52f064a83998c4150d5d4a85c"}, + {file = "wasmer_compiler_cranelift-1.1.0-cp39-none-win_amd64.whl", hash = "sha256:32fe38614fccc933da77ee4372904a5fa9c12b859209a2e4048a8284c4c371f2"}, + {file = "wasmer_compiler_cranelift-1.1.0-py3-none-any.whl", hash = "sha256:200fea80609cfb088457327acf66d5aa61f4c4f66b5a71133ada960b534c7355"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.13" +content-hash = "bc8b700268d3ea6269eb75076c073dc104fe50d562ad155f36d18018842f6aa7" diff --git a/prettier.config.mjs b/prettier.config.mjs deleted file mode 100644 index 1cf80c1..0000000 --- a/prettier.config.mjs +++ /dev/null @@ -1,16 +0,0 @@ -export default { - arrowParens: 'avoid', - bracketSameLine: true, - bracketSpacing: true, - endOfLine: 'lf', - printWidth: 120, - proseWrap: 'never', - quoteProps: 'as-needed', - semi: true, - singleQuote: true, - tabWidth: 2, - // Prettier and ESLint will fight over trailing commas. The latter has more options for configuring trailing commas, - // so disable here and let ESLint do all the work. - trailingComma: 'none', - useTabs: false -}; diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..979b066 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,43 @@ +[tool.poetry] +name = "leetcode" +version = "0.1.0" +description = "Problems and solutions for LeetCode" +authors = ["Min Huang "] +license = "MIT" +readme = "README.md" + +[tool.poetry.scripts] +all = "scripts:run_all" +format = "scripts:run_format" +lint = "scripts:run_lint" +test = "scripts:run_tests" + +[tool.poetry.dependencies] +python = "^3.13" + +[tool.poetry.group.dev.dependencies] +black = "^25.1.0" +isort = "^6.0.1" +pyright = "^1.1.397" +pytest = "^8.3.4" +ruff = "^0.9.10" +snapshottest = "^1.0.0a0" + +[tool.black] +exclude = "snapshots/" +line-length = 120 + +[tool.isort] +profile = "black" +skip = ["snapshots"] +force_sort_within_sections = true +lines_between_sections = 0 +lines_after_imports = 2 + +[tool.ruff] +exclude = ["snapshots"] +line-length = 120 + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/script/clean.mjs b/script/clean.mjs deleted file mode 100644 index 9c1ace0..0000000 --- a/script/clean.mjs +++ /dev/null @@ -1,4 +0,0 @@ -import * as fse from 'fs-extra'; - -fse.emptyDirSync('build'); -fse.emptyDirSync('dist'); diff --git a/scripts.py b/scripts.py new file mode 100644 index 0000000..a51881a --- /dev/null +++ b/scripts.py @@ -0,0 +1,25 @@ +import subprocess + + +def run_cmd(cmd): + subprocess.run(cmd, shell=True, check=True) + + +def run_all(): + run_format() + run_lint() + run_tests() + + +def run_lint(): + run_cmd("poetry run ruff check --fix src test") + run_cmd("poetry run pyright src test") + + +def run_format(): + run_cmd("poetry run black src test") + run_cmd("poetry run isort src test") + + +def run_tests(): + run_cmd("poetry run pytest test") diff --git a/src/array/best-time-to-buy-and-sell-stock-ii.ts b/src/array/best-time-to-buy-and-sell-stock-ii.ts deleted file mode 100644 index 51fda18..0000000 --- a/src/array/best-time-to-buy-and-sell-stock-ii.ts +++ /dev/null @@ -1,49 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer array prices where prices[i] is the price of a given stock on the ith day. -// -// On each day, you may decide to buy and/or sell the stock. You can only hold at most one share of the stock at any -// time. However, you can buy it then immediately sell it on the same day. -// -// Find and return the maximum profit you can achieve. -// -// See {@link https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii} -export { maxProfit }; - -// SOLUTION: -// -// The question is a bit contrived, as in reality this would never happen. Here, we are assuming we can go backwards -// in time to be able to buy at the low point and sell at the high point. Just keep that in mind: we have a time -// machine. -// -// In the easy version of this problem, we can buy the stock and sell it at some later date, ONE TIME! However, here, -// we can buy and sell as many times as we want. -// -// This actually makes the problem much easier because can simulate buying on every day and add to our profit if there -// is any. Honestly, this should be easy and the other one should be medium. -// -// COMPLEXITY: -// -// Runs in O(n) time. -function maxProfit(prices: number[]): number { - let profit = 0; - - // If we only have one day of price data, we can't make any profits at all. - if (prices.length === 1) { - return profit; - } - - for (let i = 1; i < prices.length; i++) { - // Look at the difference in price between yesterday and today. - const previous = prices[i - 1]; - const current = prices[i]; - const delta = current - previous; - - // If we end up having a profit by buying yesterday, let's sell it. - if (delta > 0) { - profit += delta; - } - } - - return profit; -} diff --git a/src/array/buildings-with-an-ocean-view.ts b/src/array/buildings-with-an-ocean-view.ts deleted file mode 100644 index e4ef23a..0000000 --- a/src/array/buildings-with-an-ocean-view.ts +++ /dev/null @@ -1,38 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// There are n buildings in a line. You are given an integer array heights of size n that represents the heights of the -// buildings in the line. -// -// The ocean is to the right of the buildings. A building has an ocean view if the building can see the ocean without -// obstructions. Formally, a building has an ocean view if all the buildings to its right have a smaller height. -// -// Return a list of indices (0-indexed) of buildings that have an ocean view, sorted in increasing order. -// -// See {@link https://leetcode.com/problems/buildings-with-an-ocean-view/} -export { findBuildings }; - -// SOLUTION: -// -// This is easier to do iterating from right to left (since the ocean is to the right). We can keep track of the -// tallest building we've seen so far, and if we encounter a building that is taller, we can add it to the list with -// ocean views. -// -// COMPLEXITY: -// -// Time complexity is O(n) because we are iterating through the list of buildings once. Space complexity is O(n) -// because we are storing a result array. -function findBuildings(heights: number[]): number[] { - const result: number[] = []; - let tallest = -Infinity; - - for (let i = heights.length - 1; i >= 0; i--) { - const height = heights[i]; - if (height > tallest) { - tallest = height; - result.push(i); - } - } - - // Problem says the buildings can't be in any order; they have to be in increasing order (of index, not height). - return result.reverse(); -} diff --git a/src/array/design-hit-counter.ts b/src/array/design-hit-counter.ts deleted file mode 100644 index e910880..0000000 --- a/src/array/design-hit-counter.ts +++ /dev/null @@ -1,69 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Design a hit counter which counts the number of hits received in the past 5 minutes (i.e., the past 300 seconds). -// -// Your system should accept a timestamp parameter (in seconds granularity), and you may assume that calls are being -// made to the system in chronological order (i.e., timestamp is monotonically increasing). Several hits may arrive -// roughly at the same time. -// -// See {@link https://leetcode.com/problems/design-hit-counter/} -export { HitCounter }; - -// SOLUTION: -// -// To do this efficiently we'll have to use a circular array buffer. This is the same technique used by time series -// databases. -// -// COMPLEXITY: -// -// Both methods run in O(1) time since we fix the array size at 300. -interface HitEvent { - timestamp: number; - hits: number; -} - -class HitCounter { - private readonly events: HitEvent[]; - - constructor() { - // Our granularity is seconds, so in theory, we only need to store only the last 300 seconds. Keep track of both - // hits by timestamp. - this.events = []; - - // Don't use Array.fill() here; you'll fill the array with 300 copies of the same object. - for (let i = 0; i < 300; i++) { - this.events[i] = { - timestamp: 0, - hits: 0 - }; - } - } - - hit(timestamp: number): void { - // Find the event in our circular buffer that would correspond to this timestamp. - const i = timestamp % 300; - const event = this.events[i]; - - // If we've already recorded hits at this timestamp, increment it. - if (event.timestamp === timestamp) { - event.hits++; - return; - } - - // Otherwise, we haven't, so set the hits to 1. - event.timestamp = timestamp; - event.hits = 1; - } - - getHits(timestamp: number): number { - let count = 0; - - for (const event of this.events) { - if (timestamp - event.timestamp < 300) { - count += event.hits; - } - } - - return count; - } -} diff --git a/src/array/dot-product-of-two-sparse-vectors.ts b/src/array/dot-product-of-two-sparse-vectors.ts deleted file mode 100644 index ddf6b20..0000000 --- a/src/array/dot-product-of-two-sparse-vectors.ts +++ /dev/null @@ -1,69 +0,0 @@ -// Given two sparse vectors, compute their dot product. -// -// Implement class SparseVector: -// -// - SparseVector(nums) Initializes the object with the vector nums -// - dotProduct(vec) Compute the dot product between the instance of SparseVector and vec -// -// A sparse vector is a vector that has mostly zero values, you should store the sparse vector efficiently and compute -// the dot product between two SparseVector. -// -// Follow up: What if only one of the vectors is sparse? -// -// See {@link https://leetcode.com/problems/dot-product-of-two-sparse-vectors/} -export { SparseVector }; - -// SOLUTION: -// -// If the vector is sparse, just store the non-zero values in a map of index to value. That will compress for storage; -// to compute the dot product you can either decompress and then do the dot product, or you can do the multiplication -// over the non-zero values directly. -// -// Doing it without decompression is more efficient, and we should start with the smaller vector to minimize the number -// of operations. -class SparseVector { - private readonly map: Map; - - constructor(nums: number[]) { - // This does the compression. - this.map = new Map(); - for (let i = 0; i < nums.length; i++) { - if (nums[i] !== 0) { - this.map.set(i, nums[i]); - } - } - } - - dotProduct(vec: SparseVector): number { - // Get the vectors in order of size; we'll iterate over the smaller vector. - const [a, b] = this.sorted(this.map, vec.map); - - // Iterate over the smaller vector and multiply the values, only if the larger vector has the same index. - let result = 0; - for (const [i, ai] of a) { - if (!b.has(i)) { - continue; - } - - const bi = b.get(i)!; - result += ai * bi; - } - - return result; - } - - // If one vector is dense, don't bother compressing it. Just compute directly. - dotProductDense(b: number[]) { - let result = 0; - for (const [i, ai] of this.map) { - const bi = b[i]; - result += ai * bi; - } - - return result; - } - - private sorted(a: Map, b: Map): [Map, Map] { - return a.size < b.size ? [a, b] : [b, a]; - } -} diff --git a/src/array/group-anagrams.ts b/src/array/group-anagrams.ts deleted file mode 100644 index c6f3a9d..0000000 --- a/src/array/group-anagrams.ts +++ /dev/null @@ -1,38 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of strings `strs`, group the anagrams together. You can return the answer in any order. -// -// An anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all -// the original letters exactly once. -// -// See {@link https://leetcode.com/problems/group-anagrams/} -export { groupAnagrams }; - -// SOLUTION: -// -// Each anagram can be rearranged into canonical form by sorting the letters. Then simply map the canonical form to -// each anagram. -// -// COMPLEXITY: -// -// Runs in O(n * m * log m) time, where n is the number of strings and m is the length of the longest string. This -// is because we have to sort each string's characters in O(m * log m), and there are n strings. -function groupAnagrams(texts: string[]): string[][] { - type Canonical = string; - type Anagram = string; - const map = new Map(); - - // Group all the anagrams together by their "canonical" form (aka a sorted version of the string). - for (let i = 0; i < texts.length; i++) { - const text = texts[i]; - const canonical = text.split('').sort().join(''); - const anagrams = map.get(canonical) ?? []; - - anagrams.push(text); - map.set(canonical, anagrams); - } - - // The map.values() function gives you an iterable of string[], so we need to convert it to an array of string[] - // instead. - return Array.from(map.values()); -} diff --git a/src/array/longest-consecutive-sequence.ts b/src/array/longest-consecutive-sequence.ts deleted file mode 100644 index 1ccaa05..0000000 --- a/src/array/longest-consecutive-sequence.ts +++ /dev/null @@ -1,60 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an unsorted array of integers nums, return the length of the longest consecutive elements sequence. -// -// You must write an algorithm that runs in O(n) time. -// -// See {@link https://leetcode.com/problems/longest-consecutive-sequence/} -export { longestConsecutive }; - -// SOLUTION: -// -// The problem is phrased in a very confusing way. The sequence doesn't ACTUALLY need to be consecutive within the -// array; it only needs to be consecutive if you plucked the sequence out of the array and sorted it. -// -// For example, [1, 500, 2, 3, 4] has consecutive elements [1, 2, 3, 4]. The fact that 500 appears in the middle -// is okay; the number 500 begins its own consecutive sequence of length 1. The other elements [1, 2, 3, 4] create -// a sequence of length 4. -// -// Conceptually, we'll do this by throwing all the array elements into a set. Then, for each element `x`, we can figure -// out if it's part of a sequence by repeatedly checking its predecessor `x - 1` in the set. -// -// COMPLEXITY: -// -// Runs in O(n) time. It may appear that the inner loop runs multiple times, but each element in the array is only -// processed once; the inner loop will skip `x - 1` if it was already part of some other sequence from a previous -// iteration. -function longestConsecutive(xs: number[]) { - let longest = 0; - - // Use a set to keep track of all elements in the array; we'll reference it to find out if a predecessor to an - // element exists as we iterate through the array. - const set = new Set(xs); - - for (let i = 0; i < xs.length; i++) { - // Let us consider if element x is part of some sequence. We can do this by considering its predecessor, x - 1. - let x = xs[i]; - - // If x - 1 is not in the set, x must begin some new sequence. Let's find out how long it is by incrementing x - // until we can't find any more consecutive elements. - if (!set.has(x - 1)) { - let length = 1; - x++; - - // Continue to increment x and look for consecutive elements in the set. - while (set.has(x)) { - length++; - x++; - } - - // Once we run out of elements, we have determined the length of the sequence between at x. We'll compare it to - // the longest sequence we've found so far. - longest = Math.max(longest, length); - } else { - // If x - 1 is in the set, we know it is part of some sequence. However, this sequence must've already been - // found by the inner loop above, so we can skip this element. - } - } - - return longest; -} diff --git a/src/array/max-chunks-to-make-sorted.ts b/src/array/max-chunks-to-make-sorted.ts deleted file mode 100644 index 34efbe9..0000000 --- a/src/array/max-chunks-to-make-sorted.ts +++ /dev/null @@ -1,37 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer array arr of length n that represents a permutation of the integers in the range [0, n - 1]. -// -// We split arr into some number of chunks (i.e., partitions), and individually sort each chunk. After concatenating -// them, the result should equal the sorted array. -// -// Return the largest number of chunks we can make to sort the array. -// -// See {@link https://leetcode.com/problems/max-chunks-to-make-sorted/} -export { maxChunksToSorted }; - -// SOLUTION: -// -// Because we know the elements in the array are a permutation of numbers less than n, we can use a greedy approach -// by keeping track of the max element seen so far. -// -// Unlike the other problem, this does not require using a stack. -function maxChunksToSorted(xs: number[]) { - let chunks = 0; - let max = 0; - - for (let i = 0; i < xs.length; i++) { - max = Math.max(max, xs[i]); - - // Because all elements are a permutation of the numbers from 0 to xs.length - 1, if we encounter i === max, - // this means that for sure all elements < i can be sorted to form a chunk. - // - // Again, because all elements to the right of i are going to be greater than max (and i), then we can be sure - // that the next element begins a new chunk. - if (i === max) { - chunks++; - } - } - - return chunks; -} diff --git a/src/array/maximum-swap.ts b/src/array/maximum-swap.ts deleted file mode 100644 index ccea926..0000000 --- a/src/array/maximum-swap.ts +++ /dev/null @@ -1,75 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer num. You can swap two digits at most once to get the maximum valued number. -// -// Return the maximum valued number you can get. -// -// See {@link https://leetcode.com/problems/maximum-swap/} -export { maximumSwap }; - -// SOLUTION: -// -// The simple idea of swapping the largest number with the first digit doesn't always work. It works a lot of the time, -// but a couple of cases mess up this algorithm: -// -// - 98368 would stay 98368; we need to swap the 3 and 8 to get 98863. -// - 1993 could become 9193, but we need to swap the 1 and other 9 to get 9913. -// -// The way to think about this is: -// -// 1. Find the FIRST digit that can be made bigger (larger digits appear later). -// 2. Swap it with the LAST occurrence of the largest digit. -// -// COMPLEXITY: -// -// The time complexity is O(n) where n is the number of digits. We iterate through the digits once to create our array -// and map. We iterate through the digits a second time to find the swap point. There is a nested loop, but the inner -// loop is bounded by the 10 digits, so it doesn't cause O(n^2) time complexity. -// -// The space complexity is O(n) because we store an array and map of the digits. -function maximumSwap(num: number): number { - // First convert the number to an array of digits. - const digits = num.toString().split('').map(Number); - - // Create a map of the last seen index of each digit. Use this to find out the last occurrence of any digit. - type Digit = number; - type LastSeen = number; - const map = new Map(); - for (let i = 0; i < digits.length; i++) { - const digit = digits[i]; - map.set(digit, i); - } - - // Find the first digit that can be made bigger by iterating through the digits list. - for (let i = 0; i < digits.length; i++) { - const smaller = digits[i]; - - // Find a larger digit to swap the smaller digit with. To do so, start at 9 and iterate downwards until we find a - // digit that is larger, and appears later in the number. - for (let larger = 9; larger >= 0; larger--) { - // Skip the digit if it's not larger. - if (larger <= smaller) { - break; - } - - // Skip the digit if it doesn't appear at all. - if (!map.has(larger)) { - continue; - } - - // Skip the digit if it appears before the smaller digit. - const j = map.get(larger)!; - if (j <= i) { - continue; - } - - // Success! We have found the largest digit that occurs as late as possible! Swap the digits and return the - // number. - [digits[i], digits[j]] = [digits[j], digits[i]]; - return Number(digits.join('')); - } - } - - // We made no swaps, so just return the number as is. - return num; -} diff --git a/src/array/merge-sorted-array.ts b/src/array/merge-sorted-array.ts deleted file mode 100644 index 64c356a..0000000 --- a/src/array/merge-sorted-array.ts +++ /dev/null @@ -1,72 +0,0 @@ -// DIFFICULTY: EASY -// -// You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, and two integers m and n, -// representing the number of elements in nums1 and nums2 respectively. -// -// Merge nums1 and nums2 into a single array sorted in non-decreasing order. -// -// The final sorted array should not be returned by the function, but instead be stored inside the array nums1. To -// accommodate this, nums1 has a length of m + n, where the first m elements denote the elements that should be merged, -// and the last n elements are set to 0 and should be ignored. nums2 has a length of n. -// -// See {@link https://leetcode.com/problems/merge-sorted-array/} -export { merge }; - -// SOLUTION: -// -// TLDR: Work backwards. Send the largest elements from the smaller array to the end of the bigger array. -// -// This would be VERY easy, if you could use extra memory. To merge without using extra memory, we do need to merge -// into the bigger array, nums1. -// -// You could go about this in two different ways: -// -// 1. Start from the logical beginning of both arrays and swap elements as needed to maintain the correct order. -// 2. Start from the logical end of both arrays and add largest elements to the end of nums1. -// -// Going with option 1 is more natural, but it's more error prone and more work. There may be elements at the end of -// nums1 that need to be shifted to the right to make room. -// -// Going with option 2 is less natural, but it ensures you have enough space. Since nums1 has size m + n, and nums2 has -// size n, you can reliably fit all of nums2 into nums1 without any collisions or shifting. Additionally, if you use -// up all the elements in nums2, you don't need to do anything with the remaining elements of nums1 since they are -// already sorted. -// -// Pro tip: when asked to do something in place, and you have extra space, consider doing backwards iteration instead of -// forwards to avoid collisions and overwriting elements. Secondly, if you started with forwards iteration and realize -// you might have to shift elements, consider a backwards iteration approach to see if it might work better. -// -// The problem doesn't state this, but we assume using no extra memory either. -function merge(nums1: number[], m: number, nums2: number[], n: number): void { - let i = m - 1; - let j = n - 1; - let last = m + n - 1; - - // Send the largest elements from both arrays to the end of the array nums1. - while (i >= 0 && j >= 0) { - const a = nums1[i]; - const b = nums2[j]; - - if (a < b) { - nums1[last] = nums2[j]; - j--; - last--; - } else { - nums1[last] = nums1[i]; - i--; - last--; - } - } - - // Now we have consumed all elements in one of the arrays. - // - // If there are remaining elements in nums2, we should add them to the array nums1. - while (j >= 0) { - nums1[last] = nums2[j]; - j--; - last--; - } - - // On the other hand, if there are remaining elements in nums2, there's nothing to be done because that slice of the - // array is already sorted. -} diff --git a/src/array/pascals-triangle.ts b/src/array/pascals-triangle.ts deleted file mode 100644 index 4706d8c..0000000 --- a/src/array/pascals-triangle.ts +++ /dev/null @@ -1,41 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer numRows, return the first numRows of Pascal's triangle. -// -// In Pascal's triangle, each number is the sum of the two numbers directly above it as shown: -// -// See {@link https://leetcode.com/problems/pascals-triangle/} -export { generate }; - -// SOLUTION: -function generate(numRows: number): number[][] { - const triangle: number[][] = []; - - for (let i = 0; i < numRows; i++) { - // Special case the first row. - if (i === 0) { - triangle.push([1]); - continue; - } - - // The first and last elements of this row have value 1. - const current: number[] = []; - current.push(1); - - // The middle elements gotten by summing pairs of the previous row. - const previous = triangle[i - 1]; - if (previous.length > 1) { - for (let j = 1; j < previous.length; j++) { - current.push(previous[j] + previous[j - 1]); - } - } - - // The first and last elements of this row have value 1. - current.push(1); - - // Push the constructed row onto the triangle. - triangle.push(current); - } - - return triangle; -} diff --git a/src/array/top-k-frequent-elements.ts b/src/array/top-k-frequent-elements.ts deleted file mode 100644 index ad995f2..0000000 --- a/src/array/top-k-frequent-elements.ts +++ /dev/null @@ -1,36 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any -// order. -// -// See {@link https://leetcode.com/problems/top-k-frequent-elements/} -export { topKFrequent }; - -// SOLUTION: -// -// Just map each number to its frequency then sort by frequency. Return the first k elements. -// -// COMPLEXITY: -// -// Time complexity dominated by the sort, which is O(n log n). Space complexity is O(n) because we are using a map to -// to store frequency. -function topKFrequent(xs: number[], k: number) { - type Frequency = number; - const map = new Map(); - - // Map each number to its frequency. - for (let i = 0; i < xs.length; i++) { - const x = xs[i]; - const frequency = map.get(x) ?? 0; - map.set(x, frequency + 1); - } - - // Now get all unique elements from the list. - const uniques = [...map.keys()]; - - // Sort the unique values by their frequency. Since we want the most frequent elements, we sort in decreasing order. - uniques.sort((a, b) => map.get(b)! - map.get(a)!); - - // Return the first k elements. - return uniques.slice(0, k); -} diff --git a/src/array/two-sum.ts b/src/array/two-sum.ts deleted file mode 100644 index 75ca38a..0000000 --- a/src/array/two-sum.ts +++ /dev/null @@ -1,40 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to -// target. -// -// You may assume that each input would have exactly one solution, and you may not use the same element twice. -// -// You can return the answer in any order. -// -// See {@link https://leetcode.com/problems/two-sum/} -export { twoSum }; - -// SOLUTION: -// -// Use a hashmap to keep track of values we've already seen. This lets us avoid the O(n^2) solution of checking every -// possible pair of values. -// -// When iterating, you can check if the complement nums[j] = target - nums[i] already exists in the hashmap. If it -// does then we've found the solution and can return the indices immediately. -function twoSum(xs: number[], target: number) { - type Index = number; - type Complement = number; - const m = new Map(); - - for (let i = 0; i < xs.length; i += 1) { - const x = xs[i]; - const y = target - x; - - // If our map has the complementary value that would make up the target, we can return it immediately. - if (m.has(y)) { - return [i, m.get(y)]; - } - - // Otherwise, store the number and its index in the map. If we find the complement later, then this index will be - // the complement's complement and we can return the indices as normal. - m.set(x, i); - } - - return []; -} diff --git a/src/binary-search/find-first-and-last-position-of-element-in-sorted-array.ts b/src/binary-search/find-first-and-last-position-of-element-in-sorted-array.ts deleted file mode 100644 index 756805c..0000000 --- a/src/binary-search/find-first-and-last-position-of-element-in-sorted-array.ts +++ /dev/null @@ -1,86 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value. -// -// If target is not found in the array, return [-1, -1]. -// -// You must write an algorithm with O(log n) runtime complexity. -// -// @see {@link https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/} -export { searchRange }; - -// SOLUTION: -// -// The problem stipulates it must run in O(log n) time complexity, so that means we can't just do a standard binary -// search and then a linear scan for the start and end ranges. -// -// The most braindead simple way to do this is to do binary search twice. Once using the left-most duplicate technique -// and once using the right-most duplicate technique. -// -// Technically we could combine both the left and right searches into one binary search, using a flag to denote the -// direction we are searching in. But that would be a bit more complex and harder to understand. -// -// COMPLEXITY: -// -// Runs in O(log n) time complexity because of max two binary searches. Uses O(1) space complexity. -function searchRange(nums: number[], target: number): number[] { - // Run binary search using the left-most duplicate technique. Here if we find that the the middle element is less - // than the target, we move to the right. - // - // Otherwise if the middle element is greater than or equal to the target, we move to the left (guaranteeing that if - // we find a duplicate target, we will keep moving left). - function binarySearchLeft() { - let left = 0; - let right = nums.length; - - while (left < right) { - const m = Math.floor((left + right) / 2); - if (nums[m] < target) { - left = m + 1; - } else { - right = m; - } - } - - // If we have found the target, return the index, otherwise return -1. - if (left < nums.length && nums[left] === target) { - return left; - } else { - return -1; - } - } - - // Run binary search using the right-most duplicate technique. Here if we find that the the middle element is greater - // than the target, we move to the left. - // - // Otherwise if the middle element is less than or equal to the target, we move to the right (guaranteeing that if - // we find a duplicate target, we will keep moving right). - function binarySearchRight() { - let left = 0; - let right = nums.length; - - while (left < right) { - const m = Math.floor((left + right) / 2); - if (nums[m] > target) { - right = m; - } else { - left = m + 1; - } - } - - if (right > 0 && nums[right - 1] === target) { - return right - 1; - } else { - return -1; - } - } - - // Now we just run both binary searches and return the range. - const left = binarySearchLeft(); - if (left === -1) { - return [-1, -1]; - } - - const right = binarySearchRight(); - return [left, right]; -} diff --git a/src/binary-search/find-peak-element.ts b/src/binary-search/find-peak-element.ts deleted file mode 100644 index e41eff9..0000000 --- a/src/binary-search/find-peak-element.ts +++ /dev/null @@ -1,79 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// A peak element is an element that is strictly greater than its neighbors. -// -// Given a 0-indexed integer array nums, find a peak element, and return its index. If the array contains multiple -// peaks, return the index to any of the peaks. -// -// You may imagine that nums[-1] = nums[n] = -∞. In other words, an element is always considered to be strictly greater -// than a neighbor that is outside the array. -// -// You must write an algorithm that runs in O(log(n)) time. -// -// See {@link https://leetcode.com/problems/find-peak-element/} -export { findPeakElement }; - -// SOLUTION: -// -// This problem is not hard, but the wording is incredibly tricky. First off, let's talk about linear scan versus -// binary search. There are only 1000 elements max in the array, so either would work. However, the problem says that -// the algorithm must run in O(log(n)) time. This means we have to use binary search. -// -// Yes, binary search will still work even if the array is not sorted. That's because at each step, we are chasing -// a gradient uphill. For example, if we see [..., 10, 5, 5, ...] then we know that the elements are RISING in the left -// direction (and then eventually fall, because the left side element is -Infinity). So, we should slice the array in -// half and look for a peak in the left half; the opposite logic applies if the array is rising to the right. -// -// Secondly, the problem doesn't explicitly say what happens if there are no peaks. The solution I give throws an error -// if none are found (e.g. [1, 1, 1], or [1, 10, 10, 1]). Nowhere does LeetCode say what should happen if the inputs -// are invalid (e.g. have no peaks), or that all inputs are valid. However, it does apply to pass if we assume peaks -// always exist! -function findPeakElement(nums: number[]): number { - if (nums.length === 1) { - return 0; - } - - // Use insertion point binary search to find the peak. - let left = 0; - let right = nums.length; - while (left < right) { - const mid = Math.floor((left + right) / 2); - const prev = mid === 0 ? -Infinity : nums[mid - 1]; - const next = mid === nums.length - 1 ? -Infinity : nums[mid + 1]; - - // If we've found a peak, we can simply return it. - if (nums[mid] > prev && nums[mid] > next) { - return mid; - } - - // Otherwise, if the right side number is larger, then a peak must exist somewhere on the right, so update the left - // boundary. - if (next > nums[mid]) { - left = mid + 1; - } - // Likewise, if the left side number is larger, then a peak must exist somewhere on the left, so update the right - // boundary. - else if (prev > nums[mid]) { - right = mid; - } - // Oh, wait, what if we have a plateau? Like [..., 10, 10, 10, ...]. We don't know which direction to go, and in - // fact, binary search would NOT even work here. Well, the problem doesn't tell you this, but this can never - // happen with the inputs you are given, haha! - else { - throw new Error('there is a plateau in the middle of the array'); - } - } - - // If we've reached this point, we have possibly found a peak. In theory, we could have landed on a plateau after - // running through the binary search, and the problem doesn't say what to do, but it appears you never get any inputs - // that cause this to happen. - const mid = left; - const prev = mid === 0 ? -Infinity : nums[mid - 1]; - const next = mid === nums.length - 1 ? -Infinity : nums[mid + 1]; - if (nums[mid] > prev && nums[mid] > next) { - return mid; - } - - // This never happens. As far as I can tell, you never even get an input with a plateau. - throw new Error('we landed on a plateau after performing binary search'); -} diff --git a/src/binary-search/find-the-smallest-divisor-given-a-threshold.ts b/src/binary-search/find-the-smallest-divisor-given-a-threshold.ts deleted file mode 100644 index 26df12a..0000000 --- a/src/binary-search/find-the-smallest-divisor-given-a-threshold.ts +++ /dev/null @@ -1,48 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of integers nums and an integer threshold, we will choose a positive integer divisor, divide all the -// array by it, and sum the division's result. Find the smallest divisor such that the result mentioned above is less -// than or equal to threshold. -// -// Each result of the division is rounded to the nearest integer greater than or equal to that element. -// (For example: 7/3 = 3 and 10/2 = 5). -// -// The test cases are generated so that there will be an answer. -// -// See {@link https://leetcode.com/problems/find-the-smallest-divisor-given-a-threshold/} -export { smallestDivisor }; - -// SOLUTION: -// -// The smallest divisor is 1. This would maximize the sum. -// -// The largest divisor is Math.max(...nums). This would minimize the sum. -// -// We want a sum that is exactly the threshold or just belong. This is a good candidate to use binary search. -function smallestDivisor(nums: number[], threshold: number): number { - function sum(xs: number[], divisor: number) { - // We do Math.ceil because the problem asks us to round up. - return xs.map(x => Math.ceil(x / divisor)).reduce((a, b) => a + b); - } - - // Use the insert point binary search approach to find the divisor we want. - let left = 1; - let right = Math.max(...nums); - while (left < right) { - const mid = Math.floor((left + right) / 2); - const divisor = mid; - const value = sum(nums, divisor); - - // If the value is too large, our divisor was too small, so we should shift our left value to be mid + 1. - if (value > threshold) { - left = mid + 1; - } - // Otherwise, we should shift our right value to mid. - else { - right = mid; - } - } - - // Found the divisor at the "insertion point". - return left; -} diff --git a/src/binary-search/k-th-missing-positive-number.ts b/src/binary-search/k-th-missing-positive-number.ts deleted file mode 100644 index a33a4f8..0000000 --- a/src/binary-search/k-th-missing-positive-number.ts +++ /dev/null @@ -1,55 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an array arr of positive integers sorted in a strictly increasing order, and an integer k. -// -// Return the kth positive integer that is missing from this array. -// -// See {@link https://leetcode.com/problems/kth-missing-positive-number/} -export { findKthPositive }; - -// SOLUTION: -// -// There are multiple naive ways to do this. One way is to throw all elements into a set, then iterate from 1 to the -// max set element, and check if the element is in the set. Do this k times to find the k-th missing element. -// -// Another naive way to do this is to do a linear scan, and keep count of the missing numbers. You'd also keep a count -// of what the current element *should* be versus the current index. If the current index matches current element, then -// continue on. Otherwise increment the count of missing numbers. If the count of missing numbers is equal to k, then -// you've found it. -// -// Finally, you can use binary search to find the k-th missing element. Since the array is sorted, there's no need to -// scan the entire array. We can calculate how many missing numbers exist at each index, and use that information to -// determine if we should go left or right. -function findKthPositive(arr: number[], k: number): number { - // Use insertion point binary search to find the k-th missing element. - let left = 0; - let right = arr.length; - while (left < right) { - const mid = Math.floor((left + right) / 2); - - // So at the mid index, if there were no missing elements, then arr[mid] should be mid + 1. For example, if we have - // - // [1, 2, 3, 4, 5] - // - // ...then mid === 2, and arr[2] === 3. And 3 === mid + 1. Therefore, there are arr[2] - (2 + 1) missing elements, - // and here we have 0 missing elements. - const missing = arr[mid] - (mid + 1); - - // If there aren't enough missing numbers, then the k-th missing number must be to the right, so update the left - // pointer to be mid + 1. - if (missing < k) { - left = mid + 1; - } - // If there are too many missing numbers, then the k-th missing number must be to the left, so update the right - // pointer to be mid - 1; - else { - right = mid; - } - } - - // At this point, left === right, and left represents the index where missing >= k. Recall that if no numbers were - // missing, then arr[left] + 1 === left. If we assume that arr[left] + 1 > left by a COUNT of k elements, then we - // can find the VALUE of the k-th missing element, we add k to left. This also works if there are no "missing" - // elements (e.g. [1, 2, 3, 4]). - return left + k; -} diff --git a/src/binary-search/k-th-smallest-element-in-a-sorted-matrix.ts b/src/binary-search/k-th-smallest-element-in-a-sorted-matrix.ts deleted file mode 100644 index b0001cb..0000000 --- a/src/binary-search/k-th-smallest-element-in-a-sorted-matrix.ts +++ /dev/null @@ -1,99 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an n x n matrix where each of the rows and columns is sorted in ascending order, return the kth smallest -// element in the matrix. -// -// Note that it is the kth smallest element in the sorted order, not the kth distinct element. -// -// You must find a solution with a memory complexity better than O(n^2). -// -// See {@link https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/} -export { kthSmallest }; - -// SOLUTION: -// -// We can't flatten the list because that would take O(n^2) memory. -// -// Use binary search to narrow down where the kth smallest element is. Here, the left value is the smallest element, -// and the right value is the largest element. -// -// We can use the mid element to tell us how close or far we are from the kth smallest element. The matrix is sorted, -// so for any mid === matrix[i][j] we can figure out how many elements are less than or equal to mid. -// -// COMPLEXITY: -// -// We are using binary search on a range between the smallest and largest elements in the matrix, call it k. We will -// do O(log k) iterations, but in each iteration, we do have to count how many elements are less than or equal to the -// mid target. -// -// The countLessThanOrEqualTo() function takes at most n steps (where n is the number of rows/columns in the matrix). -// Since this is done at every single iteration, the total time is O(n log k). -// -// Space complexity is O(1). -function kthSmallest(matrix: number[][], k: number): number { - // Use insertion sort binary search to find exactly where the kth smallest element is; don't use the standard binary - // search algorithm because we're not looking for an exact value. - let left = matrix[0][0]; - let right = matrix[matrix.length - 1][matrix.length - 1]; - while (left < right) { - const mid = Math.floor((left + right) / 2); - const count = countLessThanOrEqualTo(matrix, mid); - - if (count < k) { - left = mid + 1; - } else { - right = mid; - } - } - - return left; -} - -function countLessThanOrEqualTo(matrix: number[][], target: number) { - // We can leverage the properties of the matrix to count how many elements are less than or equal to the target. - // Use a modified two pointer technique to count up elements. - let count = 0; - - // Start at the bottom left corner of the matrix: - // - // - - - - - // - - - - - // - - - - - // x - - - - // - // If the value is less than or equal to the target, then all elements from all previous rows are also less than our - // target (since the matrix is sorted). To count up that specific column, add (row + 1) to the count (the rows are - // 0 indexed, but the count is not, so we add 1). - let row = matrix.length - 1; - let column = 0; - while (row >= 0 && column < matrix.length) { - // Add up all the elements in the column. That is: - // - // x - - - - // x - - - - // x - - - - // x - - - - // - // All x's get added to the count, which means all elements in the column get added to the count. Because the - // matrix is sorted, all elements in the above column MUST be less than or equal to the target. - // - // Stop when we reach column === matrix.length - 1. - if (matrix[row][column] <= target) { - count += row + 1; - column++; - } - // If the element is greater than the target, we can move up a row and try again: - // - // x - - - - // x - - - - // x - - - - // - - - - - // - // Stop when we reach row === 0. - else { - row--; - } - } - - return count; -} diff --git a/src/binary-search/longest-increasing-subsequence.ts b/src/binary-search/longest-increasing-subsequence.ts deleted file mode 100644 index e8d2df1..0000000 --- a/src/binary-search/longest-increasing-subsequence.ts +++ /dev/null @@ -1,58 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums, return the length of the longest strictly increasing subsequence. -// -// @see {@link https://leetcode.com/problems/longest-increasing-subsequence/} -export { lengthOfLIS }; - -// SOLUTION: -// -// The naive way is to do this with dynamic programming and two nested loops. -// -// The more efficient way is to do binary search after constructing a "tails" array. The tails array represents the -// smallest possible ending element for a subsequence of length i + 1. For example, if tails is [2, 3, 7], that means: -// -// - There is a length 1 subsequence ending in 2. -// - There is a length 2 subsequence ending in 3. -// - There is a length 3 subsequence ending in 7. -// -// As we iterate through the array, we will keep the tails array updated. At the end of iteration, the length of the -// tails array will tell us the longest increasing subsequence. -// -// COMPLEXITY: -// -// Time complexity is O(n log n) because we are doing a binary search for each element in the array. Space complexity -// is O(n) because we are using an array to store the tails. -function lengthOfLIS(nums: number[]): number { - const tails: number[] = []; - - for (const num of nums) { - // Now we do a binary search to figure out where to insert num into tails. - let left = 0; - let right = tails.length; - while (left < right) { - const mid = Math.floor((left + right) / 2); - if (tails[mid] < num) { - // Move to the right if num is larger. - left = mid + 1; - } else { - // Move to the left if num is smaller. - right = mid; - } - } - - // We found an index in tails; this number is the smallest possible ending element for a subsequence of length left. - // So update that value in tails. - if (left < tails.length) { - tails[left] = num; - } - // Otherwise left === tails.length, which means we are adding a new element to the end of tails. This number is the - // smallest possible ending element for a subsequence of length left + 1. - else { - tails.push(num); - } - } - - // The length of tails is the length of the longest increasing subsequence. - return tails.length; -} diff --git a/src/binary-search/median-of-two-sorted-arrays.ts b/src/binary-search/median-of-two-sorted-arrays.ts deleted file mode 100644 index 9de2355..0000000 --- a/src/binary-search/median-of-two-sorted-arrays.ts +++ /dev/null @@ -1,120 +0,0 @@ -// DIFFICULTY: HARD -// -// Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays. -// -// The overall run time complexity should be O(log (m+n)). -// -// See {@link https://leetcode.com/problems/median-of-two-sorted-arrays/} -export { findMedianSortedArrays }; - -// SOLUTION: -// -// This is not a reasonable question to ask in an interview. It's quite algorithmically complex. -// -// The naive way to solve this problem is to simply merge the two arrays and then take the midpoint. This is not what -// the problem asks for. -// -// To more efficiently find the median, imagine that the merged array's median partitions the merged array into two -// parts; a left part and a right part. The left part's partition will have elements that are all less than the -// median, and the right part's partition will have elements that are all larger than the median. -// -// Our goal now is to find four partitions; two (possibly different sized) left partitions from each array such that -// the values in those partitions are all less than the median. These two left partitions, if merged, would be the -// same as the left partition in the fully merged array. Likewise, the two (possibly different sized) right -// partitions, when merged, would be the right partition of the merged array. -// -// Note that we know that the left partition in the merged array is going to have h=(m+n)/2 elements, where m and n -// are the lengths of the two arrays (h representing half of the array elements in the merged array). We need to take -// a guess at where to partition on the smaller array, so let's say that the size of that partition is s=m/2. Then -// the size of the left partition for the other array must be h-s. -// -// Using the values of s and h-s, we can check if we've hit upon properly sized partitions that give us the median. -// If so, we can simply return it. If we didn't hit upon the properly sized partitions, then we'll have to update our -// guess for s and try again. -// -// How do we try again? Well, we can simulate concatenating the left partitions and right partitions to find out -// where we goofed for finding size s. For the smaller array, if we found s by setting the left pointer to 0, and -// the right pointer to m/2, then we'll have to use a modified version of binary search to update the pointers: -// -// - If the value at m=(left+right)/2 was too small, set left=m+1 and set right=m. -// - If the value at m=(left+right)/2 was too big, set left=0 and set right=m/2. -// -// Continue updating the left and right pointers until we have found the median. -function findMedianSortedArrays(xs: number[], ys: number[]) { - // Because we want to operate on the smaller array, let's find out which one is smaller and set xs equal to it. - if (xs.length > ys.length) { - [xs, ys] = [ys, xs]; - } - - // Again, we're using m and n as the array sizes, and m represents our smaller array size. - const m = xs.length; - const n = ys.length; - - // Note that this is a variation of the standard binary search algorithm. - let left = 0; - let right = m; - while (left <= right) { - // Compute the partition midpoints for both arrays. - // - // Imagine we have m=3 and n=3. Then we want p1 to be at (m+n)/2=1. The size of the "merged" array would be - // m+n=6. The midpoint of the other array, including the median, would be at (m+n)/2-3=3. - // - // However, for a "merged" array of odd size, say m=3, and n=4, then m+n=7. And if we use (m+n)/2-3=3, that - // would not include the median at index 4. So we should use (m+n+1)/2 instead. - // - // Using (m+n+1)/2 does not affect the calculation for an even sized "merged" array because the floor function - // removes the decimal. - const p1 = Math.floor((left + right) / 2); // Note that p1 needs to be calculated using left/right. - const p2 = Math.floor((m + n + 1) / 2) - p1; // Note that p2 needs to be calculated based on total size. - - // Now we imagine that the two left partitions are merged, and the two right partitions are merged. If we've - // chosen the partition points correctly, then the last value of both left partitions should be less than the - // head values of both right partitons. - // - // Here's an example partition of two arrays, where p1=2 and p2=3. - // - // xs = [1,2 | 3,3] with lmax1=2 and rmin1=3 (lmax1 is at p1-1 due to zero indexing). - // ys = [1,2,3 | 4,5,5] with lmax2=3 and rmin2=4 (lmax2 is at p2-1 due to zero indexing). - // - // In this case, we want to compare the rightmost values of xs with the opposite leftmost value of ys, and vice - // versa. - // - // Note that if p1 or p2 are zero, that means that the partitioning leaves no elements in the partition, so - // those min/max values should be replaced by -Infinity or +Infinity. - // - // Consider the following cases: - // - // xs = [] with lmax1=-Infinity because there is no value, and everything is >lmax1. - // xs = [1,2,3|] with rmax1=+Infinity because there is no value, and everything is rmin2) { - right = p1 - 1; - } - - // Here the xs side left partition's last value is too small, so we have to change the left pointer. At the next - // iteration we will do binary search where the left pointer has been set to the element after p1. - else { - left = p1 + 1; - } - } - - throw new Error('arrays are not sorted'); -} diff --git a/src/binary-search/search-in-rotated-sorted-array.ts b/src/binary-search/search-in-rotated-sorted-array.ts deleted file mode 100644 index 95c8039..0000000 --- a/src/binary-search/search-in-rotated-sorted-array.ts +++ /dev/null @@ -1,70 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// There is an integer array nums sorted in ascending order (with distinct values). -// -// Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) -// such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (0-indexed). -// For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2]. -// -// Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, -// or -1 if it is not in nums. -// -// You must write an algorithm with O(log n) runtime complexity. -// -// See {@link https://leetcode.com/problems/search-in-rotated-sorted-array/} -export { search }; - -// SOLUTION: -// -// You can still use binary search for this; you just have to recognize when you have looped around in the array. -// This uses the standard binary search algorithm, but with recursion. -// -// The standard approach is used because we are looking for an exact match for an element in the array, and return -1 -// if the element is not found. -function search(xs: number[], target: number): number { - function searchInternal(ys: number[], start: number, end: number, t: number) { - const mid = Math.floor((end + start) / 2); - if (ys[mid] === t) { - return mid; - } - - // If we've looped back around ourselves, that means the number wasn't found, and we can stop. - if (start > end) { - return -1; - } - - // If the left portion is sorted, and our target is within that portion, update the start/end indexes to be - // strictly within the left side of the pivot, and perform binary search there. - // - // Otherwise, it is in the right portion of the array, which contains a rotation. Call this function again, - // with an updated restriction on the sub array to search. - const isLeftSorted = ys[start] < ys[mid]; - const isWithinLeftPortion = t >= ys[start] && t <= ys[mid - 1]; - if (isLeftSorted && isWithinLeftPortion) { - return searchInternal(ys, start, mid - 1, t); - } - - if (isLeftSorted && !isWithinLeftPortion) { - return searchInternal(ys, mid + 1, end, t); - } - - // If the right portion is sorted, and our target is within that portion, update the start/end indexes to be - // strictly within the right side of the pivot, and perform binary search there. - // - // Otherwise, it is in the left portion of the array, which contains a rotation. Call this function again, - // with an updated restriction on the sub array to search. - const isRightSorted = !isLeftSorted; - const isWithinRightPortion = t >= ys[mid + 1] && t <= ys[end]; - if (isRightSorted && isWithinRightPortion) { - return searchInternal(ys, mid + 1, end, t); - } - - if (isRightSorted && !isWithinRightPortion) { - return searchInternal(ys, start, mid - 1, t); - } - - return -1; - } - - return searchInternal(xs, 0, xs.length - 1, target); -} diff --git a/src/dynamic-programming/README.md b/src/dynamic-programming/README.md deleted file mode 100644 index 1658486..0000000 --- a/src/dynamic-programming/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Dynamic Programming - -## Common Phrases - -These phrases indicate dynamic programming might be useful: - -- 'count the number of ways' -- 'optimal strategy' -- 'maximize sum, path, value, or profit' -- 'knapsack style constraints' diff --git a/src/dynamic-programming/arithmetic-slices-ii-subsequences.ts b/src/dynamic-programming/arithmetic-slices-ii-subsequences.ts deleted file mode 100644 index a086b44..0000000 --- a/src/dynamic-programming/arithmetic-slices-ii-subsequences.ts +++ /dev/null @@ -1,78 +0,0 @@ -// DIFFICULTY: HARD -// -// Given an integer array nums, return the number of all the arithmetic subsequences of nums. -// -// A sequence of numbers is called arithmetic if it consists of at least three elements and if the difference between -// any two consecutive elements is the same. -// -// - For example, [1, 3, 5, 7, 9], [7, 7, 7, 7], and [3, -1, -5, -9] are arithmetic sequences. -// - For example, [1, 1, 2, 5, 7] is not an arithmetic sequence. -// -// A subsequence of an array is a sequence that can be formed by removing some elements (possibly none) of the array. -// -// - For example, [2,5,10] is a subsequence of [1,2,1,2,4,1,5,10]. -// -// The test cases are generated so that the answer fits in 32-bit integer. -// -// See {@link https://leetcode.com/problems/arithmetic-slices-ii-subsequence/} -export { numberOfArithmeticSlices }; - -// SOLUTION: -// -// To solve this we can use a dynamic programming approach to build up the number of subsequences at each index, for -// each subsequence difference. -// -// Note that we can have [2, 4, 6, 8], and there are subsequences [2, 4, 6, 8] (the entire array) and [2, 4, 6], -// which is part of the array. Also note that [7, 7, 7, 7] has a total of 16 subsequences because every subsequence -// is arithmetic (they can overlap). -function numberOfArithmeticSlices(nums: number[]): number { - // An arithmetic subsequence must have at least length 3, so if we have fewer, then there are no subsequences. - if (nums.length < 3) { - return 0; - } - - let result = 0; - - // A list of dictionaries keeping track of subsequences ending at index i. The keys in the dictionary represent - // the differences in the arithmetic subsequence, and the values represent the count with that difference. - type Difference = number; - type Count = number; - const subsequences: Array> = []; - for (let i = 0; i < nums.length; i++) { - subsequences.push(new Map()); - } - - // Since we are considering pairs of elements, start at 1 to ensure that there is at least another element before - // it. - for (let i = 1; i < nums.length; i++) { - // At each index j, every number at index i (prior to j) could be participating in a subsequence where the - // diference of the arithmetic subsequence is nums[j] - nums[i]. So for each j, examine every previous element - // at i to see if it contributes to some subsequence. - for (let j = 0; j < i; j++) { - // Calculate the difference between the two numbers, and note that the difference can be negative (and the - // numbers in the array can be negative as well). This means the subsequence can be increasing or decreasing; - // it does not matter. - // - // That is, we want to find and extend any arithmetic subsequences that nums[j] might be a part of with the - // difference of of nums[i] - nums[j]. - const difference = nums[i] - nums[j]; - - // Get count of existing subsequences at j with difference (nums[j - 1] === nums[j] - difference is true). - // We can extend these subsequences by adding nums[i]. - const cj = subsequences[j].get(difference) || 0; - - // Get count of existing subsequences at i with difference (nums[i - 1] === nums[i] - difference is true). - const ci = subsequences[i].get(difference) || 0; - - // Update the count of subsequences at j, which is the previous count at i, plus the previous count at j, and - // then 1 more to account for adding j to the subsequence (by going from nums[i] to nums[j]). - subsequences[i].set(difference, ci + cj + 1); - - // Add to the running total, excluding the pair (nums[i], nums[j]). If we just have this pair, it will NOT - // contribute to a 3 element arithmetic subsequence (because there are only 2 elements). - result += cj; - } - } - - return result; -} diff --git a/src/dynamic-programming/max-subarray.ts b/src/dynamic-programming/max-subarray.ts deleted file mode 100644 index 374347d..0000000 --- a/src/dynamic-programming/max-subarray.ts +++ /dev/null @@ -1,51 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums, find the subarray with the largest sum, and return its sum. -// -// See {@link https://leetcode.com/problems/maximum-subarray/} -export { maxSubArray }; - -// SOLUTION: -// -// Kadane's algorithm finds a solution in O(n) with O(1) extra space. -// -// The idea is to consider subarrays starting from the 0th, 1st, 2nd, etc element. However, it is not necessary to -// consider the subarrays starting from 0, then 1, then 2, etc. -// -// It is enough to note that if all elements are positive, you want the entire array. But if they are not all -// positive, then you want to only take elements until it's "not worth it". To determine what is "not worth it", -// consider that if you start from the 0th index and continue to take elements, but the subarray sum becomes negative -// when it was positive before. Then we know to abandon the entire subarray from 0 to that element. -// -// Generalizing, suppose we have a current subarray sum, and we add the next element. If adding that next element -// causes the new current sum to be *less* than just that element by itself, we should abandon that subarray and -// start over. -function maxSubArray(xs: number[]): number { - if (xs.length === 0) { - return 0; - } - - if (xs.length === 1) { - return xs[0]; - } - - // Start calculating the sum of the current subarray at i = 0. - let current = xs[0]; - let max = xs[0]; - for (let i = 1; i < xs.length; i++) { - const x = xs[i]; - - if (current + x > x) { - // If adding x makes our current sum larger, we'll take x and add it to our potential max subarray sum. - current += x; - } else { - // However, if adding x makes our current sum smaller, abandon the current subarray that was from where we - // started to i. We'll start counting the sum from a new subarray, starting at i instead. - current = x; - } - - max = Math.max(current, max); - } - - return max; -} diff --git a/src/dynamic-programming/word-break-i.ts b/src/dynamic-programming/word-break-i.ts deleted file mode 100644 index 34643fc..0000000 --- a/src/dynamic-programming/word-break-i.ts +++ /dev/null @@ -1,56 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated -// sequence of one or more dictionary words. -// -// Note that the same word in the dictionary may be reused multiple times in the segmentation. -// -// See {@link https://leetcode.com/problems/word-break/} -export { wordBreak }; - -// SOLUTION: -// -// Note that this problem is significantly easier than word break ii because we don't need to do any backtracking to -// generate all of the possible sentences. -// -// Here we can just incrementally build up a solution using dynamic programming. -// -// We loop through the string and seeing if we can build a word out of (0, i). If we can, we'd like to continue on -// by seeing if we can build out a word from (0, j) where j > i. We'll keep going until we've exhausted the string -// or if we've run out of words to try. -// -// However, we'll have to try every possible starting word. Then every possible secondary word, and so on. That's -// why dynamic programming will be useful here. -function wordBreak(s: string, wordDict: string[]): boolean { - const set = new Set(wordDict); - - // We'll use a map to indicate if a slice (0,i) can make a sentence. This can just be an array, because the first - // index is always going to be 0. However, we make a map here because it's a little clearer what we are doing. - const map = new Map(); - - // The slice (0,0) is always going to form a sentence; the base sentence. - map.set('[0,0)', true); - - // We want to see if we can successfully parse a sentence from [0,s.length), but to do so, we'll incrementally build - // up whether or not we can parse a sentence from [0,j) for all j <= s.length. - // - // Yes we want j <= s.length because slice is exclusive on the last index. - for (let i = 0; i < s.length; i++) { - // We want j <= s.length here because slice is exclusive on the last index [i,j). - for (let j = i + 1; j <= s.length; j++) { - const word = s.slice(i, j); - if (!set.has(word)) { - continue; - } - - // If the slice (0,i) formed a sentence, then (0,j) must form a sentence because (i,j) is a word. For example, - // if (0,i) was 'hello' and (i,j) was 'there', then (0,j) slice of 'hellothere' is a sentence - if (map.has(`[0,${i})`)) { - map.set(`[0,${j})`, true); - } - } - } - - // If we've formed a sentence using all the characters, we've got a winner here. - return map.has(`[0,${s.length})`); -} diff --git a/src/graph/add-edges-to-make-degrees-even.ts b/src/graph/add-edges-to-make-degrees-even.ts deleted file mode 100644 index 5e58715..0000000 --- a/src/graph/add-edges-to-make-degrees-even.ts +++ /dev/null @@ -1,131 +0,0 @@ -// DIFFICULTY: HARD -// -// There is an undirected graph consisting of n nodes numbered from 1 to n. You are given the integer n and a 2D array -// edges where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi. The graph can be disconnected. -// -// You can add at most two additional edges (possibly none) to this graph so that there are no repeated edges and no self-loops. -// -// Return true if it is possible to make the degree of each node in the graph even, otherwise return false. -// -// The degree of a node is the number of edges connected to it. -// -// See {@link https://leetcode.com/problems/add-edges-to-make-degrees-of-all-nodes-even/} -export { isPossible }; - -// SOLUTION: -// -// When you add an edge between two nodes, you change the degree of those two nodes. We don't need to modify edges -// for any even degree nodes; they are already good. Instead we want to find nodes with an odd degree. -// -// If we join any two odd degree nodes with an edge, both of them will become even degree nodes. Since we can only -// add at most two edges, this can only be possible if we have 0, 2, or 4 nodes with an odd degree. -function isPossible(n: number, edges: number[][]): boolean { - type Node = number; - type Degree = number; - - // Let's define an easy way to keep track of edges, because later, we may want to add edges to our graph without - // adding duplicate edges. Typescript sets can store arrays, [1, 2] is the same as [2, 1] in an undirected graph - // so it's easier to just have a canonical string representation of an edge stored instead. - const set = new Set(); - function hasEdge(edge: number[]) { - const [a, b] = edge; - const value = a < b ? `${a},${b}` : `${b},${a}`; - return set.has(value); - } - - function addEdge(edge: number[]) { - const [a, b] = edge; - const value = a < b ? `${a},${b}` : `${b},${a}`; - set.add(value); - } - - // Find all the nodes with an odd degree. - const degreeMap = new Map(); - for (const edge of edges) { - const [u, v] = edge; - if (!degreeMap.has(u)) { - degreeMap.set(u, 0); - } - if (!degreeMap.has(v)) { - degreeMap.set(v, 0); - } - - degreeMap.set(u, degreeMap.get(u)! + 1); - degreeMap.set(v, degreeMap.get(v)! + 1); - addEdge([u, v]); - } - - // Note that the nodes are numbered 1 through n inclusive. - const oddNodes = []; - for (let i = 1; i <= n; i++) { - const degrees = degreeMap.get(i) ?? 0; - if (degrees % 2 === 1) { - oddNodes.push(i); - } - } - - // Only 0, 2, or 4 odd degree node graphs can be have edges added to create a valid all even degree graph. - if (oddNodes.length === 0) { - return true; - } - - // If we have 2 odd degree nodes, we have to check if we can create an all even degree graph by doing one of the - // following: - // - // 1. Add an edge between the two nodes, as long as it's not a duplicate. - // 2. Add two edges connecting the nodes, with another node in between. - if (oddNodes.length === 2) { - const [a, b] = oddNodes; - // Check if we can create an all even degree graph by connecting the nodes directly. - if (!hasEdge([a, b])) { - return true; - } - - // Check if we can create an all even degree graph by connecting the nodes through a different node. Note that - // the nodes are numbered 1 through n inclusive. - for (let c = 1; c <= n; c++) { - if (c === a || c === b) { - continue; - } - - if (!hasEdge([a, c]) && !hasEdge([c, b])) { - return true; - } - } - - return false; - } - - // If we have 4 odd degree nodes, we can check if we can create an all even degree graph by connecting the nodes - // together. The nodes must be connected directly though; we can't connect them through some unrelated node as that - // requires two edges already, leaving the other two odd degree nodes stranded. - if (oddNodes.length === 4) { - const [a, b, c, d] = oddNodes; - - // With 4 nodes, we can only connect them in 3 distinct ways. - const pairs = [ - [ - [a, b], - [c, d] - ], - [ - [a, c], - [b, d] - ], - [ - [a, d], - [c, b] - ] - ]; - for (const pair of pairs) { - const [first, second] = pair; - if (!hasEdge(first) && !hasEdge(second)) { - return true; - } - } - - return false; - } - - return false; -} diff --git a/src/graph/bus-routes.ts b/src/graph/bus-routes.ts deleted file mode 100644 index c4da6c5..0000000 --- a/src/graph/bus-routes.ts +++ /dev/null @@ -1,85 +0,0 @@ -// DIFFICULTY: HARD -// -// You are given an array routes representing bus routes where routes[i] is a bus route that the ith bus repeats -// forever. -// -// For example, if routes[0] = [1, 5, 7], this means that the 0th bus travels in the sequence: -// -// 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> ... -// -// ...forever. -// -// You will start at the bus stop source (You are not on any bus initially), and you want to go to the bus stop target. -// You can travel between bus stops by buses only. -// -// Return the least number of buses you must take to travel from source to target. Return -1 if it is not possible. -// -// See {@link https://leetcode.com/problems/bus-routes/} -export { numBusesToDestination }; - -// SOLUTION: -// -// This seems to be just a BFS problem. The fact that buses repeat forever doesn't seem to be relevant because there -// is no cost associated with waiting at a bus stop until the bus arrives; the only thing we are minimizing is the -// number of buses we want to take. -// -// Building a graph and then running BFS on it naively seems to exceed execution time so we'll have to do something -// more clever and dirty. -function numBusesToDestination(routes: number[][], source: number, target: number): number { - type Stop = number; - - if (source === target) { - return 0; - } - - // Once no more stops are added, break out of the loop. - let added = true; - - // The number of buses we've taken so far. - let result = 0; - - // Run a BFS from the source stop. We'll iterate through the routes and add stops to the visited set until we can - // no longer do so. If we haven't found our target stop by then we'll return -1. - const visited = new Set(); - visited.add(source); - while (added) { - // These are all routes that are reachable from currently visited stops, represented as individual stops in the - // route. - const temp: Stop[] = []; - added = false; - result++; - - for (let i = 0; i < routes.length; i++) { - const route = routes[i]; - - for (let j = 0; j < route.length; j++) { - const stop = route[j]; - - if (visited.has(stop)) { - // Collect all stops in the route associated with the current stop. - temp.push(...route); - - // We just added all the stops in the route, and we are about to mark them all as visited. Clear the routes - // array at i because we do not have to visit them again, so there's no need to iterate through them - // anymore. - routes[i] = []; - - // We've found new stops to visit; break out of the (inner) loop and move onto the next route. We'll note - // that stops have been visited, so we should continue the outer while loop. - added = true; - break; - } - } - } - - // Mark all the stops we've seen in this iteration as visited. - temp.forEach(stop => visited.add(stop)); - - // Immediately return if we have ended up visiting our target destination. - if (visited.has(target)) { - return result; - } - } - - return -1; -} diff --git a/src/graph/nested-list-weighted-sum-ii.ts b/src/graph/nested-list-weighted-sum-ii.ts deleted file mode 100644 index 54d284c..0000000 --- a/src/graph/nested-list-weighted-sum-ii.ts +++ /dev/null @@ -1,75 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given a nested list of integers nestedList. Each element is either an integer or a list whose elements may -// also be integers or other lists. -// -// The depth of an integer is the number of lists that it is inside of. For example, the nested list [1,[2,2],[[3],2],1] -// has each integer's value set to its depth. Let maxDepth be the maximum depth of any integer. -// -// The weight of an integer is maxDepth - (the depth of the integer) + 1. -// -// Return the sum of each integer in nestedList multiplied by its weight. -// -// See {@link https://leetcode.com/problems/nested-list-weight-sum-ii/} -import { NestedInteger } from '../linked-list/common/nested-integer'; -export { depthSumInverse }; - -// SOLUTION: -// -// Unlike the previous problem where we just multiply the integer by its depth, we need to multiply by the weight of the -// of the integer. Since we won't know the weight until after we've traversed the entire list, we'll need to keep track -// of depth to integers, then calculate the weight and sum up at the end. -// -// However, like before, this is still going to be a DFS search, except we don't need to keep track of visited nodes. -// -// COMPLEXITY: -// -// Similar to before, the time complexity is O(n) where n is the number of total integers and lists in the data -// structure. -// -// Similar to before, the space complexity is O(d) where d is the depth of the nested list. -function depthSumInverse(nestedList: NestedInteger[]): number { - type Integer = number; - type Depth = number; - - // Define a map of depth to a list of integers; each integer at that depth will have the same weight. We'll compute - // the max depth as we go. - const map = new Map(); - let max = 0; - - // Build up the map of depth -> ints recursively. - function compute(x: NestedInteger, depth: number): void { - if (depth > max) { - max = depth; - } - - if (x.isInteger()) { - const value = x.getInteger() ?? 0; - const list = map.get(depth) ?? []; - list.push(value); - map.set(depth, list); - return; - } - - const ys = x.getList(); - for (let i = 0; i < ys.length; i++) { - const y = ys[i]; - compute(y, depth + 1); - } - } - - for (let i = 0; i < nestedList.length; i++) { - const z = nestedList[i]; - compute(z, 0); - } - - // Once we have the map, just multiply each list of integers at each depth by the computed weight and sum. - let result = 0; - map.forEach((values, depth) => { - const weight = max - depth + 1; - const partial = values.reduce((x, y) => x + y) * weight; - result += partial; - }); - - return result; -} diff --git a/src/graph/nested-list-weighted-sum.ts b/src/graph/nested-list-weighted-sum.ts deleted file mode 100644 index 962799e..0000000 --- a/src/graph/nested-list-weighted-sum.ts +++ /dev/null @@ -1,52 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given a nested list of integers nestedList. Each element is either an integer or a list whose elements may -// also be integers or other lists. -// -// The depth of an integer is the number of lists that it is inside of. For example, the nested list [1,[2,2],[[3],2],1] -// has each integer's value set to its depth. -// -// Return the sum of each integer in nestedList multiplied by its depth. -// -// See {@link https://leetcode.com/problems/nested-list-weight-sum} -import { NestedInteger } from '../linked-list/common/nested-integer'; -export { depthSum }; - -// SOLUTION: -// -// This is essentially a depth first search problem. However, because it a nested list, there's no need to keep track -// of visited elements. Instead we'll just keep track of the depth as we traverse the list. -// -// COMPLEXITY: -// -// Time complexity is O(n) where n is the number of total integers and lists in the data structure. -// -// Space complexity is O(d) where d is the depth of the nested list, since we need a stack frame for each level of -// recursion. -function depthSum(nestedList: NestedInteger[]): number { - function compute(x: NestedInteger, depth: number): number { - // If we've encountered an integer, compute the weighted value according to the depth. - if (x.isInteger()) { - const value = x.getInteger() ?? 0; - const weight = depth; - return value * weight; - } - - // Otherwise, we have a list, so we'll recursively compute the weighted sum of the list. - let result = 0; - const ys = x.getList(); - for (let i = 0; i < ys.length; i++) { - const y = ys[i]; - result += compute(y, depth + 1); - } - - return result; - } - - const x = new NestedInteger(); - for (let i = 0; i < nestedList.length; i++) { - x.add(nestedList[i]); - } - - return compute(x, 0); -} diff --git a/src/graph/number-of-islands.ts b/src/graph/number-of-islands.ts deleted file mode 100644 index 8f0cc11..0000000 --- a/src/graph/number-of-islands.ts +++ /dev/null @@ -1,61 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of -// islands. -// -// An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may -// assume all four edges of the grid are all surrounded by water. -// -// See {@link https://leetcode.com/problems/number-of-islands/} -export { numIslands }; - -// SOLUTION: -// -// We can execute DFS from every land mass to find connected land masses. Each connected land mass constitutes 1 -// island. -// -// We can either store the coordinates (i, j) in a set to avoid visiting the same cell twice, or we can simply write -// to the cell '0' for a visited land mass. The latter will also avoid visiting the same cell twice. We do the -// latter because that's what most interviewers want to see. -function numIslands(grid: string[][]) { - let count = 0; - - // Use DFS to find all connected land masses. Avoid doing DFS calls to any coordinates that are out of bounds, and - // mark cells as visited by writing '0' (water) to them. - function dfs(m: string[][], i: number, j: number) { - if (m[i][j] === '0') { - return; - } - - m[i][j] = '0'; - - if (j + 1 < m[i].length) { - dfs(m, i, j + 1); - } - - if (j - 1 >= 0) { - dfs(m, i, j - 1); - } - - if (i - 1 >= 0) { - dfs(m, i - 1, j); - } - - if (i + 1 < m.length) { - dfs(m, i + 1, j); - } - } - - for (let i = 0; i < grid.length; i++) { - for (let j = 0; j < grid[i].length; j++) { - if (grid[i][j] === '0') { - continue; - } - - dfs(grid, i, j); - count++; - } - } - - return count; -} diff --git a/src/graph/parallel-job-scheduling.ts b/src/graph/parallel-job-scheduling.ts deleted file mode 100644 index 1503a07..0000000 --- a/src/graph/parallel-job-scheduling.ts +++ /dev/null @@ -1,77 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a list tasks and their dependencies ['A:B,C', 'B:D,C,E', 'F:G'], return a list of parallelizable tasks in -// order. For example, in this situation [(D, C, E, F), (B, G), (A)] would be a valid order because all parallelizable -// tasks are grouped together. -// -// NOTE: This is not a LeetCode question but was asked by Cruise. -// -// See {@link https://leetcode.com/discuss/interview-question/353830/google-phone-screen-parallel-job-scheduling/} -export { parallelize }; - -// SOLUTION: -function parallelize(jobs: string[]): string[][] { - const graph = new Map(); - const reversed = new Map(); - for (const job of jobs) { - const parts = job.split(':'); - const node = parts[0]; - const children = parts[1].split(','); - - // Build up an adjacency list graph. - graph.set(node, children); - - // We will also want a reverse mapping of child -> parent; this will become useful later. - for (const child of children) { - if (!graph.has(child)) { - graph.set(child, []); - } - - const parents = reversed.get(child) ?? []; - parents.push(node); - reversed.set(child, parents); - } - } - - const result: string[][] = []; - - // While we have nodes without dependencies, let's prune them from the graph. - const frontier = new Set(); - for (const [key, value] of graph) { - if (value.length === 0) { - frontier.add(key); - } - } - - while (frontier.size > 0) { - const subresult = [...frontier]; - frontier.clear(); - - for (const node of subresult) { - graph.delete(node); - - // A deletion could have caused a different node to become a leaf. To find them, let's get the reverse mapping - // of node to parents and check each parent if that parent became a leaf. - const parents = reversed.get(node) ?? []; - for (const parent of parents) { - if (!graph.has(parent)) { - continue; - } - - // Remove the node from the parent adjacency lists. - let children = graph.get(parent) ?? []; - children = children.filter(u => u !== node); - graph.set(parent, children); - - // If any list became empty, it is now prunable. - if (children.length === 0) { - frontier.add(parent); - } - } - } - - result.push(subresult); - } - - return result; -} diff --git a/src/graph/shortest-path-in-binary-matrix.ts b/src/graph/shortest-path-in-binary-matrix.ts deleted file mode 100644 index c21b10c..0000000 --- a/src/graph/shortest-path-in-binary-matrix.ts +++ /dev/null @@ -1,105 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an n x n binary matrix grid, return the length of the shortest clear path in the matrix. If there is no clear -// path, return -1. -// -// A clear path in a binary matrix is a path from the top-left cell (i.e., (0, 0)) to the bottom-right cell -// (i.e., (n - 1, n - 1)) such that: -// -// - All the visited cells of the path are 0. -// - All the adjacent cells of the path are 8-directionally connected (i.e., they are different and they share an edge -// or a corner). -// - The length of a clear path is the number of visited cells of this path. -// -// See {@link https://leetcode.com/problems/shortest-path-in-binary-matrix/} -export { shortestPathBinaryMatrix }; - -// SOLUTION: -// -// Can be solved using BFS. Just start from the top left cell and explore adjacent cells that have 0 value. -// -// Note the following: -// -// - We can explore all 8 directions, so we can go backwards. -// - We might not find a path. -// - The first cell might not even be visitable. -// - With BFS the first path that reaches the end is guaranteed to be the shortest. -// - If we had used DFS, we'd need to explicitly keep track of the shortest path because the first path to finish may -// not be the shortest. -// -// COMPLEXITY: -// -// Each cell is visited at most once. On each visited a max of 8 directions are considered. With there being n rows -// and n columns, the time complexity is O(n^2). -// -// The space complexity is O(n^2) to store the visited cells and the queue. -function shortestPathBinaryMatrix(grid: number[][]): number { - // The problem says that you MUST start at the top left, so if the top left cell is 1, you have nowhere to go. - if (grid[0][0] === 1) { - return -1; - } - - const visited = new Set(); - const queue = [ - { - position: [0, 0], - length: 1 - } - ]; - while (queue.length > 0) { - const cell = queue.shift()!; - const [x, y] = cell.position; - - // First check if this cell has been visited, and if it has, simply skip it. Store the cell as a string in a set, - // since we can't store the object directly. - const key = `[${x},${y}]`; - if (visited.has(key)) { - continue; - } - - // Note that if we're at the very last cell, we can return it immediately. Because we are using BFS, the very first - // path that reaches the end is guaranteed to be the shortest. - // - // We don't have to store the path or otherwise distinguish between paths because all shortest paths have the same - // length. - if (x === grid.length - 1 && y === grid[0].length - 1) { - return cell.length; - } - - // Note that we can actually move backwards (and we might be required to) since we can explore all 8 directions. - // Generate the coordinates of the 8 directions, then filter out the ones that are out of bounds. - const coordinates = [ - [x - 1, y], - [x, y - 1], - [x - 1, y - 1], - [x + 1, y], - [x, y + 1], - [x + 1, y + 1], - [x - 1, y + 1], - [x + 1, y - 1] - ]; - const frontier = coordinates - .filter(([x, y]) => { - return ( - x >= 0 && - x < grid.length && // Check the x bounds. - y >= 0 && - y < grid[0].length && // Check the y bounds; note that it's n x n so grid.length works too. - grid[x][y] === 0 - ); // Check if the cell can actually be moved into. - }) - .map(p => { - return { - position: p, - length: cell.length + 1 - }; - }); - - // Update the visited set and push the frontier cells onto the queue. - visited.add(key); - queue.push(...frontier); - } - - // If we've exhausted all paths without reaching the end, that means we couldn't do it. - return -1; -} diff --git a/src/graph/smallest-greater-multiple-made-of-two-digits.ts b/src/graph/smallest-greater-multiple-made-of-two-digits.ts deleted file mode 100644 index 3770dab..0000000 --- a/src/graph/smallest-greater-multiple-made-of-two-digits.ts +++ /dev/null @@ -1,74 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given three integers, k, digit1, and digit2, you want to find the smallest integer that is: -// -// - Larger than k, -// - A multiple of k, and -// - Comprised of only the digits digit1 and/or digit2. -// -// Return the smallest such integer. If no such integer exists or the integer exceeds the limit of a signed 32-bit -// integer (2^31 - 1), return -1. -// -// See {@link https://leetcode.com/problems/smallest-greater-multiple-made-of-two-digits/} -export { findInteger }; - -// SOLUTION: -// -// In order to find our target number, we start with the first digit and generate all possible 2 digit numbers. Then -// from there we generate all possible 3 digit numbers, and so on. We also go back to checking the second digit, -// generating 2 digit, 3 digit, etc numbers. -// -// This can be done via a graph search algorithm. However, since we want to find the smallest such number, we should -// use BFS instead of DFS. -function findInteger(k: number, digit1: number, digit2: number): number { - const queue: string[] = []; - const seen = new Set(); - - // We always want to start generating numbers from the smaller digit first, so let's rearrange them so that the - // smaller digit is digit1. - if (digit2 < digit1) { - [digit1, digit2] = [digit2, digit1]; - } - - // For the first digits, do not push a 0, because generating a number starting at 0 is the same as generating a - // number using the rest of the digits. - if (digit1 !== 0) { - queue.push(`${digit1}`); - seen.add(`${digit1}`); - } - - if (digit2 !== 0 && digit1 !== digit2) { - queue.push(`${digit2}`); - seen.add(`${digit2}`); - } - - const max = 2 ** 31 - 1; - while (queue.length > 0) { - const s = queue.shift()!; - const value = Number.parseInt(s, 10 /* radix */); - if (value > max) { - continue; - } - - // Check if this number is both larger than k and a multiple of k; if so we have reached our target. - if (value > k && value % k === 0) { - return value; - } - - // Otherwise, mark this number as visited and use the digits to construct our frontier nodes. - const next1 = `${s}${digit1}`; - if (!seen.has(next1)) { - queue.push(next1); - seen.add(next1); - } - - // We know that by adding in this order, the smaller number gets added first (since digit1 < digit2). - const next2 = `${s}${digit2}`; - if (!seen.has(next2)) { - queue.push(next2); - seen.add(next2); - } - } - - return -1; -} diff --git a/src/graph/word-search-i.ts b/src/graph/word-search-i.ts deleted file mode 100644 index d8f7aad..0000000 --- a/src/graph/word-search-i.ts +++ /dev/null @@ -1,89 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an m x n grid of characters board and a string word, return true if word exists in the grid. -// -// The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or -// vertically neighboring. The same letter cell may not be used more than once. -// -// See {@link https://leetcode.com/problems/word-search/} -export { exist }; - -// SOLUTION: -// -// This is just DFS; no need to build a prefix trie, in comparison to word search ii. BFS isn't going to work as well -// because BFS will generate all possible words incrementally, whereas we want to just stop if we've found our word. -function exist(board: string[][], word: string): boolean { - if (word.length === 0) { - return false; - } - - let found = false; - - // Here, as we do DFS, we are only going to go down paths that match the next character. So while we could build - // up a current: string as we go along, we do not need to. We just need to select DFS paths that correspond to - // the correct letter, and maintain the index of the letter that we are looking at. - function dfs(row: number, column: number, index: number) { - if (found) { - return; - } - - // Only continue down this path if the row is within bounds. - if (row < 0 || row >= board.length) { - return; - } - - // Only continue down this path if the column is within bounds. - if (column < 0 || column >= board[row].length) { - return; - } - - // Only continue down this path if we've not visited this cell before. - if (board[row][column] === '#') { - return; - } - - // Only continue down this path if our current string has fewer characters than our target. - if (index === word.length) { - return; - } - - // Only continue down this path if the current character is one that we want. - const c = board[row][column]; - const want = word[index]; - if (c !== want) { - return; - } - - // If we have reached the last index, check if we've found our target. - if (index === word.length - 1) { - found = true; - return; - } - - // Save this row/column's value so we can mark it as visited; we'll use a sentinel value to do so. - board[row][column] = '#'; - - dfs(row, column - 1, index + 1); - dfs(row, column + 1, index + 1); - dfs(row - 1, column, index + 1); - dfs(row + 1, column, index + 1); - - board[row][column] = c; - } - - for (let i = 0; i < board.length; i++) { - for (let j = 0; j < board[i].length; j++) { - // Only start a DFS search if the word begins with the character we are looking at. - if (board[i][j] === word[0]) { - dfs(i, j, 0); - } - - // If we've found the target, return immediately. - if (found) { - return true; - } - } - } - - return false; -} diff --git a/src/graph/word-search-ii.ts b/src/graph/word-search-ii.ts deleted file mode 100644 index ccfcdd4..0000000 --- a/src/graph/word-search-ii.ts +++ /dev/null @@ -1,125 +0,0 @@ -// DIFFICULTY: HARD -// -// Given an m x n board of characters and a list of strings words, return all words on the board. -// -// Each word must be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or -// vertically neighboring. The same letter cell may not be used more than once in a word. -// -// See {@link https://leetcode.com/problems/word-search-ii/} -export { findWords }; - -// SOLUTION: -// -// This problem looks like it can be solved with a prefix trie. -// -// 1. Construct a prefix trie from the list of words. -// 2. Run DFS from each board cell, choosing to continue the search if the sequence seen so far is a valid prefix in -// the trie. -// 3. Collect valid words from each DFS and return the result. -// -// Easy peasy. -function findWords(board: string[][], words: string[]): string[] { - // Construct the prefix trie for efficient lookup. - const root = new TrieNode(); - words.forEach(word => { - root.add(word); - }); - - // Define a DFS function to run over each cell. We could potentially make the DFS more efficient by creating the - // visited matrix once and then mutating it over each DFS visitation. - const result = new Set(); - - function createdVisitedMatrix(matrix: string[][]) { - const visited: boolean[][] = Array(matrix.length); - for (let i = 0; i < matrix.length; i++) { - visited[i] = Array(matrix[i].length).fill(false); - } - return visited; - } - - function dfs(node: TrieNode, visited: boolean[][], word: string, row: number, column: number) { - // If we've reached the end of a word, do not return right away; we might be able to reach a longer word if we - // keep going. Record the one we've seen so far though. - if (node.isTerminated) { - result.add(word); - } - - // The frontier nodes could be any direction up, down, left, or right, but not diagonal. - const frontier = [ - [row + 1, column], - [row - 1, column], - [row, column + 1], - [row, column - 1] - ]; - for (const [x, y] of frontier) { - // Do not consider this frontier cells that are out of bounds. - if (x < 0 || x >= board.length) { - continue; - } - - // Do not consider this frontier cells that are out of bounds. - if (y < 0 || y >= board[x].length) { - continue; - } - - // Do not consider visited cells. - if (visited[x][y]) { - continue; - } - - // Do not consider cells that do not form a word. - const c = board[x][y]; - if (!node.children.has(c)) { - continue; - } - - const next = node.children.get(c)!; - - // Mark this cell as visited, and continue the DFS search. After the search completes, we should unmark visited - // cells (unlike regular DFS) because a different direction may yield different words, so we'll want reconsider - // these previously visited cells. - visited[x][y] = true; - dfs(next, visited, word + c, x, y); - visited[x][y] = false; - } - } - - // Perform DFS on every single cell. - for (let i = 0; i < board.length; i++) { - for (let j = 0; j < board[i].length; j++) { - const c = board[i][j]; - if (!root.children.has(c)) { - continue; - } - - const visited = createdVisitedMatrix(board); - const node = root.children.get(c)!; - - // Note that the DFS function won't mark the current cell as visited; we'll have to do that manually. - visited[i][j] = true; - dfs(node, visited, c, i, j); - } - } - - return [...result]; -} - -class TrieNode { - public children = new Map(); - - public isTerminated = false; - - public add(word: string) { - let node: TrieNode = this; - - for (let i = 0; i < word.length; i++) { - const c = word.charAt(i); - if (!node.children.has(c)) { - node.children.set(c, new TrieNode()); - } - node = node.children.get(c)!; - } - - node.isTerminated = true; - } -} diff --git a/src/heap/README.md b/src/heap/README.md deleted file mode 100644 index deb08f9..0000000 --- a/src/heap/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Heaps - -## Common Phrases - -These phrases indicate a heap might be useful: - -- 'k closest', 'k largest', 'k smallest' -- 'top k elements' -- 'max k elements', 'min k elements' -- 'priority order' -- 'sorted order with constraints' diff --git a/src/heap/common/binary-search-max-heap.ts b/src/heap/common/binary-search-max-heap.ts deleted file mode 100644 index 68fef87..0000000 --- a/src/heap/common/binary-search-max-heap.ts +++ /dev/null @@ -1,78 +0,0 @@ -export interface Element { - value: T; - priority: number; -} - -// When there isn't time to implement a real heap in an interview, and the @datastructures-js/priority-queue library -// isn't available, you can use binary search insert in place of a real heap. This will be O(1) for popping, but it -// will be O(n) for insert (even though the binary search is O(logn), we do have to resize the array at O(n)). -export class BinarySearchMaxHeap { - private readonly heap: Element[]; - - private readonly compare: (a: number, b: number) => number; - - constructor(compare?: (a: number, b: number) => number) { - this.heap = []; - this.compare = compare ?? ((a, b) => a - b); - } - - public enqueue(value: T, priority?: number): BinarySearchMaxHeap { - const element: Element = { - value, - priority: this.getPriority(value, priority) - }; - - let left = 0; - let right = this.heap.length; - while (left < right) { - const mid = Math.floor((left + right) / 2); - const a = this.heap[mid]; - const b = element; - // You can use < here for non-stable inserts, if you don't care. Switch to <= for stable inserts. - if (this.compare(a.priority, b.priority) < 0) { - left = mid + 1; - } else { - right = mid; - } - } - - this.heap.splice(left, 0, element); - return this; - } - - public dequeue(): T { - if (this.isEmpty()) { - throw new Error('heap is empty'); - } - - return this.heap.pop()!.value; - } - - public front(): T { - if (this.isEmpty()) { - throw new Error('heap is empty'); - } - - return this.heap[this.heap.length - 1].value; - } - - public size(): number { - return this.heap.length; - } - - public isEmpty(): boolean { - return this.heap.length === 0; - } - - private getPriority(value: T, priority?: number): number { - if (priority !== undefined) { - return priority; - } - - if (typeof value === 'number') { - return value; - } - - return 0; - } -} diff --git a/src/heap/common/simple-max-heap.ts b/src/heap/common/simple-max-heap.ts deleted file mode 100644 index d0b896a..0000000 --- a/src/heap/common/simple-max-heap.ts +++ /dev/null @@ -1,63 +0,0 @@ -export interface Element { - value: T; - priority: number; -} - -// When there isn't time to implement a real heap in an interview, and the @datastructures-js/priority-queue library -// isn't available, just insert and sort. This will be O(nlogn) on insert but it's quick and dirty. -export class SimpleMaxHeap { - private readonly heap: Element[]; - - private readonly compare: (a: number, b: number) => number; - - constructor(compare?: (a: number, b: number) => number) { - this.heap = []; - this.compare = compare ?? ((a, b) => a - b); - } - - public enqueue(value: T, priority?: number): SimpleMaxHeap { - const element: Element = { - value, - priority: this.getPriority(value, priority) - }; - this.heap.push(element); - this.heap.sort((a, b) => this.compare(a.priority, b.priority)); - return this; - } - - public dequeue(): T { - if (this.isEmpty()) { - throw new Error('heap is empty'); - } - - return this.heap.pop()!.value; - } - - public front(): T { - if (this.isEmpty()) { - throw new Error('heap is empty'); - } - - return this.heap[this.heap.length - 1].value; - } - - public size(): number { - return this.heap.length; - } - - public isEmpty(): boolean { - return this.heap.length === 0; - } - - private getPriority(value: T, priority?: number): number { - if (priority !== undefined) { - return priority; - } - - if (typeof value === 'number') { - return value; - } - - return 0; - } -} diff --git a/src/heap/furthest-building-you-can-reach.ts b/src/heap/furthest-building-you-can-reach.ts deleted file mode 100644 index 30dfe42..0000000 --- a/src/heap/furthest-building-you-can-reach.ts +++ /dev/null @@ -1,79 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer array heights representing the heights of buildings, some bricks, and some ladders. -// -// You start your journey from building 0 and move to the next building by possibly using bricks or ladders. -// -// While moving from building i to building i+1 (0-indexed), -// -// - If the current building's height is greater than or equal to the next building's height, you do not need a ladder -// or bricks. -// - If the current building's height is less than the next building's height, you can either use one ladder or -// (h[i+1] - h[i]) bricks. -// -// Return the furthest building index (0-indexed) you can reach if you use the given ladders and bricks optimally. -// -// See {@link https://leetcode.com/problems/furthest-building-you-can-reach/} -const { MinPriorityQueue } = require('@datastructures-js/priority-queue'); -export { furthestBuilding }; - -// SOLUTION: -// -// This cannot be solved with the sliding window technique; usually that technique involves finding a fixed size -// sub-string sub-array with constraints. Here we need to dynamically adjust our resource (brick/ladder) usage at -// each step, making a greedy algorithm better to solve the problem. -// -// COMPLEXITY: -// -// We iterate through n heights in O(n). Each iteration, we may push onto the heap, which is O(log(n)). This results in -// O(n * log(n)) time complexity. -function furthestBuilding(heights: number[], bricks: number, ladders: number): number { - // Use a heap/priority queue to store the number of jumps we need to make with either bricks or ladders. - const heap = new MinPriorityQueue(); - - for (let i = 0; i < heights.length - 1; i++) { - const delta = heights[i + 1] - heights[i]; - - // If the height is same level or lower, we can jump across with no penalty. - if (delta <= 0) { - continue; - } - - // If the height is higher by some amount, push it onto the heap and make a determination on whether we use a - // ladder or some bricks to scale the difference. - // - // We should use ladders on the biggest deltas, so if we greedily consume the smallest deltas from the heap, as - // long as we have enough ladders to cover the remaining length of the heap, we can continue. - heap.enqueue(delta); - - // As mentioned, the heap stores how many jumps that consume ladders or bricks at this point. Because ladders - // can represent any number of bricks, this means that we "buy" elements in the heap, or that the heap may have - // an ambient size. - // - // For example, normally each element pushed onto the heap must be popped off. However, if we have 3 ladders, - // the min heap is permitted to contain 3 elements that don't need to be popped off. These 3 elements are the - // biggest deltas, and we use ladders to traverse them. - // - // At this moment, we don't know which 3 elements will be the biggest, but we just treat 3 as the "floor" size of - // the heap and continue if we haven't met this floor. - if (ladders >= heap.size()) { - // Assume that this delta, at height i, will be claimed by a ladder. - continue; - } - - // If, at this point, we've run out of ladders, we'll need to pop off the smallest element and use bricks to cross - // the delta. This will cause the largest element to be different, but the ladder doesn't care how big the - // largest element is. - const smallest = heap.dequeue().element; - bricks -= smallest; - - // If we've run out of bricks, it's okay. But if we've gone negative into bricks, we can't cross this gap and - // we return i (since we can't get to i + 1). - if (bricks < 0) { - return i; - } - } - - // There's no building at heights.length; the last building is at heights.length - 1. - return heights.length - 1; -} diff --git a/src/heap/k-closest-points-to-origin.ts b/src/heap/k-closest-points-to-origin.ts deleted file mode 100644 index eb591ac..0000000 --- a/src/heap/k-closest-points-to-origin.ts +++ /dev/null @@ -1,56 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the -// k closest points to the origin (0, 0). -// -// The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x1 - x2)2 + (y1 - y2)2). -// -// You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). -// -// See {@link https://leetcode.com/problems/k-closest-points-to-origin/} -import { MaxPriorityQueue } from '@datastructures-js/priority-queue'; -export { kClosest }; - -// SOLUTION: -// -// The request for "k-closest" indicates you'll want a heap. Using a either a min heap or max heap will work. -// -// With a min heap, just jam all the points on the heap and then slice the first k elements. This requires storing all -// the points on the heap. -// -// With a max heap, you can jam points on the heap until you have k elements. Then, when you have a new point, compare -// it to the max element on the heap. If it's closer, pop off the max element and jam it on the heap; otherwise, ignore -// it. -// -// The max heap approach can be more efficient, so we'll go that route. -// -// COMPLEXITY: -// -// With a min heap, it will be O(n * log(n)) to insert all the points on the heap. -// -// With a max heap, if k is much smaller than n, it will be O(n * log(k)) to insert all the points on the heap. -function kClosest(points: number[][], k: number): number[][] { - // Calculates the distance to the origin. Technically, since we're only using the distance to do comparison, we - // don't need to take the square root since they are all going to compare the same square root or not. I'm leaving - // it here for correctness. - function distance(p: number[]) { - const [x, y] = p; - return Math.sqrt(x ** 2 + y ** 2); - } - - // The datastructures-js/priority-queue behavior depends on the comparator. Make sure you do (a, b) => b - a to get - // max heap behavior, or else you'll end up with a min heap. - const heap = new MaxPriorityQueue({ compare: (a: number[], b: number[]) => distance(b) - distance(a) }); - for (const p of points) { - heap.enqueue(p); - - // If we have more than k elements, remove the farthest point. - if (heap.size() > k) { - heap.dequeue(); - } - } - - // Why do we need this cast? Because of LeetCode, TypeScript, datastructures-js, and the reason. - const array = heap.toArray() as unknown; - return array as number[][]; -} diff --git a/src/heap/kth-largest-element.ts b/src/heap/kth-largest-element.ts deleted file mode 100644 index 33b3ce5..0000000 --- a/src/heap/kth-largest-element.ts +++ /dev/null @@ -1,60 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums and an integer k, return the kth largest element in the array. -// -// Note that it is the kth largest element in the sorted order, not the kth distinct element. -// -// Can you solve it without sorting? -// -// See {@link https://leetcode.com/problems/kth-largest-element-in-an-array/} -const { MinPriorityQueue } = require('@datastructures-js/priority-queue'); -export { findKthLargest }; - -// SOLUTION: -// -// You can do this with quick select, similar to quick sort. The idea is to eliminate half of the search space at each -// step. You pick a pivot element (using heuristics, or even randomly), then partition the array into two halves: left -// half has elements smaller than the pivot, right half has elements larger than the pivot. -// -// After the partition, you can check if the pivot is the (n - k)th element. If it is, you're done. If not, recurse -// into the left or right partitions depending on the pivot's position. -// -// That said, the leetcode questions seem to be crafted to ruin poor pivot index choices. -// -// The other way to do this is to use a heap. You can do this an efficient way or an inefficient way. The inefficient -// way involves jamming all the elements onto a max heap, then popping off k elements to get the kth largest element. -// -// The efficient way is to maintain a min heap and only ever allow k elements on the heap. This way, each time you pop -// an element off the heap, you're left with ever larger elements. Since the heap size is k, the kth largest element -// will be at the top of the heap. -// -// COMPLEXITY: -// -// The quick select method has an average time complexity of O(n), but a worst case time complexity of O(n^2), -// especially if you pick the wrong pivot each time. However, the space complexity is O(1). -// -// The min heap approach is O(n log k) time complexity, and O(k) space complexity. -// -// The latter offers you more consistent run times. -function findKthLargest(nums: number[], k: number): number { - // Use a min heap because we'll want to pop off the smallest element every time we reach the limit. - const heap = new MinPriorityQueue(); - - // Iterate through the numbers, and jam numbers on the heap until we have k elements. - for (const num of nums) { - heap.enqueue(num); - - // Now we have the k + 1 smallest elements we've seen so far. If we get a new element, pop from the - // heap, which will leave us with larger elements in the heap. - // - // At the end, we'll pop off all the smallest elements, leaving us with the top k largest elements. - if (heap.size() > k) { - heap.dequeue(); - } - } - - // This is a min heap, so the smallest element is at the front. However, there are (k - 1) larger elements in - // elsewhere in the heap. That makes the front element the kth largest element. - const result = heap.front().element; - return result; -} diff --git a/src/heap/max-stack.ts b/src/heap/max-stack.ts deleted file mode 100644 index 1caa1bd..0000000 --- a/src/heap/max-stack.ts +++ /dev/null @@ -1,174 +0,0 @@ -// DIFFICULTY: HARD -// -// Design a max stack data structure that supports the stack operations and supports finding the stack's maximum element. -// -// Implement the MaxStack class: -// -// - MaxStack() Initializes the stack object. -// - void push(int x) Pushes element x onto the stack. -// - int pop() Removes the element on top of the stack and returns it. -// - int top() Gets the element on the top of the stack without removing it. -// - int peekMax() Retrieves the maximum element in the stack without removing it. -// - int popMax() Retrieves the maximum element in the stack and removes it. If there is more than one maximum element, -// only remove the top-most one. -// -// You must come up with a solution that supports O(1) for each top call and O(logn) for each other call. -// -// See {@link https://leetcode.com/problems/max-stack/} -const { MaxPriorityQueue } = require('@datastructures-js/priority-queue'); -export { MaxStack }; - -// SOLUTION: -// -// We want a max priority queue, but we need to determine the max by comparing both a key and a value. This can -// only be done by supplying a custom comparator, so instead of using a MaxPriorityQueue, we'll use a PriorityQueue -// that takes a custom comparator. -interface StackNode { - key: number; - value: number; - previous: StackNode; - next: StackNode; -} - -class MaxStack { - // MaxPriorityQueue is exported as a value and not a type in version 5.4.0. - private readonly heap: InstanceType; - - private readonly compare: (a: StackNode, b: StackNode) => number; - - private readonly deleted: Set; - - private keys: number; - - private head: StackNode; - - private tail: StackNode; - - constructor() { - this.keys = 0; - this.deleted = new Set(); - this.compare = (a: StackNode, b: StackNode): number => { - // Because we want to pop max the top most max element, we should compare their keys in the case of a tie; - // that is because the higher key will be on top of the stack. - if (a.value === b.value) { - return b.key - a.key; - } - - return b.value - a.value; - }; - this.heap = new MaxPriorityQueue({ - compare: this.compare - }); - - // Create sentinel values for head and tail so we don't have to deal with null pointers. - const head: Partial = { - key: Number.MIN_SAFE_INTEGER, - value: Number.MIN_SAFE_INTEGER - }; - const tail: Partial = { - key: Number.MIN_SAFE_INTEGER, - value: Number.MIN_SAFE_INTEGER - }; - - head.next = tail as StackNode; - tail.previous = head as StackNode; - this.head = head as StackNode; - this.tail = tail as StackNode; - } - - push(x: number): void { - const node: StackNode = { - key: this.keys++, - value: x, - previous: this.head, - next: this.tail - }; - this.heap.enqueue(node); - - // If we have no elements between head and tail, insert this node between them as the only element. - if (this.head.next === this.tail) { - node.previous = this.head; - node.next = this.tail; - this.head.next = node; - this.tail.previous = node; - return; - } - - // If we do have an element between head and tail, add this node after that one. - const previous = this.tail.previous; - - node.previous = previous; - node.next = this.tail; - - previous.next = node; - this.tail.previous = node; - } - - pop(): number { - if (this.head.next === this.tail) { - throw new Error('nothing to pop'); - } - - const node = this.tail.previous; - const previous = node.previous; - const next = node.next; - previous.next = next; - next.previous = previous; - - // In contrast with removing from a doubly linked list, it's going to be very hard to remove from the middle of - // the heap. - // - // Instead, we'll cheat and just save this deletion for later. - this.deleted.add(node.key); - return node.value; - } - - top(): number { - if (this.head.next === this.head) { - throw new Error('nothing on top'); - } - - return this.tail.previous.value; - } - - peekMax(): number { - if (this.heap.isEmpty()) { - throw new Error('nothing to peek max'); - } - - this.deleteMax(); - const node = this.heap.front(); - return node.value; - } - - popMax(): number { - if (this.heap.isEmpty()) { - throw new Error('nothing to pop max'); - } - - this.deleteMax(); - if (this.heap.isEmpty()) { - throw new Error('nothing to pop max'); - } - - const node = this.heap.dequeue(); - const previous = node.previous; - const next = node.next; - previous.next = next; - next.previous = previous; - - return node.value; - } - - private deleteMax() { - while (!this.heap.isEmpty()) { - const max = this.heap.front(); - if (!this.deleted.has(max.key)) { - return; - } - - this.heap.dequeue(); - this.deleted.delete(max.key); - } - } -} diff --git a/src/heap/merge-k-sorted-lists.ts b/src/heap/merge-k-sorted-lists.ts deleted file mode 100644 index 1c27fe9..0000000 --- a/src/heap/merge-k-sorted-lists.ts +++ /dev/null @@ -1,58 +0,0 @@ -// DIFFICULTY: HARD -// -// You are given an array of k linked-lists lists, each linked-list is sorted in ascending order. -// -// Merge all the linked-lists into one sorted linked-list and return it. -// -// See {@link https://leetcode.com/problems/merge-k-sorted-lists/} -import { MinPriorityQueue } from '@datastructures-js/priority-queue'; -import { ListNode } from '../linked-list/common/list-node'; -export { mergeKLists }; - -// SOLUTION: -// -// Instead of only 2 lists, we have to merge k lists. The naive way is to do a linear scan of the head of every list -// to find the smallest element then create a new list node with that element and append it to the result list. -// -// However, we can do better by using a heap to store the head of each list so we can always find the minimum value -// without a linear scan. Additionally, the problem does not say we cannot reuse the input lists, so we do not have to -// create new list nodes. -// -// COMPLEXITY: -// -// There are k lists, so each enqueuing operation will take O(log k) time complexity. However, we are going to do this -// for every node in the list, so the total time complexity is O(n * log k) where n is the total number of nodes in all -// the lists. The naive approach with a linear scan would be O(n * k). -// -// Space complexity is O(k) because we are storing the head of each list in the heap, and we do not create new list -// nodes. -function mergeKLists(lists: Array): ListNode | null { - // Use a heap to store the head of each list so we can always find the minimum value to add to the result list - // without resorting to a linear scan. - const heap = new MinPriorityQueue({ priority: (node: ListNode) => node.val }); - for (const head of lists) { - if (head !== null) { - heap.enqueue(head); - } - } - - // As we keep advancing the current node, we'll lose track of the head. Let's have a sentinel node so that we can - // always keep track of where the list begins. - const sentinel = new ListNode(-1); - let current = sentinel; - while (heap.size() > 0) { - const smallest = heap.dequeue().element; - - // Advance the current node and push the smallest element in. - current.next = smallest; - current = current.next; - - // Now, advance the smallest node to the next value. - if (smallest.next !== null) { - heap.enqueue(smallest.next); - } - } - - // The sentinel node will point to the head of the list, so we did not lose it. - return sentinel.next; -} diff --git a/src/heap/minimize-deviation-in-array.ts b/src/heap/minimize-deviation-in-array.ts deleted file mode 100644 index ad609cc..0000000 --- a/src/heap/minimize-deviation-in-array.ts +++ /dev/null @@ -1,77 +0,0 @@ -// DIFFICULTY: HARD -// -// You are given an array nums of n positive integers. -// -// You can perform two types of operations on any element of the array any number of times: -// -// If the element is even, divide it by 2. -// -// For example, if the array is [1,2,3,4], then you can do this operation on the last element, and the array will be -// [1,2,3,2]. -// -// If the element is odd, multiply it by 2. -// -// For example, if the array is [1,2,3,4], then you can do this operation on the first element, and the array will be -// [2,2,3,4]. -// -// The deviation of the array is the maximum difference between any two elements in the array. -// -// Return the minimum deviation the array can have after performing some number of operations. -// -// See {@link https://leetcode.com/problems/minimize-deviation-in-array/} -const { MaxPriorityQueue } = require('@datastructures-js/priority-queue'); -export { minimumDeviation }; - -// SOLUTION: -// -// To solve this problem, we have to multiply elements to make them bigger, or divide elements to make them smaller. -// We continue to do this until the minimum and maximum elements are as close as possible. -// -// To make this problem easier, we try to minimize the deviation by making bigger numbers smaller, instead of -// simultaneously trying to make numbers bigger and smaller. To do this, we multiply all odd numbers by 2, so that -// they all become even. Afterwards, we can choose to perform a division or not to make it smaller. -// -// COMPLEXITY: -// -// Runs in O(n * log(n)) time due to the heap operations. -function minimumDeviation(nums: number[]): number { - // Initialize a max heap of all the numbers in this array. - const heap = new MaxPriorityQueue(); - - // Normalize all the numbers so that they are even. Now we can consider only division as a way to make numbers - // smaller and closer to each other. If a previously odd number was too big, we will eventually resize it smaller - // by division if necessary. - for (let i = 0; i < nums.length; i++) { - if (nums[i] % 2 === 1) { - nums[i] *= 2; - } - - heap.enqueue(nums[i]); - } - - let min = Math.min(...nums); - let deviation = Infinity; - - // Calculate the current deviation using the max element in the array, then half the max element and return it to - // the heap. Then repeat to bring the deviation down more and more. - while (true) { - // Calculate current deviation. - const max = heap.dequeue().element; - deviation = Math.min(deviation, max - min); - - // Oh no! If the max value was odd, we can't halve it and re-insert it into the heap. This means whatever the - // deviation is now, we are stuck, as halving smaller even values will not change the deviation. - if (max % 2 === 1) { - break; - } - - // Halve the max value and return it into the heap for re-processing. - const value = max / 2; - heap.enqueue(value); - - // Update the minimum value in case we've changed the minimum value by manipulating the max value. - min = Math.min(value, min); - } - - return deviation; -} diff --git a/src/heap/numbers-of-orders-in-the-backlog.ts b/src/heap/numbers-of-orders-in-the-backlog.ts deleted file mode 100644 index d915c6c..0000000 --- a/src/heap/numbers-of-orders-in-the-backlog.ts +++ /dev/null @@ -1,150 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given a 2D integer array orders, where each orders[i] = [pricei, amounti, orderTypei] denotes that amounti -// orders have been placed of type orderTypei at the price pricei. The orderTypei is: -// -// 0 if it is a batch of buy orders, or -// 1 if it is a batch of sell orders. -// -// Note that orders[i] represents a batch of amounti independent orders with the same price and order type. All orders -// represented by orders[i] will be placed before all orders represented by orders[i+1] for all valid i. -// -// There is a backlog that consists of orders that have not been executed. The backlog is initially empty. When an order -// is placed, the following happens: -// -// If the order is a buy order, you look at the sell order with the smallest price in the backlog. If that sell order's -// price is smaller than or equal to the current buy order's price, they will match and be executed, and that sell order -// will be removed from the backlog. Else, the buy order is added to the backlog. -// -// Vice versa, if the order is a sell order, you look at the buy order with the largest price in the backlog. If that -// buy order's price is larger than or equal to the current sell order's price, they will match and be executed, and -// that buy order will be removed from the backlog. Else, the sell order is added to the backlog. -// -// Return the total amount of orders in the backlog after placing all the orders from the input. Since this number can -// be large, return it modulo 10^9 + 7. -// -// See {@link https://leetcode.com/problems/number-of-orders-in-the-backlog/} -const { MaxPriorityQueue, MinPriorityQueue } = require('@datastructures-js/priority-queue'); -export { getNumberOfBacklogOrders }; - -// SOLUTION: -// -// This problem looks like it can be solved by maintaining a sorted list of buy orders and sell orders (aka heaps). -// For a sell order, you want buy orders in largest to smallest (max heap). For a buy order, you want sell orders -// from smallest to largest (min heap). -// -// COMPLEXITY: -// -// The time complexity is O(n * log(n)) because we are using heaps to maintain the order of the orders. -function getNumberOfBacklogOrders(orders: number[][]): number { - // Order = [price, amount]. - type Order = [number, number]; - - // Order by [price, _] for buys and sells. - const buys = new MaxPriorityQueue({ priority: (order: Order) => order[0] }); - const sells = new MinPriorityQueue({ priority: (order: Order) => order[0] }); - - function handleBuy(price: number, amount: number) { - while (amount > 0) { - // No more sellers, push buy order to the backlog. - if (sells.size() === 0) { - buys.enqueue([price, amount]); - return; - } - - const order = sells.front().element; - - // If the buy price is lower than the minimum selling price, we cannot find a match, so let's just put the buy - // order on the backlog. - if (price < order[0]) { - buys.enqueue([price, amount]); - return; - } - - // Otherwise, we have found a match, so let's execute the order. - const a = Math.min(amount, order[1]); - amount -= a; - order[1] -= a; - - // If the sell order has been exhausted, pop it from the heap. - if (order[1] === 0) { - sells.dequeue(); - } - - // If the buy order has been exhausted, we've processed the buy order, so return. - if (amount === 0) { - return; - } - } - } - - function handleSell(price: number, amount: number) { - while (amount > 0) { - // No more buyers, push sell order to the backlog. - if (buys.size() === 0) { - sells.enqueue([price, amount]); - return; - } - - const order = buys.front().element; - - // If the sell price is higher than the maximum buy price, we cannot find a buyer, so push onto the backlog. - if (price > order[0]) { - sells.enqueue([price, amount]); - return; - } - - // Otherwise, we have found a match, so let's execute the order. - const a = Math.min(amount, order[1]); - amount -= a; - order[1] -= a; - - // If the buy order has been exhausted, pop it from the heap. - if (order[1] === 0) { - buys.dequeue(); - } - - // If the buy order has been exhausted, we've processed the buy order, so return. - if (amount === 0) { - return; - } - } - } - - for (const order of orders) { - const [price, amount, orderType] = order; - - // Nothing to buy or sell here. - if (amount === 0) { - continue; - } - - // Buy order; match with the cheapest seller. - if (orderType === 0) { - handleBuy(price, amount); - continue; - } - - // Sell order; match with the highest buyer. - if (orderType === 1) { - handleSell(price, amount); - continue; - } - } - - // Calculate *amount* of total orders remaining in the backlog. - const modulus = 1e9 + 7; - let total = 0; - - for (const item of buys.toArray()) { - const [_, amount] = item.element; - total = (total + amount) % modulus; - } - - for (const item of sells.toArray()) { - const [_, amount] = item.element; - total = (total + amount) % modulus; - } - - return total; -} diff --git a/src/heap/task-scheduler.ts b/src/heap/task-scheduler.ts deleted file mode 100644 index 2e26e1e..0000000 --- a/src/heap/task-scheduler.ts +++ /dev/null @@ -1,74 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an array of CPU tasks, each represented by letters A to Z, and a cooling time, n. Each cycle or -// interval allows the completion of one task. Tasks can be completed in any order, but there's a constraint: identical -// tasks must be separated by at least n intervals due to cooling time. -// -// ​Return the minimum number of intervals required to complete all tasks. -// -// See {@link https://leetcode.com/problems/task-scheduler/} -const { MaxPriorityQueue } = require('@datastructures-js/priority-queue'); -export { leastInterval }; - -// SOLUTION: -// -// It's not necessary to return an execution order, only the minimum number of cycles/intervals required to complete -// all of the tasks. -// -// Note that either the most frequent task or the number of tasks will dictate the number of cycles required. First -// let's examine the case where we have a very frequent task that requires multiple cooling off periods. -// -// Let's suppose that task A appears 10 times, and task B appears 9 times, and some other tasks appear with lower -// frequency. No matter what, the 10 executions of task A will require n cycles of gaps between them, except for the -// last execution of task A. This means that there will be n cycles of idle slots between the executions of -// task A. But because we have 9 executions that need to spaced out (not the last), there are (10 - 1) * n empty -// cycles between A, for the other tasks to execute. -// -// Here, even though task B appears 9 times, we can fit task B comfortably in between the 9 cycles of task A. This, -// however, doesn't quite work if there are a huge number of tasks; if there are 10 task A's and 9 task B's, but 50 -// other uniquely named tasks, then we will still take 50 cycles if (10 - 1) * n is a smaller number. -function leastInterval(tasks: string[], n: number): number { - type Task = string; - type Frequency = number; - const map = new Map(); - for (const task of tasks) { - const freq = map.get(task) ?? 0; - map.set(task, freq + 1); - } - - const heap = new MaxPriorityQueue(); - for (const [_, freq] of map) { - heap.enqueue(freq); - } - - type Item = { - freq: number; - cycle: number; - }; - let cycle = 0; - const queue: Item[] = []; - while (!heap.isEmpty() || queue.length !== 0) { - if (queue.length !== 0) { - if (queue[0].cycle === cycle) { - const item = queue.shift()!; - heap.enqueue(item.freq); - } - } - - if (!heap.isEmpty()) { - let freq = heap.dequeue().element; - freq--; - - if (freq !== 0) { - queue.push({ - freq, - cycle: cycle + n + 1 - }); - } - } - - cycle++; - } - - return cycle; -} diff --git a/src/interval/interval-list-intersections.ts b/src/interval/interval-list-intersections.ts deleted file mode 100644 index a6dd241..0000000 --- a/src/interval/interval-list-intersections.ts +++ /dev/null @@ -1,96 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given two lists of closed intervals, firstList and secondList, where firstList[i] = [starti, endi] and -// secondList[j] = [startj, endj]. Each list of intervals is pairwise disjoint and in sorted order. -// -// Return the intersection of these two interval lists. -// -// A closed interval [a, b] (with a <= b) denotes the set of real numbers x with a <= x <= b. -// -// The intersection of two closed intervals is a set of real numbers that are either empty or represented as a closed -// interval. For example, the intersection of [1, 3] and [2, 4] is [2, 3]. -// -// See {@link https://leetcode.com/problems/interval-list-intersections/} -export { intervalIntersection }; - -// SOLUTION: -// -// This is a very straightforward problem except there's a very strange wrinkle in that intersections can be empty -// intervals. Because of this we can't just advance both pointers when we find an intersection. Instead, we have to -// only advance the one with the smaller endpoint. -// -// COMPLEXITY: -// -// Time complexity is O(m + n) since it iterates through both lists once each. -// -// Space complexity is O(min(m, n)) since the intersections array is limited by the size of the smaller list. -function intervalIntersection(firstList: number[][], secondList: number[][]): number[][] { - // The two intervals will intersect if we have a situation like this: - // - // A: [.......] - // B: [.......] - // - // ...where B[x] is greater than A[x] and B[x] is less than A[y]. Also, we can get an intersection like this: - // - // A: [.......] - // B: [.......] - // - // ...where A[x] is greater than B[x] and A[x] is less than B[y]. - function isIntersecting(a: number[], b: number[]) { - const [ax, ay] = a; - const [bx, by] = b; - return (bx >= ax && bx <= ay) || (ax >= bx && ax <= by); - } - - // Now we can iterate through both and check if they intersect. If no intersection exists, then we must have a - // a situation like one of these: - // - // A: [..] - // B: [.....] - // - // ...or... - // - // A: [.....] - // B: [..] - // - // In these cases, whichever of A[y] or B[y] is SMALLER is the one that should be dropped and advanced. That's - // because the intervals are disjoint, so the smaller of the y values will be the one that is further to the left, - // and not able to intersect with anything. - // - // We can stop as soon as one list is exhausted because there can't be any more intersections after that. - const intersections: number[][] = []; - let i = 0; - let j = 0; - while (i < firstList.length && j < secondList.length) { - const a = firstList[i]; - const b = secondList[j]; - if (isIntersecting(a, b)) { - // Calculate the intersection. - // - // A: [...|....] - // B: [....|...] - // - // We'll want the max of the two start points and the min of the two end points. - // - // It could also be the case that we have something like this: - // - // A: [....| - // B: |....] - // - // In which case the intersection is an empty interval. This is weird. But it will become relevant in the next - // step. We shouldn't just advance BOTH pointers because of this empty interval situation. Instead, we should - // just advance the smaller of the two end points. - const c = [Math.max(a[0], b[0]), Math.min(a[1], b[1])]; - intersections.push(c); - } - - // Advance the one that has the smaller end point. - if (a[1] < b[1]) { - i++; - } else { - j++; - } - } - - return intersections; -} diff --git a/src/interval/meeting-rooms.ts b/src/interval/meeting-rooms.ts deleted file mode 100644 index 97f7ee0..0000000 --- a/src/interval/meeting-rooms.ts +++ /dev/null @@ -1,47 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an array of meeting time intervals where intervals[i] = [starti, endi], determine if a person could attend all -// meetings. -// -// See {@link https://leetcode.com/problems/meeting-rooms/} -export { canAttendMeetings }; - -// SOLUTION: -// -// See the merge intervals problem for a more detailed solution. -function canAttendMeetings(intervals: number[][]): boolean { - function compare(a: number[], b: number[]) { - if (a[0] < b[0]) { - return -1; - } - - if (a[0] > b[0]) { - return 1; - } - - return a[1] - b[1]; - } - - function isOverlap(a: number[], b: number[]) { - if (a[1] <= b[0]) { - return false; - } - - return true; - } - - if (intervals.length <= 1) { - return true; - } - - intervals.sort((a, b) => compare(a, b)); - for (let i = 1; i < intervals.length; i++) { - const previous = intervals[i - 1]; - const current = intervals[i]; - if (isOverlap(previous, current)) { - return false; - } - } - - return true; -} diff --git a/src/interval/meeting-scheduler.ts b/src/interval/meeting-scheduler.ts deleted file mode 100644 index 84b9ceb..0000000 --- a/src/interval/meeting-scheduler.ts +++ /dev/null @@ -1,66 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given the availability time slots arrays slots1 and slots2 of two people and a meeting duration duration, return the -// earliest time slot that works for both of them and is of duration duration. -// -// If there is no common time slot that satisfies the requirements, return an empty array. -// -// The format of a time slot is an array of two elements [start, end] representing an inclusive time range from start to -// end. -// -// It is guaranteed that no two availability slots of the same person intersect with each other. That is, for any two -// time slots [start1, end1] and [start2, end2] of the same person, either start1 > end2 or start2 > end1. -// -// See {@link https://leetcode.com/problems/meeting-scheduler/} -export { minAvailableDuration }; - -// SOLUTION: -// -// To solve this, sort the intervals and use the two pointer technique to find a time slot that works for everybody. -function minAvailableDuration(slots1: number[][], slots2: number[][], duration: number): number[] { - function compare(a: number[], b: number[]) { - if (a[0] < b[0]) { - return -1; - } - - if (a[0] > b[0]) { - return 1; - } - - // If a[0] and b[0] are the same value, we can just compare based on a[1] and b[1], and a normal comparator will - // give elements in ascending order via x - y. - return a[1] - b[1]; - } - - slots1.sort(compare); - slots2.sort(compare); - - let i = 0; - let j = 0; - while (i < slots1.length && j < slots2.length) { - const a = slots1[i]; - const b = slots2[j]; - - // The start time is the max of the two individuals start times; we can't start before the other person is ready. - // The end time is the min of the two individuals end times; we can't end later than the other person's hard stop. - const start = Math.max(a[0], b[0]); - const end = Math.min(a[1], b[1]); - - // If we can accomodate the availability we should return the start time plus the duration; we shouldn't keep the - // meeting longer than it needs to be. - if (end - start >= duration) { - return [start, start + duration]; - } - - // Otherwise, we should move the pointer for the person who has the earlier ending time; these times represent - // availability, so if an earlier availability doesn't match the other person's later availability, we should - // move the person with the earlier availability up. - if (a[1] < b[1]) { - i++; - } else { - j++; - } - } - - return []; -} diff --git a/src/interval/merge-intervals.ts b/src/interval/merge-intervals.ts deleted file mode 100644 index c037ec8..0000000 --- a/src/interval/merge-intervals.ts +++ /dev/null @@ -1,61 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array -// of the non-overlapping intervals that cover all the intervals in the input. -// -// See {@link https://leetcode.com/problems/merge-intervals/} -export { merge }; - -// SOLUTION: -function merge(intervals: number[][]): number[][] { - function compareInternal(a: number[], b: number[]) { - if (a[0] < b[0]) { - return -1; - } - - if (a[0] > b[0]) { - return 1; - } - - // If a[0] and b[0] are the same value, we can just compare based on a[1] and b[1], and a normal comparator will - // give elements in ascending order via x - y. - return a[1] - b[1]; - } - - function shouldMerge(a: number[], b: number[]) { - // If the intervals are disjoint, then a[1] is going to be strictly less than b[0]. In every other case, we - // should merge. We can make this assumption because we know the intervals are sorted, and a <= b. - if (a[1] < b[0]) { - return false; - } - - return true; - } - - function mergeInternal(a: number[], b: number[]) { - // Because the intervals are sorted (a <= b), a[0] is always going to be smaller or the same as b[0]. - return [a[0], Math.max(a[1], b[1])]; - } - - // First sort the interval, then run our merge algorithm. - intervals.sort((a, b) => compareInternal(a, b)); - - const merged: number[][] = []; - for (const interval of intervals) { - if (merged.length === 0) { - merged.push(interval); - continue; - } - - const a = merged[merged.length - 1]; - const b = interval; - if (!shouldMerge(a, b)) { - merged.push(b); - continue; - } - - merged[merged.length - 1] = mergeInternal(a, b); - } - - return merged; -} diff --git a/src/leetcode/__init__.py b/src/leetcode/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/leetcode/array/best_time_to_buy_and_sell_stock_ii.py b/src/leetcode/array/best_time_to_buy_and_sell_stock_ii.py new file mode 100644 index 0000000..f49d185 --- /dev/null +++ b/src/leetcode/array/best_time_to_buy_and_sell_stock_ii.py @@ -0,0 +1,51 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an integer array prices where prices[i] is the price of a given stock on the ith day. +# +# On each day, you may decide to buy and/or sell the stock. You can only hold at most one share of the stock at any +# time. However, you can buy it then immediately sell it on the same day. +# +# Find and return the maximum profit you can achieve. +# +# See https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii +class Solution: + def maxProfit(self, prices: list[int]) -> int: + """ + SOLUTION + -------- + + The question is a bit contrived, as in reality this would never happen. Here, we are assuming we can go + backwards in time to be able to buy at the low point and sell at the high point. Just keep that in mind: we + have a time machine. + + In the easy version of this problem, we can buy the stock and sell it at some later date, ONE TIME! However, + here, we can buy and sell as many times as we want. + + This actually makes the problem much easier because can simulate buying on every day and add to our profit if + there is any. Honestly, this should be easy and the other one should be medium. + + COMPLEXITY + ---------- + + Time complexity is O(n). + + Space complexity is O(1). + """ + profit = 0 + + # If we only have one day of price data, we can't make any profits at all. + if len(prices) == 1: + return profit + + for i in range(1, len(prices)): + # Look at the difference in price between yesterday and today. + previous = prices[i - 1] + current = prices[i] + delta = current - previous + + # If we end up having a profit by buying yesterday, let's sell it. + if delta > 0: + profit += delta + + return profit diff --git a/src/leetcode/array/buildings_with_an_ocean_view.py b/src/leetcode/array/buildings_with_an_ocean_view.py new file mode 100644 index 0000000..fbda59f --- /dev/null +++ b/src/leetcode/array/buildings_with_an_ocean_view.py @@ -0,0 +1,41 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# There are n buildings in a line. You are given an integer array heights of size n that represents the heights of the +# buildings in the line. +# +# The ocean is to the right of the buildings. A building has an ocean view if the building can see the ocean without +# obstructions. Formally, a building has an ocean view if all the buildings to its right have a smaller height. +# +# Return a list of indices (0-indexed) of buildings that have an ocean view, sorted in increasing order. +# +# See https://leetcode.com/problems/buildings-with-an-ocean-view +class Solution: + def findBuildings(self, heights: list[int]) -> list[int]: + """ + SOLUTION + -------- + + This is easier to do iterating from right to left (since the ocean is to the right). We can keep track of the + tallest building we've seen so far, and if we encounter a building that is taller, we can add it to the list + with ocean views. + + COMPLEXITY + ---------- + + Time complexity is O(n) because we are iterating through the list of buildings once, and reversing once. + + Space complexity is O(n) because we are storing a result array. + """ + result: list[int] = [] + tallest = float("-inf") + + for i in reversed(range(len(heights))): + height = heights[i] + if height > tallest: + tallest = height + result.append(i) + + # Because we want the buildings in increasing order and we iterated in reverse, reverse the result list. We + # could have used a deque, but converting the deque to a list will be O(n) anyways. + return result[::-1] diff --git a/src/leetcode/array/design_hit_counter.py b/src/leetcode/array/design_hit_counter.py new file mode 100644 index 0000000..00003d1 --- /dev/null +++ b/src/leetcode/array/design_hit_counter.py @@ -0,0 +1,51 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Design a hit counter which counts the number of hits received in the past 5 minutes (i.e., the past 300 seconds). +# +# Your system should accept a timestamp parameter (in seconds granularity), and you may assume that calls are being +# made to the system in chronological order (i.e., timestamp is monotonically increasing). Several hits may arrive +# roughly at the same time. +# +# See https://leetcode.com/problems/design-hit-counter +class HitCounter: + def __init__(self) -> None: + """ + SOLUTION + -------- + + To do this efficiently we'll have to use a circular array buffer. This is the same technique used by time + series databases. + + COMPLEXITY + ---------- + + Time complexity is O(1) for both methods since we fix the array size at 300. + + Space complexity is O(1) for both methods because we fix the array size at 300. + """ + # Each event is a [timestamp, hit_count]. Since our granularity is in seconds, we only need to track the last + # 300 seconds. + self.timestamps = [0 for _ in range(300)] + self.hits = [0 for _ in range(300)] + + def hit(self, timestamp: int) -> None: + # Find the index of the event in our circular buffer that would correspond to this timestamp. + i = timestamp % 300 + + # If we've already recorded this timestamp, increment it. + if self.timestamps[i] == timestamp: + self.hits[i] += 1 + # If we haven't, set the hits to 1. + else: + self.timestamps[i] = timestamp + self.hits[i] = 1 + + def getHits(self, timestamp: int) -> int: + count = 0 + + for i, t in enumerate(self.timestamps): + if timestamp - t < 300: + count += self.hits[i] + + return count diff --git a/src/leetcode/array/dot_product_of_two_sparse_vectors.py b/src/leetcode/array/dot_product_of_two_sparse_vectors.py new file mode 100644 index 0000000..5310df6 --- /dev/null +++ b/src/leetcode/array/dot_product_of_two_sparse_vectors.py @@ -0,0 +1,53 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given two sparse vectors, compute their dot product. +# +# Implement class SparseVector: +# +# - SparseVector(nums) Initializes the object with the vector nums +# - dotProduct(vec) Compute the dot product between the instance of SparseVector and vec +# +# A sparse vector is a vector that has mostly zero values, you should store the sparse vector efficiently and compute +# the dot product between two SparseVector. +# +# Follow up: What if only one of the vectors is sparse? +# +# See https://leetcode.com/problems/dot-product-of-two-sparse-vectors +class SparseVector: + def __init__(self, nums: list[int]) -> None: + """ + SOLUTION + -------- + + If the vector is sparse, just store the non-zero values in a map of index to value. That will compress for + storage; to compute the dot product you can either decompress and then do the dot product, or you can do the + multiplication over the non-zero values directly. + + Doing it without decompression is more efficient, and we should start with the smaller vector to minimize the + number of operations. + + COMPLEXITY + ---------- + + Time complexity is O(k) in the best case, where k is the number of non-zero elements. In the worst case, + though, it could still be O(n) if both vectors are dense. + + Space complexity is O(k) in the best case, where k is the number of non-zero elements. + """ + + # This compresses the vector so that non-zero values are mapped. + self.mapping: dict[int, int] = {i: num for i, num in enumerate(nums) if num != 0} + + def dotProduct(self, vec: "SparseVector") -> int: + # Get the vectors in order of size; we'll iterate over the smaller vector. + (smaller, larger) = (self.mapping, vec.mapping) + if len(self.mapping) >= len(vec.mapping): + (smaller, larger) = (vec.mapping, self.mapping) + + result = 0 + for i, value in smaller.items(): + if i in larger: + result += value * larger[i] + + return result diff --git a/src/leetcode/array/group_anagrams.py b/src/leetcode/array/group_anagrams.py new file mode 100644 index 0000000..6a945d8 --- /dev/null +++ b/src/leetcode/array/group_anagrams.py @@ -0,0 +1,37 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an array of strings `strs`, group the anagrams together. You can return the answer in any order. +# +# An anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all +# the original letters exactly once. +# +# See https://leetcode.com/problems/group-anagrams +from collections import defaultdict + + +class Solution: + def groupAnagrams(self, texts: list[str]) -> list[list[str]]: + """ + SOLUTION + -------- + + Each anagram can be rearranged into canonical form by sorting the letters. Then simply map the canonical form to + each anagram. + + COMPLEXITY + ---------- + + Time complexity is O(n * m * log m) where n is the number of strings, and m is the length of the longest string. + This is because we have to sort each string's characters in O(m * log m), and there are n strings. + + Space complexity is O(m * n). + """ + # Define a map of canonical representation -> list of anagrams. + mapping: dict[str, list[str]] = defaultdict(list) + + for text in texts: + canonical = "".join(sorted(text)) + mapping[canonical].append(text) + + return list(mapping.values()) diff --git a/src/leetcode/array/longest_consecutive_sequence.py b/src/leetcode/array/longest_consecutive_sequence.py new file mode 100644 index 0000000..3323014 --- /dev/null +++ b/src/leetcode/array/longest_consecutive_sequence.py @@ -0,0 +1,64 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an unsorted array of integers nums, return the length of the longest consecutive elements sequence. +# +# You must write an algorithm that runs in O(n) time. +# +# See https://leetcode.com/problems/longest-consecutive-sequence +class Solution: + def longestConsecutive(self, xs: list[int]) -> int: + """ + SOLUTION + -------- + + The problem is phrased in a very confusing way. The sequence doesn't ACTUALLY need to be consecutive within the + array; it only needs to be consecutive if you plucked the sequence out of the array and sorted it. + + For example, [1, 500, 2, 3, 4] has consecutive elements [1, 2, 3, 4]. The fact that 500 appears in the middle + is okay; the number 500 begins its own consecutive sequence of length 1. The other elements [1, 2, 3, 4] create + a sequence of length 4. + + Conceptually, we'll do this by throwing all the array elements into a set. Then, for each element `x`, we can + figure out if it's part of a sequence by repeatedly checking its predecessor `x - 1` in the set. + + COMPLEXITY + ---------- + + Time complexity is O(n). It may appear that the inner loop runs multiple times, but each element in the array + is only processed once; the inner loop will skip `x - 1` if it was already part of some other sequence from a + previous iteration. + + Space complexity is O(n). + """ + longest = 0 + + # Use a set to keep track of all elements in the array; we'll reference it to find out if a predecessor to an + # element exists as we iterate through the array. + uniques: set[int] = set(xs) + + for x in uniques: + # Let us consider if element x is part of some sequence. We can do this by considering its predecessor, + # x - 1. + # + # If x - 1 is not in the set, x must begin some new sequence. Let's find out how long it is by incrementing + # x until we can't find any more consecutive elements. + if x - 1 not in uniques: + # We know that x is in the array, so let's start checking x + 1 and onwards for the length of a + # consecutive sequence. + x = x + 1 + length = 1 + + while x in uniques: + x += 1 + length += 1 + + # Once we run out of elements, we have determined the length of the sequence between at x. We'll + # compare it to the longest sequence we've found so far. + longest = max(longest, length) + else: + # If x - 1 is in the set, we know it is part of some sequence. However, this sequence must've already + # been found by the inner loop above, so we can skip this element. + pass + + return longest diff --git a/src/leetcode/array/max_chunks_to_make_sorted.py b/src/leetcode/array/max_chunks_to_make_sorted.py new file mode 100644 index 0000000..cf8780f --- /dev/null +++ b/src/leetcode/array/max_chunks_to_make_sorted.py @@ -0,0 +1,44 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an integer array arr of length n that represents a permutation of the integers in the range [0, n - 1]. +# +# We split arr into some number of chunks (i.e., partitions), and individually sort each chunk. After concatenating +# them, the result should equal the sorted array. +# +# Return the largest number of chunks we can make to sort the array. +# +# See https://leetcode.com/problems/max-chunks-to-make-sorted +class Solution: + def maxChunksToSorted(self, xs: list[int]) -> int: + """ + SOLUTION + -------- + + Because we know the elements in the array are a permutation of numbers less than n, we can use a greedy approach + by keeping track of the max element seen so far. + + Unlike the other problem, this does not require using a stack. + + COMPLEXITY + ---------- + + Time complexity is O(n). + + Space complexity is O(1). + """ + chunks = 0 + max_chunks = 0 + + for i, x in enumerate(xs): + max_chunks = max(x, max_chunks) + + # Because all elements are a permutation of the numbers from 0 to xs.length - 1, if we encounter i === max, + # this means that for sure all elements < i can be sorted to form a chunk. + # + # Again, because all elements to the right of i are going to be greater than max (and i), then we can be + # sure that the next element begins a new chunk. + if i == max_chunks: + chunks += 1 + + return chunks diff --git a/src/leetcode/array/maximum_swap.py b/src/leetcode/array/maximum_swap.py new file mode 100644 index 0000000..e646ed7 --- /dev/null +++ b/src/leetcode/array/maximum_swap.py @@ -0,0 +1,69 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an integer num. You can swap two digits at most once to get the maximum valued number. +# +# Return the maximum valued number you can get. +# +# See https://leetcode.com/problems/maximum-swap +class Solution: + def maximumSwap(self, num: int) -> int: + """ + SOLUTION + -------- + + The simple idea of swapping the largest number with the first digit doesn't always work. It works a lot of the + time, but a couple of cases mess up this algorithm: + + - 98368 would stay 98368; we need to swap the 3 and 8 to get 98863. + - 1993 could become 9193, but we need to swap the 1 and other 9 to get 9913. + + The way to think about this is: + + 1. Find the FIRST digit that can be made bigger (larger digits appear later). + 2. Swap it with the LAST occurrence of the largest digit. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of digits. We iterate through the digits once to create our array + and map. We iterate through the digits a second time to find the swap point. There is a nested loop, but the + inner loop is bounded by the 10 digits, so it doesn't cause O(n^2) time complexity. + + Space complexity is O(n) because we store an array and map of the digits. + """ + # First convert the number to an array of digits. + digits = list(str(num)) + + # Create a map of digit -> index where it was last seen. Use this to find out the last occurrence of any digit. + last_seen: dict[int, int] = {} + for i, digit in enumerate(digits): + last_seen[int(digit)] = i + + # Find the first digit that can be made bigger by iterating through the digits list. + for i, digit in enumerate(digits): + smaller = int(digit) + + # Find a larger digit to swap the smaller digit with. To do so, start at 9 and iterate downwards until we + # find a digit that is larger, and appears later in the number. + for larger in range(9, smaller, -1): + # Skip the digit if it's not larger. + if larger <= smaller: + continue + + # Skip the digit if it doesn't appear at all. + if larger not in last_seen: + continue + + # Skip the digit if it appears before the smaller digit. + j = last_seen[larger] + if j <= i: + continue + + # Success! We have found the largest digit that occurs as late as possible! Swap the digits and return + # the number. + digits[i], digits[j] = digits[j], digits[i] + return int("".join(digits)) + + # We made no swaps, so just return the number as is. + return num diff --git a/src/leetcode/array/merge_sorted_array.py b/src/leetcode/array/merge_sorted_array.py new file mode 100644 index 0000000..4b81eb4 --- /dev/null +++ b/src/leetcode/array/merge_sorted_array.py @@ -0,0 +1,78 @@ +# DIFFICULTY: EASY +# ---------------- +# +# You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, and two integers m and n, +# representing the number of elements in nums1 and nums2 respectively. +# +# Merge nums1 and nums2 into a single array sorted in non-decreasing order. +# +# The final sorted array should not be returned by the function, but instead be stored inside the array nums1. To +# accommodate this, nums1 has a length of m + n, where the first m elements denote the elements that should be merged, +# and the last n elements are set to 0 and should be ignored. nums2 has a length of n. +# +# See https://leetcode.com/problems/merge-sorted-array +class Solution: + def merge(self, xs: list[int], m: int, ys: list[int], n: int) -> None: + """ + SOLUTION + -------- + + TLDR: Work backwards. Send the largest elements from the smaller array to the end of the bigger array. + + This would be VERY easy, if you could use extra memory. To merge without using extra memory, we do need to + merge into the bigger array, xs. + + You could go about this in two different ways: + + 1. Start from the logical beginning of both arrays and swap elements as needed to maintain the correct order. + 2. Start from the logical end of both arrays and add largest elements to the end of xs. + + Going with option 1 is more natural, but it's more error prone and more work. There may be elements at the end + of xs that need to be shifted to the right to make room. + + Going with option 2 is less natural, but it ensures you have enough space. Since nums1 has size m + n, and ys + has size n, you can reliably fit all of nums2 into nums1 without any collisions or shifting. Additionally, if + you use up all the elements in ys, you don't need to do anything with the remaining elements of ys since they + are already sorted. + + Pro tip: when asked to do something in place, and you have extra space, consider doing backwards iteration + instead of forwards to avoid collisions and overwriting elements. Secondly, if you started with forwards + iteration and realize you might have to shift elements, consider a backwards iteration approach to see if it + might work better. + + The problem doesn't state this, but we assume using no extra memory either. + + COMPLEXITY + ---------- + + Time complexity is O(m + n). + + Space complexity is O(1) as per problem requirements. + """ + i = m - 1 + j = n - 1 + last = m + n - 1 + + # Send the largest elements from both arrays to the end of the array xs. + while i >= 0 and j >= 0: + a = xs[i] + b = ys[j] + + if a < b: + xs[last] = ys[j] + j -= 1 + last -= 1 + else: + xs[last] = xs[i] + i -= 1 + last -= 1 + + # Now we have consumed all elements in one of the arrays. If there are remaining elements in ys, we should add + # them to the array xs. + # + # On the other hand, if there are remaining elements in xs, there's nothing to be done because that slice of the + # array is already sorted. + while j >= 0: + xs[last] = ys[j] + j -= 1 + last -= 1 diff --git a/src/leetcode/array/pascals_triangle.py b/src/leetcode/array/pascals_triangle.py new file mode 100644 index 0000000..8c576b1 --- /dev/null +++ b/src/leetcode/array/pascals_triangle.py @@ -0,0 +1,46 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer numRows, return the first numRows of Pascal's triangle. +# +# In Pascal's triangle, each number is the sum of the two numbers directly above it as shown: +# +# See https://leetcode.com/problems/pascals-triangle +class Solution: + def generate(self, numRows: int) -> list[list[int]]: + """ + SOLUTION: + + A simple straightforward algorithm works. + + COMPLEXITY + ---------- + + Time complexity is O(n). + + Space complexity is O(n). + """ + triangle: list[list[int]] = [] + + for i in range(numRows): + # Special case the first row + if i == 0: + triangle.append([1]) + continue + + # The first and last elements of this row have value 1. + current = [1] + + # The middle elements gotten by summing pairs of the previous row. + previous = triangle[i - 1] + if len(previous) > 1: + for j in range(1, len(previous)): + current.append(previous[j] + previous[j - 1]) + + # The first and last elements of this row have value 1. + current.append(1) + + # Push the constructed row onto the triangle. + triangle.append(current) + + return triangle diff --git a/src/leetcode/array/top_k_frequent_elements.py b/src/leetcode/array/top_k_frequent_elements.py new file mode 100644 index 0000000..05fdce0 --- /dev/null +++ b/src/leetcode/array/top_k_frequent_elements.py @@ -0,0 +1,41 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any +# order. +# +# See https://leetcode.com/problems/top-k-frequent-elements +from collections import defaultdict + + +class Solution: + def topKFrequent(self, xs: list[int], k: int) -> list[int]: + """ + SOLUTION + -------- + + Just map each number to its frequency then sort by frequency. Return the first k elements. + + COMPLEXITY + ---------- + + Time complexity dominated by the sort, which is O(n log n). + + Space complexity is O(n) because we are using a map to store frequency. + """ + # Create a map of number -> frequency. + mapping: dict[int, int] = defaultdict(int) + + # Now populate the map by mapping each number to its frequency. + for x in xs: + mapping[x] += 1 + + # Now get all the unique elements from the list. + uniques = list(mapping.keys()) + + # Sort the unique values by their frequency. Since we want the most frequent elements, we sort in decreasing + # order (aka reverse order). + uniques = sorted(mapping.keys(), key=lambda x: mapping[x], reverse=True) + + # Return the first k elements + return uniques[:k] diff --git a/src/leetcode/array/two_sum.py b/src/leetcode/array/two_sum.py new file mode 100644 index 0000000..8003e20 --- /dev/null +++ b/src/leetcode/array/two_sum.py @@ -0,0 +1,45 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to +# target. +# +# You may assume that each input would have exactly one solution, and you may not use the same element twice. +# +# You can return the answer in any order. +# +# See https://leetcode.com/problems/two-sum +class Solution: + def twoSum(self, xs: list[int], target: int) -> list[int]: + """ + SOLUTION + -------- + + Use a hashmap to keep track of values we've already seen. This lets us avoid the O(n^2) solution of checking + every possible pair of values. + + When iterating, you can check if the complement nums[j] = target - nums[i] already exists in the hashmap. If it + does then we've found the solution and can return the indices immediately. + + COMPLEXITY + ---------- + + Time complexity is O(n). + + Space complexity is O(n). + """ + # Map number complement -> index where it appears. + mapping: dict[int, int] = {} + + for i, x in enumerate(xs): + complement = target - x + + # If our map has the complementary value that would make up the target, we can return it immediately. + if complement in mapping: + return [i, mapping[complement]] + + # Otherwise, store the number and its index in the map. If we find the complement later, then this index + # will be the complement's complement and we can return the indices as normal. + mapping[x] = i + + return [] diff --git a/src/binary-search/README.md b/src/leetcode/binary_search/README.md similarity index 77% rename from src/binary-search/README.md rename to src/leetcode/binary_search/README.md index 0030111..9ea77d8 100644 --- a/src/binary-search/README.md +++ b/src/leetcode/binary_search/README.md @@ -12,18 +12,19 @@ Use this to check if an element exists in the array. The `left` and `right` poi Note that this can be used like left most duplicate to find a good insertion point as well. ``` -let left = 0, right = xs.length - 1; -while (left <= right) { - const m = Math.floor((left + right) / 2); - if (xs[m] < target) { - left = m + 1; - } else if (xs[m] > target) { - right = m - 1; - } else { - return m; - } -} -return -1; +left = 0 +right = len(xs) - 1 + +while left <= right: + m = (left + right) // 2 + if xs[m] < target: + left = m + 1 + elif xs[m] > target: + right = m - 1 + else: + return m + +return -1 ``` ### Left-most Duplicate @@ -31,24 +32,25 @@ return -1; Use this to check where an element can be inserted into the array. The `left` and `right` pointers are not always within bounds (because the element could be inserted at the end of the array). Exiting the while loop indicates the element was found or you've found a good place to insert. ``` -let left = 0, right = xs.length; -while (left < right) { - const m = Math.floor((left + right) / 2); - if (xs[m] < target) { - left = m + 1; - } else { - right = m; - } -} -return left < xs.length && xs[left] === target ? left : -1; +left = 0 +right = len(xs) + +while left < right: + m = (left + right) // 2 + if xs[m] < target: + left = m + 1 + else: + right = m + +return left < len(xs) && xs[left] === target and left or -1 ``` ## Common Phrases These phrases indicate binary search might be useful: -- 'sorted array' -- 'find the position or index' +- sorted array +- find the position or index ## Boundaries diff --git a/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py b/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py new file mode 100644 index 0000000..8c61dbe --- /dev/null +++ b/src/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array.py @@ -0,0 +1,83 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value. +# +# If target is not found in the array, return [-1, -1]. +# +# You must write an algorithm with O(log n) runtime complexity. +# +# See https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array +class Solution: + def searchRange(self, xs: list[int], target: int) -> list[int]: + """ + SOLUTION + -------- + + The problem stipulates it must run in O(log n) time complexity, so that means we can't just do a standard binary + search and then a linear scan for the start and end ranges. + + The most braindead simple way to do this is to do binary search twice. Once using the left-most duplicate + technique and once using the right-most duplicate technique. + + Technically we could combine both the left and right searches into one binary search, using a flag to denote the + direction we are searching in. But that would be a bit more complex and harder to understand. + + COMPLEXITY + ---------- + + Time complexity is O(log n) time complexity because of max two binary searches. + + Space complexity is O(1) space complexity. + """ + + # Run binary search using the left-most duplicate technique. Here if we find that the the middle element is + # less than the target, we move to the right. + # + # Otherwise if the middle element is greater than or equal to the target, we move to the left (guaranteeing that + # if we find a duplicate target, we will keep moving left). + def binarySearchLeft() -> int: + left = 0 + right = len(xs) + + while left < right: + mid = (left + right) // 2 + if xs[mid] < target: + left = mid + 1 + else: + right = mid + + # If we have found the target, return the index, otherwise return -1. + if left < len(xs) and xs[left] == target: + return left + else: + return -1 + + # Run binary search using the right-most duplicate technique. Here if we find that the the middle element is + # greater than the target, we move to the left. + # + # Otherwise if the middle element is less than or equal to the target, we move to the right (guaranteeing that + # if we find a duplicate target, we will keep moving right). + def binarySearchRight() -> int: + left = 0 + right = len(xs) + + while left < right: + mid = (left + right) // 2 + if xs[mid] > target: + right = mid + else: + left = mid + 1 + + if right > 0 and xs[right - 1] == target: + return right - 1 + else: + return -1 + + # Now we just run both binary searches and return the range. + left = binarySearchLeft() + if left == -1: + return [-1, -1] + + right = binarySearchRight() + return [left, right] diff --git a/src/leetcode/binary_search/find_peak_element.py b/src/leetcode/binary_search/find_peak_element.py new file mode 100644 index 0000000..0498bf8 --- /dev/null +++ b/src/leetcode/binary_search/find_peak_element.py @@ -0,0 +1,82 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# A peak element is an element that is strictly greater than its neighbors. +# +# Given a 0-indexed integer array nums, find a peak element, and return its index. If the array contains multiple +# peaks, return the index to any of the peaks. +# +# You may imagine that nums[-1] = nums[n] = -∞. In other words, an element is always considered to be strictly greater +# than a neighbor that is outside the array. +# +# You must write an algorithm that runs in O(log(n)) time. +# +# See https://leetcode.com/problems/find-peak-element +class Solution: + def findPeakElement(self, xs: list[int]) -> int: + """ + SOLUTION + -------- + + This problem is not hard, but the wording is incredibly tricky. First off, let's talk about linear scan versus + binary search. There are only 1000 elements max in the array, so either would work. However, the problem says + that the algorithm must run in O(log(n)) time. This means we have to use binary search. + + Yes, binary search will still work even if the array is not sorted. That's because at each step, we are chasing + a gradient uphill. For example, if we see [..., 10, 5, 5, ...] then we know that the elements are RISING in the + left direction (and then eventually fall, because the left side element is -Infinity). So, we should slice the + array in half and look for a peak in the left half; the opposite logic applies if the array is rising to the + right. + + Secondly, the problem doesn't explicitly say what happens if there are no peaks. The solution I give throws an + error if none are found (e.g. [1, 1, 1], or [1, 10, 10, 1]). Nowhere does LeetCode say what should happen if + the inputs are invalid (e.g. have no peaks), or that all inputs are valid. However, it does apply to pass if we + assume peaks always exist! + + COMPLEXITY + ---------- + + Time complexity is O(log n). + + Space complexity is O(1). + """ + if len(xs) == 1: + return 0 + + # Use insertion point binary search to find the peak. + left = 0 + right = len(xs) + mid = -1 + + while left < right: + mid = (left + right) // 2 + prev = float("-inf") if mid == 0 else xs[mid - 1] + next = float("-inf") if mid == len(xs) - 1 else xs[mid + 1] + + # If we've found a peak, we can simply return it. + if xs[mid] > prev and xs[mid] > next: + return mid + + # Otherwise, if the right side number is larger, then a peak must exist somewhere on the right, so update + # the left boundary. + if next > xs[mid]: + left = mid + 1 + # Likewise, if the left side number is larger, then a peak must exist somewhere on the left, so update the + # right boundary. + elif prev > xs[mid]: + right = mid + # Oh, wait, what if we have a plateau? Like [..., 10, 10, 10, ...]. We don't know which direction to go, + # and in fact, binary search would NOT even work here. + # + # In fact, the inputs given will never cause this situation to occur. So we will simply ignore this edge + # case. + else: + pass + + # If we've reached this point, we have possibly found a peak. In theory, we could have landed on a plateau + # after running through the binary search, and the problem doesn't say what to do, but it appears you never + # get any inputs that cause this to happen. + # + # Again, the inputs given will never cause this situation to occur. Therefore, we simply assume that the binary + # search ran successfully and return the midpoint. + return mid diff --git a/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py b/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py new file mode 100644 index 0000000..bd29004 --- /dev/null +++ b/src/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold.py @@ -0,0 +1,56 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an array of integers nums and an integer threshold, we will choose a positive integer divisor, divide all the +# array by it, and sum the division's result. Find the smallest divisor such that the result mentioned above is less +# than or equal to threshold. +# +# Each result of the division is rounded to the nearest integer greater than or equal to that element. +# (For example: 7/3 = 3 and 10/2 = 5). +# +# The test cases are generated so that there will be an answer. +# +# See https://leetcode.com/problems/find-the-smallest-divisor-given-a-threshold +import math + + +class Solution: + def smallestDivisor(self, xs: list[int], threshold: int) -> int: + """ + SOLUTION + -------- + + The smallest divisor is 1. This would maximize the sum. + + The largest divisor is Math.max(...nums). This would minimize the sum. + + We want a sum that is exactly the threshold or just belong. This is a good candidate to use binary search. + + COMPLEXITY + ---------- + + Time complexity is O(n log k) where n is the length of the list and k is the max element in the list. + + Space complexity is O(1). + """ + # Use the insert point binary search approach to find the divisor we want. + left = 1 + right = max(xs) + + while left < right: + mid = (left + right) // 2 + divisor = mid + # We do math.ceil because the problem asks us to round up. + # + # Note: there's no way to chain map -> reduce. Mind boggling. + value = sum(math.ceil(x / divisor) for x in xs) + + # If the value is too large, our divisor was too small, so we should shift our left value to be mid + 1. + if value > threshold: + left = mid + 1 + # Otherwise, we should shift our right value to mid. + else: + right = mid + + # Found the divisor at the "insertion point". + return left diff --git a/src/leetcode/binary_search/kth_missing_positive_number.py b/src/leetcode/binary_search/kth_missing_positive_number.py new file mode 100644 index 0000000..c6970fa --- /dev/null +++ b/src/leetcode/binary_search/kth_missing_positive_number.py @@ -0,0 +1,63 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given an array arr of positive integers sorted in a strictly increasing order, and an integer k. +# +# Return the kth positive integer that is missing from this array. +# +# See https://leetcode.com/problems/kth-missing-positive-number +class Solution: + def findKthPositive(self, xs: list[int], k: int) -> int: + """ + SOLUTION + -------- + + There are multiple naive ways to do this. One way is to throw all elements into a set, then iterate from 1 to + the max set element, and check if the element is in the set. Do this k times to find the k-th missing element. + + Another naive way to do this is to do a linear scan, and keep count of the missing numbers. You'd also keep a + count of what the current element *should* be versus the current index. If the current index matches current + element, then continue on. Otherwise increment the count of missing numbers. If the count of missing numbers + is equal to k, then you've found it. + + Finally, you can use binary search to find the k-th missing element. Since the array is sorted, there's no need + to scan the entire array. We can calculate how many missing numbers exist at each index, and use that + information to determine if we should go left or right. + + COMPLEXITY + ---------- + + Time complexity is O(log n). + + Space complexity is O(1). + """ + # Use insertion point binary search to find the k-th missing element. + left = 0 + right = len(xs) + + while left < right: + mid = (left + right) // 2 + + # So at the mid index, if there were no missing elements, then xs[mid] should be mid + 1. For example, if + # we have: + # + # [1, 2, 3, 4, 5] + # + # ...then mid === 2, and xs[2] === 3. And 3 === mid + 1. Therefore, there are xs[2] - (2 + 1) missing + # elements, and here we have 0 missing elements. + missing = xs[mid] - (mid + 1) + + # If there aren't enough missing numbers, then the k-th missing number must be to the right, so update the + # left pointer to be mid + 1. + if missing < k: + left = mid + 1 + # If there are too many missing numbers, then the k-th missing number must be to the left, so update the + # right pointer to be mid - 1; + else: + right = mid + + # At this point, left === right, and left represents the index where missing >= k. Recall that if no numbers + # were missing, then arr[left] + 1 === left. If we assume that arr[left] + 1 > left by a COUNT of k elements, + # then we can find the VALUE of the k-th missing element, we add k to left. This also works if there are no + # "missing" elements (e.g. [1, 2, 3, 4]). + return left + k diff --git a/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py b/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py new file mode 100644 index 0000000..49ca712 --- /dev/null +++ b/src/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix.py @@ -0,0 +1,98 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an n x n matrix where each of the rows and columns is sorted in ascending order, return the kth smallest +# element in the matrix. +# +# Note that it is the kth smallest element in the sorted order, not the kth distinct element. +# +# You must find a solution with a memory complexity better than O(n^2). +# +# See https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix +class Solution: + def kthSmallest(self, matrix: list[list[int]], k: int) -> int: + """ + SOLUTION + -------- + + We can't flatten the list because that would take O(n^2) memory. + + Use binary search to narrow down where the kth smallest element is. Here, the left value is the smallest + element, and the right value is the largest element. + + We can use the mid element to tell us how close or far we are from the kth smallest element. The matrix is + sorted, so for any mid === matrix[i][j] we can figure out how many elements are less than or equal to mid. + + COMPLEXITY + ---------- + + Time complexity is O(n log m). We are using binary search on a range between the smallest and largest elements + in the matrix, call it m. We will do O(log m) iterations, but in each iteration, we do have to count how many + elements are less than or equal to the mid target. + + The countLessThanOrEqualTo() function takes at most n steps (where n is the number of rows/columns in the + matrix). Since this is done at every single iteration, the total time is O(n log m). + + Space complexity is O(1). + """ + + def countLessThanOrEqualTo(matrix: list[list[int]], target: int) -> int: + # We can leverage the properties of the matrix to count how many elements are less than or equal to the + # target. Use a modified two pointer technique to count up elements. + count = 0 + + # Start at the bottom left corner of the matrix: + # + # - - - - + # - - - - + # - - - - + # x - - - + # + # If the value is less than or equal to the target, then all elements from all previous rows are also less + # than our target (since the matrix is sorted). To count up that specific column, add (row + 1) to the + # count (the rows are 0 indexed, but the count is not, so we add 1). + row = len(matrix) - 1 + column = 0 + while row >= 0 and column < len(matrix): + # Add up all the elements in the column. That is: + # + # x - - - + # x - - - + # x - - - + # x - - - + # + # All x's get added to the count, which means all elements in the column get added to the count. + # Because the matrix is sorted, all elements in the above column MUST be less than or equal to the + # target. + # + # Stop when we reach column === matrix.length - 1. + if matrix[row][column] <= target: + count += row + 1 + column += 1 + # If the element is greater than the target, we can move up a row and try again: + # + # x - - - + # x - - - + # x - - - + # - - - - + # + # Stop when we reach row === 0. + else: + row -= 1 + + return count + + # Use insertion sort binary search to find exactly where the kth smallest element is; don't use the standard + # binary search algorithm because we're not looking for an exact value. + left = matrix[0][0] + right = matrix[len(matrix) - 1][len(matrix) - 1] + while left < right: + mid = (left + right) // 2 + count = countLessThanOrEqualTo(matrix, mid) + + if count < k: + left = mid + 1 + else: + right = mid + + return left diff --git a/src/leetcode/binary_search/longest_increasing_subsequence.py b/src/leetcode/binary_search/longest_increasing_subsequence.py new file mode 100644 index 0000000..680b2fa --- /dev/null +++ b/src/leetcode/binary_search/longest_increasing_subsequence.py @@ -0,0 +1,58 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums, return the length of the longest strictly increasing subsequence. +# +# See https://leetcode.com/problems/longest-increasing-subsequence +class Solution: + def lengthOfLIS(self, xs: list[int]) -> int: + """ + SOLUTION + -------- + + The naive way is to do this with dynamic programming and two nested loops. + + The more efficient way is to do binary search after constructing a "tails" array. The tails array represents + the smallest possible ending element for a subsequence of length i + 1. For example, if tails is [2, 3, 7], + that means: + + - There is a length 1 subsequence ending in 2. + - There is a length 2 subsequence ending in 3. + - There is a length 3 subsequence ending in 7. + + As we iterate through the array, we will keep the tails array updated. At the end of iteration, the length of + the tails array will tell us the longest increasing subsequence. + + COMPLEXITY + ---------- + + Time complexity is O(n log n) because we are doing a binary search for each element in the array. + + Space complexity is O(n) because we are using an array to store the tails. + """ + tails: list[int] = [] + + for x in xs: + left = 0 + right = len(tails) + while left < right: + mid = (left + right) // 2 + + if tails[mid] < x: + # Move to the right if num is larger. + left = mid + 1 + else: + # Move to the left if num is smaller. + right = mid + + # We found an index in tails; this number is the smallest possible ending element for a subsequence of + # length left. So update that value in tails. + if left < len(tails): + tails[left] = x + # Otherwise left == len(tails), which means we are adding a new element to the end of tails. This number is + # the smallest possible ending element for a subsequence of length left + 1. + else: + tails.append(x) + + # The length of tails is the length of the longest increasing subsequence. + return len(tails) diff --git a/src/leetcode/binary_search/median_of_two_sorted_arrays.py b/src/leetcode/binary_search/median_of_two_sorted_arrays.py new file mode 100644 index 0000000..1446dcb --- /dev/null +++ b/src/leetcode/binary_search/median_of_two_sorted_arrays.py @@ -0,0 +1,128 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays. +# +# The overall run time complexity should be O(log (m+n)). +# +# See https://leetcode.com/problems/median-of-two-sorted-arrays +import math + + +class Solution: + def findMedianSortedArrays(self, xs: list[int], ys: list[int]) -> float: + """ + SOLUTION + -------- + + This is not a reasonable question to ask in an interview. It's quite algorithmically complex. + + The naive way to solve this problem is to simply merge the two arrays and then take the midpoint. This is not + what the problem asks for. + + To more efficiently find the median, imagine that the merged array's median partitions the merged array into two + parts; a left part and a right part. The left part's partition will have elements that are all less than the + median, and the right part's partition will have elements that are all larger than the median. + + Our goal now is to find four partitions; two (possibly different sized) left partitions from each array such + that the values in those partitions are all less than the median. These two left partitions, if merged, would + be the same as the left partition in the fully merged array. Likewise, the two (possibly different sized) right + partitions, when merged, would be the right partition of the merged array. + + Note that we know that the left partition in the merged array is going to have h = (m + n) / 2 elements, where m + and n are the lengths of the two arrays (h representing half of the array elements in the merged array). We + need to take a guess at where to partition on the smaller array, so let's say that the size of that partition is + s = m / 2. Then the size of the left partition for the other array must be h - s. + + Using the values of s and h - s, we can check if we've hit upon properly sized partitions that give us the + median. If so, we can simply return it. If we didn't hit upon the properly sized partitions, then we'll have + to update our guess for s and try again. + + How do we try again? Well, we can simulate concatenating the left partitions and right partitions to find out + where we goofed for finding size s. For the smaller array, if we found s by setting the left pointer to 0, and + the right pointer to m / 2, then we'll have to use a modified version of binary search to update the pointers: + + - If the value at m = (left + right) / 2 was too small, set left = m + 1 and set right = m. + - If the value at m = (left + right) / 2 was too big, set left = 0 and set right = m / 2. + + Continue updating the left and right pointers until we have found the median. + + COMPLEXITY + ---------- + + Time complexity is O(log (m + n)). + + Space complexity is O(1). + """ + # Because we want to operate on the smaller array, let's find out which one is smaller and set xs equal to it. + if len(xs) > len(ys): + xs, ys = ys, xs + + # Again, we're using m and n as the array sizes, and m represents our smaller array size. + m = len(xs) + n = len(ys) + + # Note that this is a variation of the standard binary search algorithm. + left = 0 + right = m + while left <= right: + # Compute the partition midpoints for both arrays. + # + # Imagine we have m = 3 and n = 3. Then we want p1 to be at (m + n) / 2 = 1. The size of the "merged" + # array would be m + n = 6. The midpoint of the other array, including the median, would be at + # (m + n) / 2 - 3 = 3. + # + # However, for a "merged" array of odd size, say m = 3, and n = 4, then m + n = 7. And if we use + # (m + n) /2 - 3 = 3, that would not include the median at index 4. So we should use (m + n + 1) / 2 + # instead. + # + # Using (m + n + 1) / 2 does not affect the calculation for an even sized "merged" array because the floor + # function removes the decimal. + p1 = (left + right) // 2 # Note that p1 needs to be calculated using left / right. + p2 = ((m + n + 1) // 2) - p1 # Note that p2 needs to be calculated based on total size. + + # Now we imagine that the two left partitions are merged, and the two right partitions are merged. If we've + # chosen the partition points correctly, then the last value of both left partitions should be less than the + # head values of both right partitons. + # + # Here's an example partition of two arrays, where p1 = 2 and p2 = 3. + # + # xs = [1,2 | 3,3] with lmax1 = 2 and rmin1 = 3 (lmax1 is at p1 - 1 due to zero indexing). + # ys = [1,2,3 | 4,5,5] with lmax2 = 3 and rmin2 = 4 (lmax2 is at p2 - 1 due to zero indexing). + # + # In this case, we want to compare the rightmost values of xs with the opposite leftmost value of ys, and + # vice versa. + # + # Note that if p1 or p2 are zero, that means that the partitioning leaves no elements in the partition, so + # those min/max values should be replaced by -Infinity or +Infinity. + # + # Consider the following cases: + # + # xs = [] with lmax1 = -Infinity because there is no value, and everything is > lmax1. + # xs = [1,2,3|] with rmax1 = +Infinity because there is no value, and everything is < rmin1. + lmax1 = -math.inf if p1 == 0 else xs[p1 - 1] # If xs partition is empty, lmax1 value is unused. + rmin1 = math.inf if p1 == m else xs[p1] # If xs partition is full, rmin1 value is unused. + lmax2 = -math.inf if p2 == 0 else ys[p2 - 1] # If ys partition is empty, lmax2 value is unused. + rmin2 = math.inf if p2 == n else ys[p2] # If ys partition is full, rmin2 value is unused. + + # If our inequalities hold, we have found the median. The value differs depending on if we have an odd + # sized "merged" array or even. + if lmax1 <= rmin2 and lmax2 <= rmin1: + # Even case + if (m + n) % 2 == 0: + return (max(lmax1, lmax2) + min(rmin1, rmin2)) / 2 + + # Odd case. + return max(lmax1, lmax2) + + # Here the xs side left partition's last value is too large, so we have to update the right pointer. At the + # next iteration we will do a binary search where the right pointer has been set to the element before p1. + if lmax1 > rmin2: + right = p1 - 1 + # Here the xs side left partition's last value is too small, so we have to change the left pointer. At the + # next iteration we will do binary search where the left pointer has been set to the element after p1. + else: + left = p1 + 1 + + # This is never supposed to happen. + raise Exception("arrays are not sorted") diff --git a/src/leetcode/binary_search/search_in_rotated_sorted_array.py b/src/leetcode/binary_search/search_in_rotated_sorted_array.py new file mode 100644 index 0000000..e346fcb --- /dev/null +++ b/src/leetcode/binary_search/search_in_rotated_sorted_array.py @@ -0,0 +1,70 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# There is an integer array nums sorted in ascending order (with distinct values). +# +# Prior to being passed to your function, nums is possibly rotated at an unknown pivot index k (1 <= k < nums.length) +# such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (0-indexed). +# For example, [0,1,2,4,5,6,7] might be rotated at pivot index 3 and become [4,5,6,7,0,1,2]. +# +# Given the array nums after the possible rotation and an integer target, return the index of target if it is in nums, +# or -1 if it is not in nums. +# +# You must write an algorithm with O(log n) runtime complexity. +# +# See https://leetcode.com/problems/search-in-rotated-sorted-array +class Solution: + def search(self, xs: list[int], target: int) -> int: + """ + SOLUTION + -------- + + You can still use binary search for this; you just have to recognize when you have looped around in the array. + This uses the standard binary search algorithm, but with recursion. + + The standard approach is used because we are looking for an exact match for an element in the array, and return + -1 if the element is not found. + + COMPLEXITY + ---------- + + Time complexity is O(log n). + + Space complexity is O(log n). + """ + + def searchInternal(ys: list[int], start: int, end: int, t: int) -> int: + # If we've looped back around ourselves, that means the number wasn't found, and we can stop. + if start > end: + return -1 + + mid = (start + end) // 2 + if ys[mid] == t: + return mid + + isLeftSorted = ys[start] <= ys[mid] + if isLeftSorted: + # If the left portion is sorted, and our target is within that portion, update the start/end indexes to + # be strictly within the left side of the pivot, and perform binary search there. + if ys[start] <= t <= ys[mid]: + return searchInternal(ys, start, mid - 1, t) + # Otherwise, it is in the right portion of the array, which contains a rotation. Call this function + # again, with an updated restriction on the sub array to search. + else: + return searchInternal(ys, mid + 1, end, t) + + isRightSorted = not isLeftSorted + if isRightSorted: + # If the right portion is sorted, and our target is within that portion, update the start/end indexes to + # be strictly within the right side of the pivot, and perform binary search there. + if ys[mid + 1] <= t <= ys[end]: + return searchInternal(ys, mid + 1, end, t) + # Otherwise, it is in the left portion of the array, which contains a rotation. Call this function + # again, with an updated restriction on the sub array to search. + else: + return searchInternal(ys, start, mid - 1, t) + + # Otherwise, the value wasn't found at all + return -1 + + return searchInternal(xs, 0, len(xs) - 1, target) diff --git a/src/leetcode/dynamic_programming/README.md b/src/leetcode/dynamic_programming/README.md new file mode 100644 index 0000000..5573b57 --- /dev/null +++ b/src/leetcode/dynamic_programming/README.md @@ -0,0 +1,10 @@ +# Dynamic Programming + +## Common Phrases + +These phrases indicate dynamic programming might be useful: + +- count the number of ways +- optimal strategy +- maximize sum, path, value, or profit +- knapsack style constraints diff --git a/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py b/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py new file mode 100644 index 0000000..90a548f --- /dev/null +++ b/src/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences.py @@ -0,0 +1,86 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Given an integer array nums, return the number of all the arithmetic subsequences of nums. +# +# A sequence of numbers is called arithmetic if it consists of at least three elements and if the difference between +# any two consecutive elements is the same. +# +# - For example, [1, 3, 5, 7, 9], [7, 7, 7, 7], and [3, -1, -5, -9] are arithmetic sequences. +# - For example, [1, 1, 2, 5, 7] is not an arithmetic sequence. +# +# A subsequence of an array is a sequence that can be formed by removing some elements (possibly none) of the array. +# +# - For example, [2,5,10] is a subsequence of [1,2,1,2,4,1,5,10]. +# +# The test cases are generated so that the answer fits in 32-bit integer. +# +# See https://leetcode.com/problems/arithmetic-slices-ii-subsequence +from collections import defaultdict + + +class Solution: + def numberOfArithmeticSlices(self, xs: list[int]) -> int: + """ + SOLUTION: + + To solve this we can use a dynamic programming approach to build up the number of subsequences at each index, + for each subsequence difference. + + Note that we can have [2, 4, 6, 8], and there are subsequences [2, 4, 6, 8] (the entire array) and [2, 4, 6], + which is part of the array. Also note that [7, 7, 7, 7] has a total of 16 subsequences because every + subsequence is arithmetic (they can overlap). + + COMPLEXITY: + + Time complexity is O(n^2) due to iterating over all pairs (i, j). + + Space complexity is O(n^2) due to storing a dictionary for each index (and each dictionary having at most O(n) + entries). + """ + if len(xs) < 3: + # An arithmetic subsequence must have at least length 3, so if we have fewer, then there are no + # subsequences. + return 0 + + result = 0 + + # A list of dictionaries keeping track of subsequences ending at index i. The keys in the dictionary represent + # the differences in the arithmetic subsequence, and the values represent the count with that difference. + # + # Each dict is a map of difference -> count. + subsequences: list[dict[int, int]] = [] + for _ in range(len(xs)): + subsequences.append(defaultdict(int)) + + # Since we are considering pairs of elements, start at 1 to ensure that there is at least another element before + # it. + for i in range(len(xs)): + # At each index j, every number at index i (prior to j) could be participating in a subsequence where the + # difference of the arithmetic subsequence is xs[j] - xs[i]. So for each j, examine every previous element + # at i to see if it contributes to some subsequence. + for j in range(i): + # Calculate the difference between the two numbers, and note that the difference can be negative (and + # the numbers in the array can be negative as well). This means the subsequence can be increasing or + # decreasing; it does not matter. + # + # That is, we want to find and extend any arithmetic subsequences that xs[j] might be a part of with the + # difference of of xs[i] - xs[j]. + difference = xs[i] - xs[j] + + # Get count of existing subsequences at j with difference (xs[j - 1] === xs[j] - difference is true). + # We can extend these subsequences by adding xs[i]. + cj = subsequences[j][difference] + + # Get count of existing subsequences at i with difference (xs[i - 1] === xs[i] - difference is true). + ci = subsequences[i][difference] + + # Update the count of subsequences at j, which is the previous count at i, plus the previous count at j, + # and then 1 more to account for adding j to the subsequence (by going from xs[i] to xs[j]). + subsequences[i][difference] = ci + cj + 1 + + # Add to the running total, excluding the pair (xs[i], xs[j]). If we just have this pair, it will NOT + # contribute to a 3 element arithmetic subsequence (because there are only 2 elements). + result += cj + + return result diff --git a/src/leetcode/dynamic_programming/max_subarray.py b/src/leetcode/dynamic_programming/max_subarray.py new file mode 100644 index 0000000..4ad7262 --- /dev/null +++ b/src/leetcode/dynamic_programming/max_subarray.py @@ -0,0 +1,52 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums, find the subarray with the largest sum, and return its sum. +# +# See https://leetcode.com/problems/maximum-subarray +class Solution: + def maxSubArray(self, xs: list[int]) -> int: + """ + SOLUTION: + + Kadane's algorithm finds a solution in O(n) with O(1) extra space. + + The idea is to consider subarrays starting from the 0th, 1st, 2nd, etc element. However, it is not necessary to + consider the subarrays starting from 0, then 1, then 2, etc. + + It is enough to note that if all elements are positive, you want the entire array. But if they are not all + positive, then you want to only take elements until it's "not worth it". To determine what is "not worth it", + consider that if you start from the 0th index and continue to take elements, but the subarray sum becomes + negative when it was positive before. Then we know to abandon the entire subarray from 0 to that element. + + Generalizing, suppose we have a current subarray sum, and we add the next element. If adding that next element + causes the new current sum to be LESS than just that element by itself, we should abandon that subarray and + start over. + + COMPLEXITY: + + Time complexity is O(n). + + Space complexity is O(1). + """ + if len(xs) == 0: + return 0 + + if len(xs) == 1: + return xs[0] + + # Start calculating the sum of the current subarray at i = 0. + current = xs[0] + max_value = xs[0] + for x in xs[1:]: + # If adding x makes our current sum larger, we'll take x and add it to our potential max subarray sum. + if current + x > x: + current += x + # However, if adding x makes our current sum smaller, abandon the current subarray that was from where we + # started to i. We'll start counting the sum from a new subarray, starting at i instead. + else: + current = x + + max_value = max(current, max_value) + + return max_value diff --git a/src/leetcode/dynamic_programming/word_break_i.py b/src/leetcode/dynamic_programming/word_break_i.py new file mode 100644 index 0000000..b061ca0 --- /dev/null +++ b/src/leetcode/dynamic_programming/word_break_i.py @@ -0,0 +1,52 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated +# sequence of one or more dictionary words. +# +# Note that the same word in the dictionary may be reused multiple times in the segmentation. +# +# See https://leetcode.com/problems/word-break +class Solution: + def wordBreak(self, s: str, wordDict: list[str]) -> bool: + """ + SOLUTION: + + Note that this problem is significantly easier than word break ii because we don't need to do any backtracking + to generate all of the possible sentences. + + Here we can just incrementally build up a solution using dynamic programming. + + We loop through the string and seeing if we can build a word out of (0, i). If we can, we'd like to continue on + by seeing if we can build out a word from (0, j) where j > i. We'll keep going until we've exhausted the string + or if we've run out of words to try. + + However, we'll have to try every possible starting word. Then every possible secondary word, and so on. That's + why dynamic programming will be useful here. + """ + uniques = set(wordDict) + + # We'll use a map to indicate if a slice (0,i) can make a sentence. This can just be an array, because the + # first index is always going to be 0. However, we make a map here because it's a little clearer what we are + # doing. + mapping: dict[tuple[int, int], bool] = {} + + # The slice [0,0) is always going to form a sentence; the base sentence. + mapping[(0, 0)] = True + + # We want to see if we can successfully parse a sentence from [0,s.length), but to do so, we'll incrementally + # build up whether or not we can parse a sentence from [0,j) for all j <= len(s). + for i in range(len(s)): + # Yes we want j <= len(s) because slice is exclusive on the last index [i, j). + for j in range(i + 1, len(s) + 1): + word = s[i:j] + if word not in uniques: + continue + + # If the slice (0,i) formed a sentence, then (0,j) must form a sentence because (i,j) is a word. For + # example, if (0,i) was 'hello' and (i,j) was 'there', then (0,j) slice of 'hellothere' is a sentence. + if (0, i) in mapping: + mapping[(0, j)] = True + + # If we've formed a sentence using all the characters, we've got a winner here. + return (0, len(s)) in mapping diff --git a/src/leetcode/graph/add_edges_to_make_degrees_even.py b/src/leetcode/graph/add_edges_to_make_degrees_even.py new file mode 100644 index 0000000..6178e69 --- /dev/null +++ b/src/leetcode/graph/add_edges_to_make_degrees_even.py @@ -0,0 +1,120 @@ +# DIFFICULTY: HARD +# ---------------- +# +# There is an undirected graph consisting of n nodes numbered from 1 to n. You are given the integer n and a 2D array +# edges where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi. The graph can be disconnected. +# +# You can add at most two additional edges (possibly none) to this graph so that there are no repeated edges and no self-loops. +# +# Return true if it is possible to make the degree of each node in the graph even, otherwise return false. +# +# The degree of a node is the number of edges connected to it. +# +# See https://leetcode.com/problems/add-edges-to-make-degrees-of-all-nodes-even +from collections import defaultdict + + +class Solution: + def isPossible(self, n: int, edges: list[list[int]]) -> bool: + """ + SOLUTION + -------- + + When you add an edge between two nodes, you change the degree of those two nodes. We don't need to modify edges + for any even degree nodes; they are already good. Instead we want to find nodes with an odd degree. + + If we join any two odd degree nodes with an edge, both of them will become even degree nodes. Since we can only + add at most two edges, this can only be possible if we have 0, 2, or 4 nodes with an odd degree. + + COMPLEXITY + ---------- + + Time complexity is O(m + n). Each node and edge must be examined. + + Space complexity is O(m + n). We are storing a set of edges and a map of each node to degree. + """ + edge_set: set[tuple[int, int]] = set() + + def normalizeEdge(edge: tuple[int, int]) -> tuple[int, int]: + (a, b) = edge + return (a, b) if a < b else (b, a) + + def hasEdge(edge: tuple[int, int]) -> bool: + nonlocal edge_set + + e = normalizeEdge(edge) + return e in edge_set + + def addEdge(edge: tuple[int, int]) -> None: + nonlocal edge_set + + e = normalizeEdge(edge) + edge_set.add(e) + + # Create a map of node -> degrees. + degree_mapping: dict[int, int] = defaultdict(int) + for edge in edges: + [a, b] = edge + if a not in degree_mapping: + degree_mapping[a] = 0 + if b not in degree_mapping: + degree_mapping[b] = 0 + + degree_mapping[a] += 1 + degree_mapping[b] += 1 + addEdge((a, b)) + + # Find all odd degree nodes. Note that nodes are number 1 through n inclusive. + odd_nodes: list[int] = [] + for i in range(1, n + 1): + degrees = degree_mapping[i] + if degrees % 2 == 1: + odd_nodes.append(i) + + # Only 0, 2, or 4 odd degree node graphs can have edges added to create a valid all even degree path. So if we + # have 0 odd degree nodes, we are golden. + if len(odd_nodes) == 0: + return True + + # If we have 2 odd degree nodes, we have to check if we can create an all even degree graph by doing one of the + # following: + # + # 1. Add an edge between the two nodes, as long as it's not a duplicate. + # 2. Add two edges connecting the nodes, with another node in between. + if len(odd_nodes) == 2: + [a, b] = odd_nodes + # Check if we can create an all even degree graph by connecting the nodes directly. + if not hasEdge((a, b)): + return True + + # Check if we can create an all even degree graph by connecting the nodes through a different node. Note + # that the nodes are numbered 1 through n inclusive. + for c in range(1, n + 1): + if c == a or c == b: + continue + + if not hasEdge((a, c)) and not hasEdge((b, c)): + return True + + # Otherwise, it can't be done. + return False + + # If we have 4 odd degree nodes, we can check if we can create an all even degree graph by connecting the nodes + # together. The nodes must be connected directly though; we can't connect them through some unrelated node as + # that requires two edges already, leaving the other two odd degree nodes stranded. + if len(odd_nodes) == 4: + [a, b, c, d] = odd_nodes + + # With 4 nodes, we can only connect them in 3 distinct ways: + pairs = [((a, b), (c, d)), ((a, c), (b, d)), ((a, d), (c, b))] + for pair in pairs: + (e1, e2) = pair + if not hasEdge(e1) and not hasEdge(e2): + return True + + # Otherwise, it can't be done. + return False + + # If we do NOT have 0, 2, or 4 odd degree nodes, there are no combination of 2 edges we can add to make this + # possible. + return False diff --git a/src/leetcode/graph/bus_routes.py b/src/leetcode/graph/bus_routes.py new file mode 100644 index 0000000..3273947 --- /dev/null +++ b/src/leetcode/graph/bus_routes.py @@ -0,0 +1,84 @@ +# DIFFICULTY: HARD +# ---------------- +# +# You are given an array routes representing bus routes where routes[i] is a bus route that the ith bus repeats +# forever. +# +# For example, if routes[0] = [1, 5, 7], this means that the 0th bus travels in the sequence: +# +# 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> ... +# +# ...forever. +# +# You will start at the bus stop source (You are not on any bus initially), and you want to go to the bus stop target. +# You can travel between bus stops by buses only. +# +# Return the least number of buses you must take to travel from source to target. Return -1 if it is not possible. +# +# See https://leetcode.com/problems/bus-routes +class Solution: + def numBusesToDestination(self, routes: list[list[int]], source: int, target: int) -> int: + """ + SOLUTION + -------- + + This seems to be just a BFS problem. The fact that buses repeat forever doesn't seem to be relevant because + there is no cost associated with waiting at a bus stop until the bus arrives; the only thing we are minimizing + is the number of buses we want to take. + + Building a graph and then running BFS on it naively seems to exceed execution time so we'll have to do something + more clever and dirty. + + COMPLEXITY + ---------- + + Time complexity is O(n * m) where n is the number of bus routes, and m is the number of stops per route. + + Space complexity is O(n * m). + """ + if source == target: + return 0 + + # Once no more stops are added, break out of the loop. + added = True + + # The number of buses we've taken so far. + result = 0 + + # Run a BFS from the source stop. We'll iterate through the routes and add stops to the visited set until we + # can no longer do so. If we haven't found our target stop by then we'll return -1. + visited: set[int] = set() + visited.add(source) + + while added: + # These are all routes that are reachable from currently visited stops, represented as individual stops in + # the route. + stops: list[int] = [] + added = False + result += 1 + + for i, route in enumerate(routes): + for stop in route: + if stop in visited: + # Collect all stops in the route associated with the current stop. + stops.extend(route) + + # We just added all the stops in the route, and we are about to mark them all as visited. Clear + # the routes array at i because we do not have to visit them again, so there's no need to + # iterate through them anymore. + routes[i] = [] + + # We've found new stops to visit; break out of the (inner) loop and move onto the next route. + # We'll note that stops have been visited, so we should continue the outer while loop. + added = True + break + + # Mark all the stops we've seen in this iteration as visited. + for stop in stops: + visited.add(stop) + + # Immediately return if we have ended up visiting our target destination. + if target in visited: + return result + + return -1 diff --git a/src/leetcode/graph/number_of_islands.py b/src/leetcode/graph/number_of_islands.py new file mode 100644 index 0000000..087e666 --- /dev/null +++ b/src/leetcode/graph/number_of_islands.py @@ -0,0 +1,55 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of +# islands. +# +# An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may +# assume all four edges of the grid are all surrounded by water. +# +# See https://leetcode.com/problems/number-of-islands +class Solution: + def numIslands(self, grid: list[list[str]]): + """ + SOLUTION + -------- + + We can execute DFS from every land mass to find connected land masses. Each connected land mass constitutes 1 + island. + + We can either store the coordinates (i, j) in a set to avoid visiting the same cell twice, or we can simply + write to the cell '0' for a visited land mass. The latter will also avoid visiting the same cell twice. We do + the latter because that's what most interviewers want to see. + + COMPLEXITY + ---------- + + Time complexity is O(v + e). + + Space complexity is O(v + e). We don't modify the input grid but the DFS stack frames do consume memory. + """ + count = 0 + + def dfs(i: int, j: int): + if not (0 <= i < len(grid)): + return + + if not (0 <= j < len(grid[i])): + return + + if grid[i][j] == "0": + return + + grid[i][j] = "0" + dfs(i, j + 1) + dfs(i, j - 1) + dfs(i - 1, j) + dfs(i + 1, j) + + for i in range(len(grid)): + for j in range(len(grid[i])): + if grid[i][j] == "1": + dfs(i, j) + count += 1 + + return count diff --git a/src/leetcode/graph/parallel_job_scheduling.py b/src/leetcode/graph/parallel_job_scheduling.py new file mode 100644 index 0000000..2853872 --- /dev/null +++ b/src/leetcode/graph/parallel_job_scheduling.py @@ -0,0 +1,69 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a list of tasks and their dependencies ['A:B,C', 'B:D,C,E', 'F:G'], return a list of parallelizable tasks in +# order. For example, in this situation [(D, C, E, F), (B, G), (A)] would be a valid order because all parallelizable +# tasks are grouped together. +# +# See https://leetcode.com/discuss/interview-question/353830/google-phone-screen-parallel-job-scheduling +from collections import defaultdict, deque + + +class Solution: + def parallelize(self, jobs: list[str]) -> list[list[str]]: + """ + SOLUTION + -------- + + Use BFS with Kahn's algorithm to perform topological sort. When processing nodes in parallel batches, reduce + the in degree count each time we process. + + COMPLEXITY + ---------- + + Time complexity is O(v + e) to build the graph, find zero in degree nodes, and performing topological sort, + where v is the number of tasks and e is the number of dependencies between them. + + Space complexity is O(v + e) as well. + """ + graph: dict[str, set[str]] = defaultdict(set) + in_degrees: dict[str, int] = defaultdict(int) + nodes: set[str] = set() + + # Build the graph and compute in degrees. + for job_spec in jobs: + parts = job_spec.split(":") + node = parts[0] + children = parts[1].split(",") if len(parts) > 1 else [] + nodes.add(node) + + for child in children: + graph[node].add(child) + in_degrees[child] += 1 + nodes.add(child) + + # Initialize BFS queue with zero in degree nodes. + queue: deque[str] = deque() + for node in nodes: + if in_degrees[node] == 0: + queue.append(node) + + # Process nodes level by level in parallel execution batches. + result: list[list[str]] = [] + while queue: + size = len(queue) + sub_result: list[str] = [] + + for _ in range(size): + node = queue.popleft() + sub_result.append(node) + + # Reduce the in degree for each child. + for child in graph[node]: + in_degrees[child] -= 1 + if in_degrees[child] == 0: + queue.append(child) + + result.append(sub_result) + + return result diff --git a/src/leetcode/graph/shortest_path_in_binary_matrix.py b/src/leetcode/graph/shortest_path_in_binary_matrix.py new file mode 100644 index 0000000..d9ebcb8 --- /dev/null +++ b/src/leetcode/graph/shortest_path_in_binary_matrix.py @@ -0,0 +1,98 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an n x n binary matrix grid, return the length of the shortest clear path in the matrix. If there is no clear +# path, return -1. +# +# A clear path in a binary matrix is a path from the top-left cell (i.e., (0, 0)) to the bottom-right cell +# (i.e., (n - 1, n - 1)) such that: +# +# - All the visited cells of the path are 0. +# - All the adjacent cells of the path are 8-directionally connected (i.e., they are different and they share an edge +# or a corner). +# - The length of a clear path is the number of visited cells of this path. +# +# See https://leetcode.com/problems/shortest-path-in-binary-matrix +from collections import deque + + +class Solution: + def shortestPathBinaryMatrix(self, grid: list[list[int]]) -> int: + """ + SOLUTION + -------- + + Can be solved using BFS. Just start from the top left cell and explore adjacent cells that have 0 value. + + Note the following: + + - We can explore all 8 directions, so we can go backwards. + - We might not find a path. + - The first cell might not even be visitable. + - With BFS the first path that reaches the end is guaranteed to be the shortest. + - If we had used DFS, we'd need to explicitly keep track of the shortest path because the first path to finish may + not be the shortest. + + COMPLEXITY + ---------- + + Time complexity is O(n^2). Each cell is visited at most once. On each visited a max of 8 directions are + considered. + + Space complexity is O(n^2) to store the visited cells and the queue. + """ + # The problem says that you MUST start at the top left, so if the top left cell is 1, you have nowhere to go. + if grid[0][0] == 1: + return -1 + + # Keep a set of visited cells for BFS. + visited: set[tuple[int, int]] = set() + + # Store a queue of (x, y, length). + queue: deque[tuple[int, int, int]] = deque() + + # Start at the (0, 0) cell with length = 1. + queue.append((0, 0, 1)) + while queue: + x, y, length = queue.popleft() + + # First check if this cell has been visited, and if it has, simply skip it. Store the cell as a string in a + # set, since we can't store the object directly. + if (x, y) in visited: + continue + + # Mark this cell as visited. + visited.add((x, y)) + + # Note that if we're at the very last cell, we can return it immediately. Because we are using BFS, the + # very first path that reaches the end is guaranteed to be the shortest. + # + # We don't have to store the path or otherwise distinguish between paths because all shortest paths have the + # same length. + if x == len(grid) - 1 and y == len(grid[0]) - 1: + return length + + # Note that we can actually move backwards (and we might be required to) since we can explore all 8 + # directions. Generate the coordinates of the 8 directions, then filter out the ones that are out of + # bounds, and explore the rest. + coordinates = [ + [x - 1, y], + [x, y - 1], + [x - 1, y - 1], + [x + 1, y], + [x, y + 1], + [x + 1, y + 1], + [x - 1, y + 1], + [x + 1, y - 1], + ] + for fx, fy in coordinates: + if not (0 <= fx < len(grid)): + continue + if not (0 <= fy < len(grid[0])): + continue + if grid[fx][fy] != 0: + continue + queue.append((fx, fy, length + 1)) + + # If we've exhausted all paths without reaching the end, that means we couldn't do it. + return -1 diff --git a/src/leetcode/graph/smallest_greater_multiple_made_of_two_digits.py b/src/leetcode/graph/smallest_greater_multiple_made_of_two_digits.py new file mode 100644 index 0000000..366f800 --- /dev/null +++ b/src/leetcode/graph/smallest_greater_multiple_made_of_two_digits.py @@ -0,0 +1,78 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given three integers, k, digit1, and digit2, you want to find the smallest integer that is: +# +# - Larger than k, +# - A multiple of k, and +# - Comprised of only the digits digit1 and/or digit2. +# +# Return the smallest such integer. If no such integer exists or the integer exceeds the limit of a signed 32-bit +# integer (2^31 - 1), return -1. +# +# See https://leetcode.com/problems/smallest-greater-multiple-made-of-two-digits +from collections import deque + + +class Solution: + def findInteger(self, k: int, digit1: int, digit2: int) -> int: + """ + SOLUTION + -------- + + In order to find our target number, we start with the first digit and generate all possible 2 digit numbers. + Then from there we generate all possible 3 digit numbers, and so on. We also go back to checking the second + digit, generating 2 digit, 3 digit, etc numbers. + + This can be done via a graph search algorithm. However, since we want to find the smallest such number, we + should use BFS instead of DFS. + + COMPLEXITY + ---------- + """ + queue: deque[str] = deque() + seen: set[str] = set() + + # We always want to start generating numbers from the smaller digit first, so let's rearrange them so that the + # smaller digit is digit1. + (d1, d2) = (digit1, digit2) if digit1 < digit2 else (digit2, digit1) + + # Convert the digits to strings; this will be useful in building our number later. + s1 = str(d1) + s2 = str(d2) + + # For the first digits, do not push a 0, because generating a number starting at 0 is the same as generating a + # number using the rest of the digits. + if d1 != 0: + queue.append(s1) + seen.add(s1) + + if d2 != 0 and d1 != d2: + queue.append(s2) + seen.add(s2) + + max_value = 2**31 - 1 + while queue: + s = queue.popleft() + + # From the problem description, this is the max integer we should consider. + value = int(s) + if value > max_value: + continue + + # Check if this number is both larger than k and a multiple of k; if so we have reached our target. + if value > k and value % k == 0: + return value + + # Otherwise, use the digits to construct our frontier nodes. We know that by adding in this order, the + # smaller number gets added first (since digit1 < digit2). + next1 = s + s1 + next2 = s + s2 + if next1 not in seen: + queue.append(next1) + seen.add(next1) + if next2 not in seen: + queue.append(next2) + seen.add(next2) + + return -1 diff --git a/src/leetcode/graph/word_search.py b/src/leetcode/graph/word_search.py new file mode 100644 index 0000000..f99706c --- /dev/null +++ b/src/leetcode/graph/word_search.py @@ -0,0 +1,69 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an m x n grid of characters board and a string word, return true if word exists in the grid. +# +# The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or +# vertically neighboring. The same letter cell may not be used more than once. +# +# See https://leetcode.com/problems/word-search +class Solution: + def exist(self, board: list[list[str]], word: str) -> bool: + """ + SOLUTION + -------- + + This is just DFS; no need to build a prefix trie, in comparison to word search ii. BFS isn't going to work as + well because BFS will generate all possible words incrementally, whereas we want to just stop if we've found our + word. + + COMPLEXITY + ---------- + + Time complexity is O(m * n * 4^k) where m and n are the dimensions of the board, and k is the length of the + longest word. + + Space complexity is O(k). + """ + if len(word) == 0: + return False + + def dfs(row: int, column: int, i: int) -> bool: + if i == len(word): + return True + + # Only continue down this path if the row is within bounds. + if not (0 <= row < len(board)): + return False + + # Only continue down this path if the column is within bounds. + if not (0 <= column < len(board[row])): + return False + + # Only continue down this path if there's a match. + c = board[row][column] + if c != word[i]: + return False + + # Save this row/column's value so we can mark it as visited; we'll use a sentinel value to do so. + board[row][column] = "#" + + found = ( + dfs(row, column - 1, i + 1) + or dfs(row, column + 1, i + 1) + or dfs(row - 1, column, i + 1) + or dfs(row + 1, column, i + 1) + ) + + board[row][column] = c + + return found + + for i in range(len(board)): + for j in range(len(board[i])): + # Only start a DFS search if the word begins with the character we are looking at. + if board[i][j] == word[0]: + if dfs(i, j, 0): + return True + + return False diff --git a/src/leetcode/graph/word_search_ii.py b/src/leetcode/graph/word_search_ii.py new file mode 100644 index 0000000..bd7a030 --- /dev/null +++ b/src/leetcode/graph/word_search_ii.py @@ -0,0 +1,104 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Given an m x n board of characters and a list of strings words, return all words on the board. +# +# Each word must be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or +# vertically neighboring. The same letter cell may not be used more than once in a word. +# +# See https://leetcode.com/problems/word-search-ii +class TrieNode: + def __init__(self) -> None: + self.children: dict[str, TrieNode] = {} + self.isTerminated = False + + def add(self, word: str) -> None: + node: TrieNode = self + for c in word: + if c not in node.children: + node.children[c] = TrieNode() + node = node.children[c] + node.isTerminated = True + + +class Solution: + def findWords(self, board: list[list[str]], words: list[str]) -> list[str]: + """ + SOLUTION + -------- + + This problem looks like it can be solved with a prefix trie. + + 1. Construct a prefix trie from the list of words. + 2. Run DFS from each board cell, choosing to continue the search if the sequence seen so far is a valid prefix + in the trie. + 3. Collect valid words from each DFS and return the result. + + Easy peasy. + + COMPLEXITY + ---------- + + Time complexity is O(w * k + m * n * 4^k) where w is the number of words, k is the maximum word length, m and n + are the dimensions of the board. Building the trie takes O(w * k). For DFS, we perform DFS on every cell (of + which there are m * n cells), and the length of each word in a DFS call is L, but there is a branching factor of + 4. + + Space complexity is O(w * k + k) due to the trie storage and the DFS stack. + """ + # Construct the prefix trie for efficient lookup. + root = TrieNode() + for word in words: + root.add(word) + + # Define a DFS function to run over each cell. We could potentially make the DFS more efficient by creating the + # visited matrix once and then mutating it over each DFS visitation. + result: set[str] = set() + + def dfs(node: TrieNode, visited: set[tuple[int, int]], word: str, row: int, column: int) -> None: + # If we've reached the end of a word, do not return right away; we might be able to reach a longer word if + # we keep going. Record the one we've seen so far though. + if node.isTerminated: + result.add(word) + + # The frontier nodes could be any direction up, down, left, or right, but not diagonal. + frontier = [(row + 1, column), (row - 1, column), (row, column + 1), (row, column - 1)] + for x, y in frontier: + # Do not consider frontier cells that are out of bounds. + if not (0 <= x < len(board)): + continue + + if not (0 <= y < len(board[x])): + continue + + # Do not consider visited cells. + if (x, y) in visited: + continue + + # Do not consider cells that do not form a word. + c = board[x][y] + if c not in node.children: + continue + + # Continue DFS using the next TrieNode. + next = node.children[c] + + # Mark this cell as visited, and continue the DFS search. After the search completes, we should unmark + # visited cells (unlike regular DFS) because a different direction may yield different words, so we'll + # want reconsider these previously visited cells. + visited.add((x, y)) + dfs(next, visited, word + c, x, y) + visited.remove((x, y)) + + # Perform DFS on every cell. + for i in range(len(board)): + for j in range(len(board[i])): + c = board[i][j] + if c not in root.children: + continue + + node = root.children[c] + visited = {(i, j)} + dfs(node, visited, c, i, j) + + return list(result) diff --git a/src/leetcode/heap/README.md b/src/leetcode/heap/README.md new file mode 100644 index 0000000..cfa8f46 --- /dev/null +++ b/src/leetcode/heap/README.md @@ -0,0 +1,11 @@ +# Heaps + +## Common Phrases + +These phrases indicate a heap might be useful: + +- k closest, k largest, k smallest +- top k elements +- max k elements, min k elements +- priority order +- sorted order with constraints diff --git a/src/leetcode/heap/common/list_node.py b/src/leetcode/heap/common/list_node.py new file mode 100644 index 0000000..bfe706e --- /dev/null +++ b/src/leetcode/heap/common/list_node.py @@ -0,0 +1,6 @@ +# This class definition comes from the problem itself and we cannot change it, or else our submission will not be +# accepted. +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next diff --git a/src/leetcode/heap/furthest_building_you_can_reach.py b/src/leetcode/heap/furthest_building_you_can_reach.py new file mode 100644 index 0000000..33ea4bd --- /dev/null +++ b/src/leetcode/heap/furthest_building_you_can_reach.py @@ -0,0 +1,73 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an integer array heights representing the heights of buildings, some bricks, and some ladders. +# +# You start your journey from building 0 and move to the next building by possibly using bricks or ladders. +# +# While moving from building i to building i+1 (0-indexed), +# +# - If the current building's height is greater than or equal to the next building's height, you do not need a ladder +# or bricks. +# - If the current building's height is less than the next building's height, you can either use one ladder or +# (h[i+1] - h[i]) bricks. +# +# Return the furthest building index (0-indexed) you can reach if you use the given ladders and bricks optimally. +# +# See https://leetcode.com/problems/furthest-building-you-can-reach +from heapq import heappop, heappush + + +class Solution: + def furthestBuilding(self, heights: list[int], bricks: int, ladders: int) -> int: + """ + SOLUTION + -------- + + You can't do this problem using sliding window or two pointer technique easily. + + Sliding window requires finding some value in a fixed size window. Here we don't know what the window size will + be. + + Two pointer technique can solve this problem but not optimally. We need to dynamically adjust our resources of + bricks or ladder at each step. + + A greedy algorithm would be better suited to solve this problem. + + COMPLEXITY + ---------- + + Time complexity is O(n log n) time where n is number of buildings. + + Space complexity is O(n) in worst case. + """ + min_heap: list[int] = [] + + for i in range(len(heights) - 1): + # Calculate the delta that we need to "jump" across. + delta = heights[i + 1] - heights[i] + + # A negative or zero height jump can be traversed by jumping across or down. + if delta <= 0: + continue + + # A non-negative delta means we must use either bricks or a ladder to jump across. + heappush(min_heap, delta) + + # Because the ladders can traverse any height, we'll "save" the ladders here to use on the the biggest + # deltas. As long as we have ladders remaining we can maintain a heap size of deltas that we'll + # eventually use ladders on. + if len(min_heap) <= ladders: + continue + + # If we have more elements on the heap than we do ladders, then SOME of these deltas need to be traversed + # using bricks instead of ladders. Let's pop the smallest delta out and use bricks on it. + smallest = heappop(min_heap) + bricks -= smallest + + # If we've run out of bricks, that's it. We can can only reach the building at position i. + if bricks < 0: + return i + + # If we got here, that means we can get to the very last building! + return len(heights) - 1 diff --git a/src/leetcode/heap/k_closest_points_to_origin.py b/src/leetcode/heap/k_closest_points_to_origin.py new file mode 100644 index 0000000..f26a01c --- /dev/null +++ b/src/leetcode/heap/k_closest_points_to_origin.py @@ -0,0 +1,72 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the +# k closest points to the origin (0, 0). +# +# The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x1 - x2)2 + (y1 - y2)2). +# +# You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in). +# +# See https://leetcode.com/problems/k-closest-points-to-origin +import heapq as hq +import math + + +def heappush(max_heap: list[tuple[float, list[int]]], item: tuple[float, list[int]]) -> None: + # Simulate a max heap by negating the distance. When storing a tuple in the heap, Python will sort by tuple + # ordering, so will first sort by the first element, then the second. + d, p = item + hq.heappush(max_heap, (-d, p)) + + +def heappop(max_heap: list[tuple[float, list[int]]]) -> tuple[float, list[int]]: + d, p = hq.heappop(max_heap) + return (-d, p) + + +class Solution: + def kClosest(self, points: list[list[int]], k: int) -> list[list[int]]: + """ + SOLUTION + -------- + + The request for "k-closest" indicates you'll want a heap. Using a either a min heap or max heap will work. + + With a min heap, just jam all the points on the heap and then slice the first k elements. This requires storing + all the points on the heap. + + With a max heap, you can jam points on the heap until you have k elements. Then, when you have a new point, + compare it to the max element on the heap. If it's closer, pop off the max element and jam it on the heap; + otherwise, ignore it. + + The max heap approach can be more efficient, so we'll go that route. + + COMPLEXITY + ---------- + + Time complexity is O(n log k) with a max heap, which will be faster when k is much smaller than n. If we go + with the min heap approach, it's O(n log n). + + Space complexity is O(n). + """ + + # Calculates the distance to the origin. Technically, since we're only using the distance to do comparison, we + # don't need to take the square root since they are all going to compare the same square root or not. I'm + # leaving it here for correctness. + def distance(p: list[int]) -> float: + [x, y] = p + return math.sqrt(x**2 + y**2) + + # Store max heap of (distance, point). LeetCode gives us the points as list of [x, y]. + max_heap: list[tuple[float, list[int]]] = [] + for p in points: + d = distance(p) + heappush(max_heap, (d, p)) + + # If we have more than k elements, remove the farthest point. + if len(max_heap) > k: + heappop(max_heap) + + # Return the k closest points, throwing away the distance. + return [p for _, p in max_heap] diff --git a/src/leetcode/heap/kth_largest_element.py b/src/leetcode/heap/kth_largest_element.py new file mode 100644 index 0000000..c5378a0 --- /dev/null +++ b/src/leetcode/heap/kth_largest_element.py @@ -0,0 +1,57 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums and an integer k, return the kth largest element in the array. +# +# Note that it is the kth largest element in the sorted order, not the kth distinct element. +# +# Can you solve it without sorting? +# +# See https://leetcode.com/problems/kth-largest-element-in-an-array +from heapq import heappop, heappush + + +class Solution: + def findKthLargest(self, nums: list[int], k: int) -> int: + """ + SOLUTION + -------- + + You can do this with quick select, similar to quick sort. The idea is to eliminate half of the search space at + each step. You pick a pivot element (using heuristics, or even randomly), then partition the array into two + halves: left half has elements smaller than the pivot, right half has elements larger than the pivot. + + After the partition, you can check if the pivot is the (n - k)th element. If it is, you're done. If not, + recurse into the left or right partitions depending on the pivot's position. + + That said, the leetcode questions seem to be crafted to ruin poor pivot index choices. + + The other way to do this is to use a heap. You can do this an efficient way or an inefficient way. The + inefficient way involves jamming all the elements onto a max heap, then popping off k elements to get the kth + largest element. + + The efficient way is to maintain a min heap and only ever allow k elements on the heap. This way, each time you + pop an element off the heap, you're left with ever larger elements. Since the heap size is k, the kth largest + element will be at the top of the heap. + + COMPLEXITY: + + Time complexity is O(n log k) where n is the length of the array. + + Space complexity is O(k). + """ + min_heap: list[int] = [] + + for n in nums: + heappush(min_heap, n) + + # Now we have the k + 1 smallest elements we've seen so far. If we get a new element, pop from the + # heap, which will leave us with larger elements in the heap. + # + # At the end, we'll pop off all the smallest elements, leaving us with the top k largest elements. + if len(min_heap) > k: + heappop(min_heap) + + # This is a min heap, so the smallest element is at the front. However, there are (k - 1) larger elements in + # elsewhere in the heap. That makes the front element the kth largest element. + return min_heap[0] diff --git a/src/leetcode/heap/merge_k_sorted_lists.py b/src/leetcode/heap/merge_k_sorted_lists.py new file mode 100644 index 0000000..2a85fa3 --- /dev/null +++ b/src/leetcode/heap/merge_k_sorted_lists.py @@ -0,0 +1,67 @@ +# DIFFICULTY: HARD +# ---------------- +# +# You are given an array of k linked-lists lists, each linked-list is sorted in ascending order. +# +# Merge all the linked-lists into one sorted linked-list and return it. +# +# See https://leetcode.com/problems/merge-k-sorted-lists +from heapq import heappop, heappush +from leetcode.heap.common.list_node import ListNode + + +class Solution: + def mergeKLists(self, lists: list[ListNode | None]) -> ListNode | None: + """ + SOLUTION + -------- + + Instead of only 2 lists, we have to merge k lists. The naive way is to do a linear scan of the head of every + list to find the smallest element then create a new list node with that element and append it to the result + list. + + However, we can do better by using a heap to store the head of each list so we can always find the minimum value + without a linear scan. Additionally, the problem does not say we cannot reuse the input lists, so we do not + have to create new list nodes. + + COMPLEXITY + ---------- + + Time complexity is O(n log k), where n is the number of nodes and there are k lists, so each enqueuing operation + will take O(log k) time. However, we are going to do this for every node in the list, so the total time + complexity is O(n log k). The naive approach with a linear scan would be O(n * k). + + Space complexity is O(k) because we are storing the head of each list in the heap, and we do not create new list + nodes. + """ + # Use a heap to store the head of each list so we can always find the minimum value to add to the result list + # without resorting to a linear scan. + # + # Store (value, id, ListNode) tuples in the heap because we need to handle dupes. In case of dupes, Python will + # try to compare the second value of the tuple. If the second value is a ListNode, it won't know how to compare + # it. To fix this, store some unique ID for the ListNode as the second value so the third value will never be + # compared. + min_heap: list[tuple[int, int, ListNode]] = [] + for head in lists: + if head: + heappush(min_heap, (head.val, id(head), head)) + + # As we keep advancing the current node, we'll lose track of the head. Let's have a sentinel node so that we + # can always keep track of where the list begins. + sentinel = ListNode(-1) + current = sentinel + + while min_heap: + (_, _, smallest) = heappop(min_heap) + + # Advance the current node and push the smallest element in. + current.next = smallest + current = current.next + + # Now, advance the smallest node to the next value. + if smallest.next: + next = smallest.next + heappush(min_heap, (next.val, id(next), next)) + + # The sentinel node will point to the head of the list, so we did not lose it. + return sentinel.next diff --git a/src/leetcode/heap/minimize_deviation_in_array.py b/src/leetcode/heap/minimize_deviation_in_array.py new file mode 100644 index 0000000..f1c4612 --- /dev/null +++ b/src/leetcode/heap/minimize_deviation_in_array.py @@ -0,0 +1,90 @@ +# DIFFICULTY: HARD +# ---------------- +# +# You are given an array nums of n positive integers. +# +# You can perform two types of operations on any element of the array any number of times: +# +# If the element is even, divide it by 2. +# +# For example, if the array is [1,2,3,4], then you can do this operation on the last element, and the array will be +# [1,2,3,2]. +# +# If the element is odd, multiply it by 2. +# +# For example, if the array is [1,2,3,4], then you can do this operation on the first element, and the array will be +# [2,2,3,4]. +# +# The deviation of the array is the maximum difference between any two elements in the array. +# +# Return the minimum deviation the array can have after performing some number of operations. +# +# See https://leetcode.com/problems/minimize-deviation-in-array +import heapq as hq +import math + + +def heappush(max_heap: list[int], item: int) -> None: + # Simulate a max heap by negating the value. + hq.heappush(max_heap, -item) + + +def heappop(max_heap: list[int]) -> int: + return -hq.heappop(max_heap) + + +class Solution: + def minimumDeviation(self, nums: list[int]): + """ + SOLUTION + -------- + + To solve this problem, we have to multiply elements to make them bigger, or divide elements to make them + smaller. We continue to do this until the minimum and maximum elements are as close as possible. + + To make this problem easier, we try to minimize the deviation by making bigger numbers smaller, instead of + simultaneously trying to make numbers bigger and smaller. To do this, we multiply all odd numbers by 2, so that + they all become even. Afterwards, we can choose to perform a division or not to make it smaller. + + COMPLEXITY + ---------- + + Time complexity is O(n log m) where n is the number of elements in nums, and m is the number of times we have to + halve a value. + + Space complexity is O(n). + """ + max_heap: list[int] = [] + + # Normalize all the numbers so that they are even. Now we can consider only division as a way to make numbers + # smaller and closer to each other. If a previously odd number was too big, we will eventually resize it smaller + # by division is necessary. + for i, value in enumerate(nums): + if value % 2 == 1: + value *= 2 + nums[i] = value + + heappush(max_heap, value) + + minimum = min(nums) + deviation = math.inf + + # Calculate the current deviation using the max element of the array, then half the max element and return it to + # theheap. Then repeat to keep bringing the deviation down. + while True: + maximum = heappop(max_heap) + deviation = min(deviation, maximum - minimum) + + # Oh no! If the max value was odd, we can't halve it and re-insert into the heap. This means that whatever + # the deviation is now, we are stuck. Halving smaller even values will not change the deviation. + if maximum % 2 == 1: + break + + # Halve the max value and return it to the heap for re-processing. + value = int(maximum / 2) + heappush(max_heap, value) + + # Update the minimum value in case we've changed the minimum value by manipulating the maximum value. + minimum = min(value, minimum) + + return int(deviation) diff --git a/src/leetcode/heap/number_of_orders_in_the_backlog.py b/src/leetcode/heap/number_of_orders_in_the_backlog.py new file mode 100644 index 0000000..def3bd0 --- /dev/null +++ b/src/leetcode/heap/number_of_orders_in_the_backlog.py @@ -0,0 +1,135 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given a 2D integer array orders, where each orders[i] = [pricei, amounti, orderTypei] denotes that amounti +# orders have been placed of type orderTypei at the price pricei. The orderTypei is: +# +# 0 if it is a batch of buy orders, or +# 1 if it is a batch of sell orders. +# +# Note that orders[i] represents a batch of amounti independent orders with the same price and order type. All orders +# represented by orders[i] will be placed before all orders represented by orders[i+1] for all valid i. +# +# There is a backlog that consists of orders that have not been executed. The backlog is initially empty. When an order +# is placed, the following happens: +# +# If the order is a buy order, you look at the sell order with the smallest price in the backlog. If that sell order's +# price is smaller than or equal to the current buy order's price, they will match and be executed, and that sell order +# will be removed from the backlog. Else, the buy order is added to the backlog. +# +# Vice versa, if the order is a sell order, you look at the buy order with the largest price in the backlog. If that +# buy order's price is larger than or equal to the current sell order's price, they will match and be executed, and +# that buy order will be removed from the backlog. Else, the sell order is added to the backlog. +# +# Return the total amount of orders in the backlog after placing all the orders from the input. Since this number can +# be large, return it modulo 10^9 + 7. +# +# See https://leetcode.com/problems/number-of-orders-in-the-backlog +from heapq import heappop, heappush + + +class Solution: + def getNumberOfBacklogOrders(self, orders: list[list[int]]) -> int: + """ + SOLUTION + -------- + + This problem looks like it can be solved by maintaining a sorted list of buy orders and sell orders (aka heaps). + For a sell order, you want buy orders in largest to smallest (max heap). For a buy order, you want sell orders + from smallest to largest (min heap). + + COMPLEXITY + ---------- + + Time complexity is O(n log n) because we are using heaps to maintain the order of the orders. + + Space complexity is O(n). + """ + # We want to match sellers with the largest price in the buy orders, so we want a max heap. Pro-tip: Negate the + # values here. + buys_max_heap: list[tuple[int, int]] = [] + # We want to match buyers with the lowest price in the sell orders, so we want a min heap. + sells_min_heap: list[tuple[int, int]] = [] + MOD = 10**9 + 7 + + def handleBuy(buy_price: int, buy_amount: int): + while buy_amount > 0: + # If we don't have any sellers, we can't match up a sale. So we should push the order into the buys + # backlog. + # + # Note that the buys backlog is a max heap, so we have to negate the price. + if not sells_min_heap: + heappush(buys_max_heap, (-buy_price, buy_amount)) + return + + # If we do have a seller, we can check the lowest sale price. If the lowest sale price is still too + # high, we can't match up a sale. So we should push the order into the buys backlog anyways. + # + # Note that the buys backlog is a max heap, so we have to negate the price. + sell_price, sell_amount = sells_min_heap[0] + if buy_price < sell_price: + heappush(buys_max_heap, (-buy_price, buy_amount)) + return + + # Here the buyer and seller have agreed to a sale, so let's execute the order. We can buy only as many + # shares as the seller is selling at that price. + delta = min(buy_amount, sell_amount) + buy_amount -= delta + sell_amount -= delta + sells_min_heap[0] = (sell_price, sell_amount) + + # If the seller isn't offering any more shares, then pop their order from the sells backlog. + if sell_amount == 0: + heappop(sells_min_heap) + + # If the buyer doesn't want any more shares, we can stop processing the loop. + if buy_amount == 0: + return + + def handleSell(sell_price: int, sell_amount: int): + while sell_amount > 0: + # If we don't have any buyers, we can't match up a sale. So we should push the order into the sells backlog. + if not buys_max_heap: + heappush(sells_min_heap, (sell_price, sell_amount)) + return + + # If we do have a buyer, we can check the highest buy price. If the highest buy price is still too low, + # we can't match up a sale. So we should push the order into the buys backlog anyways. + # + # Note that this is a max heap so we need to negate the value. + buy_price, buy_amount = buys_max_heap[0] + buy_price *= -1 + if sell_price > buy_price: + heappush(sells_min_heap, (sell_price, sell_amount)) + return + + # Here the buyer nad seller have agreed to a sale, so let's execute the order. We can only buy as many shares + # as the seller is willing to sell. + delta = min(buy_amount, sell_amount) + buy_amount -= delta + sell_amount -= delta + buys_max_heap[0] = (-buy_price, buy_amount) + + # If the buyer isn't willing to buy any more shares, then pop their order from the buys backlog. + if buy_amount == 0: + heappop(buys_max_heap) + + # If the seller doesn't want any more shares, stop processing the loop. + if sell_amount == 0: + return + + for price, amount, order_type in orders: + if amount == 0: + continue + + if order_type == 0: + handleBuy(price, amount) + else: + handleSell(price, amount) + + total = 0 + for _, amount in buys_max_heap: + total = (total + amount) % MOD + for _, amount in sells_min_heap: + total = (total + amount) % MOD + return total diff --git a/src/leetcode/heap/task_scheduler.py b/src/leetcode/heap/task_scheduler.py new file mode 100644 index 0000000..ca12fe5 --- /dev/null +++ b/src/leetcode/heap/task_scheduler.py @@ -0,0 +1,86 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an array of CPU tasks, each represented by letters A to Z, and a cooling time, n. Each cycle or +# interval allows the completion of one task. Tasks can be completed in any order, but there's a constraint: identical +# tasks must be separated by at least n intervals due to cooling time. +# +# ​Return the minimum number of intervals required to complete all tasks. +# +# See https://leetcode.com/problems/task-scheduler +from collections import defaultdict, deque +import heapq as hq + + +def heappush(max_heap: list[int], item: int) -> None: + # Simulate a max heap by negating the value. + hq.heappush(max_heap, -item) + + +def heappop(max_heap: list[int]) -> int: + return -hq.heappop(max_heap) + + +class Solution: + def leastInterval(self, tasks: list[str], n: int) -> int: + """ + SOLUTION + -------- + + It's not necessary to return an execution order, only the minimum number of cycles/intervals required to + complete all of the tasks. + + Note that either the most frequent task or the number of tasks will dictate the number of cycles required. + First let's examine the case where we have a very frequent task that requires multiple cooling off periods. + + Let's suppose that task A appears 10 times, and task B appears 9 times, and some other tasks appear with lower + frequency. No matter what, the 10 executions of task A will require n cycles of gaps between them, except for + the last execution of task A. This means that there will be n cycles of idle slots between the executions of + task A. But because we have 9 executions that need to spaced out (not the last), there are (10 - 1) * n empty + cycles between A, for the other tasks to execute. + + Here, even though task B appears 9 times, we can fit task B comfortably in between the 9 cycles of task A. + This, however, doesn't quite work if there are a huge number of tasks; if there are 10 task A's and 9 task B's, + but 50 other uniquely named tasks, then we will still take 50 cycles if (10 - 1) * n is a smaller number. + + COMPLEXITY + ---------- + + Time complexity is O(m + n) where m is the total number of tasks. While we do perform O(k log k) operations to + build the heap, k is always at most 26 characters, so that is still effectively constant time. Likewise, any + log k operations are effectively constant time as well. + + Space complexity is O(n). + """ + # Create a map of task to frequency. + mapping: dict[str, int] = defaultdict(int) + for task in tasks: + mapping[task] += 1 + + # Create a max heap of frequency. + max_heap: list[int] = [] + for freq in mapping.values(): + heappush(max_heap, freq) + + # Create a queue of [freq, cycle] for cooldown tasks. + queue: deque[tuple[int, int]] = deque() + cycle = 0 + + # Process tasks. + while max_heap or queue: + if queue: + (f, c) = queue[0] + if c == cycle: + queue.popleft() + heappush(max_heap, f) + + if max_heap: + f = heappop(max_heap) + f -= 1 + + if f != 0: + queue.append((f, cycle + n + 1)) + + cycle += 1 + + return cycle diff --git a/src/leetcode/hybrid/lru_cache.py b/src/leetcode/hybrid/lru_cache.py new file mode 100644 index 0000000..7118fa3 --- /dev/null +++ b/src/leetcode/hybrid/lru_cache.py @@ -0,0 +1,107 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Design a data structure that follows the constraints of a Least Recently Used (LRU) cache. +# +# Implement the LRUCache class: +# +# LRUCache(int capacity) +# Initialize the LRU cache with positive size capacity. +# int get(int key) +# Return the value of the key if the key exists, otherwise return -1. +# void put(int key, int value) +# Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of +# keys exceeds the capacity from this operation, evict the least recently used key. +# +# The functions get and put must each run in O(1) average time complexity. +# +# See https://leetcode.com/problems/lru-cache +from typing import cast + + +class Node: + def __init__(self, key: int, value: int) -> None: + self.key = key + self.value = value + self.previous: "Node" = cast("Node", None) + self.next: "Node" = cast("Node", None) + + +class LRUCache: + def __init__(self, capacity: int) -> None: + """ + SOLUTION + -------- + + This problem can be solved using a combination of a doubly linked list and a hashmap. + + COMPLEXITY + ---------- + + Time complexity is O(1) for all operations. + + Space complexity is O(n) where n is the number of keys in the data structure. + """ + self.mapping: dict[int, Node] = {} + self.capacity = capacity + + # Create two sentinel nodes to simplify the logic. + self.head = Node(-1, -1) + self.tail = Node(-1, -1) + self.head.next = self.tail + self.tail.previous = self.head + + def get(self, key: int) -> int: + if key not in self.mapping: + return -1 + + node = self.mapping[key] + self.__updateTimestamp(node) + return node.value + + def put(self, key: int, value: int) -> None: + if key in self.mapping: + node = self.mapping[key] + node.value = value + self.__updateTimestamp(node) + return + + # If the key isn't in the map, we need to add it. + node = Node(key, value) + self.mapping[key] = node + self.__addLeftNode(node) + + # If we've exceeded the capacity, remove the least recently used key. + if len(self.mapping) > self.capacity: + node = self.tail.previous + self.mapping.pop(node.key) + self.__removeNode(node) + + def __removeNode(self, node: Node) -> None: + # Line up the node pointers in the desired order: a -> b -> c. + b = node + a = b.previous + c = b.next + + a.next = c + c.previous = a + + def __addLeftNode(self, node: Node) -> None: + # Line up the node pointers in the desired order: a -> b -> c. + a = self.head + b = node + c = a.next + + # Update the pointers for the node itself. + b.previous = a + b.next = c + + # Update the pointers for the surrounding nodes. + a.next = b + c.previous = b + + def __updateTimestamp(self, node: Node) -> None: + # Update the timestamp by moving the node to the front of the list. Moving to the front of the list can be + # simulated by removing the node then adding it to the front. + self.__removeNode(node) + self.__addLeftNode(node) diff --git a/src/leetcode/hybrid/max_stack.py b/src/leetcode/hybrid/max_stack.py new file mode 100644 index 0000000..2002979 --- /dev/null +++ b/src/leetcode/hybrid/max_stack.py @@ -0,0 +1,169 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Design a max stack data structure that supports the stack operations and supports finding the stack's maximum element. +# +# Implement the MaxStack class: +# +# - MaxStack() Initializes the stack object. +# - void push(int x) Pushes element x onto the stack. +# - int pop() Removes the element on top of the stack and returns it. +# - int top() Gets the element on the top of the stack without removing it. +# - int peekMax() Retrieves the maximum element in the stack without removing it. +# - int popMax() Retrieves the maximum element in the stack and removes it. If there is more than one maximum element, +# only remove the top-most one. +# +# You must come up with a solution that supports O(1) for each top call and O(logn) for each other call. +# +# See https://leetcode.com/problems/max-stack +import heapq as hq + + +def heappush(max_heap: list[tuple[int, int, "StackNode"]], item: tuple[int, int, "StackNode"]) -> None: + # Simulate a max heap by negating the values. When storing a tuple in the heap, Python will sort by tuple ordering. + value, key, node = item + hq.heappush(max_heap, (-value, -key, node)) + + +def heappop(max_heap: list[tuple[int, int, "StackNode"]]) -> tuple[int, int, "StackNode"]: + value, key, node = hq.heappop(max_heap) + return (-value, -key, node) + + +class StackNode: + # The problem itself states that the smallest value we can get is -10^7. This definition avoids having to declare + # the type of a node key/value to be float for float("-inf"). + MIN_INT_VALUE = -(10**7) - 1 + + def __init__(self, key: int, value: int): + self.key = key + self.value = value + self.previous: "StackNode | None" = None + self.next: "StackNode | None" = None + + +class MaxStack: + def __init__(self): + """ + SOLUTION + -------- + + Use a heap and a doubly linked list to represent the max stack. Most operations can be performed in O(1) or + O(log n). The only challenge is that when popping from the stack, it's easy to remove from the end of the + linked list, but it's not easy to remove from the middle of the max heap. + + Instead of removing from the max heap immediately, defer the deletion until we perform a peekMax or popMax, and + check if processing the deferred deletions are required. If so, perform them there. + + For the purposes of this solution, we won't consider edge cases like popping from an empty stack. This keeps + the solution simple. + + COMPLEXITY + ---------- + + Time complexity is O(1) for top and O(log n) for each other call. + + Space complexity is O(n). + """ + + # Because the stack could have multiple duplicate elements, use a unique ID for each node. + self.keys = 0 + # Because deleting from the middle of a heap is hard, keep track of a set of deletions while popping and defer + # them for later. + self.deleted = set() + # Use a max heap to keep track of max elements. The heap will have a tuple of (value, key, node) because we + # want to support dupes, so have the heap order by value first, then the key. + self.max_heap = [] + # Setup sentinel values for the head and tail so we don't have to check for nulls. + self.head = StackNode(StackNode.MIN_INT_VALUE, StackNode.MIN_INT_VALUE) + self.tail = StackNode(StackNode.MIN_INT_VALUE, StackNode.MIN_INT_VALUE) + self.head.next = self.tail + self.tail.previous = self.head + + def push(self, value: int) -> None: + # Add this new node to the linked list. + node = StackNode(self.keys, value) + self.keys += 1 + + # Push onto the heap; we want to store the node only, but Python sorts by tuple ordering, so order by the value + # first, then the key as a tie breaker. + heappush(self.max_heap, (value, node.key, node)) + + # Now add this node to the end of the linked list. + a = self.tail.previous + b = node + c = self.tail + self.__insertNode(a, b, c) + + def pop(self) -> int: + node = self.tail.previous + assert node is not None + + # Remove the last element of the linked list. + self.__deleteNode(node) + + # We also need to remove this element from the heap, but this will be difficult as we can't remove arbitrary + # elements from the middle of the heap. Instead, we will note the deletion and defer it. + self.deleted.add(node.key) + return node.value + + def top(self) -> int: + assert self.tail.previous is not None + return self.tail.previous.value + + def peekMax(self) -> int: + # Since peekMax is allowed to run in O(log n), perform the deferred heap deletions from any previous pops. + self.__deleteMax() + + # Return the front of the of heap. + (_, _, node) = self.max_heap[0] + return node.value + + def popMax(self) -> int: + # Since popMax is allowed to run in O(log n), perform the deferred heap deletions from any previous pops. + self.__deleteMax() + + # Pop off the front of the heap, which is efficient to do so here (we don't have to delete from the middle). + (_, _, node) = heappop(self.max_heap) + + # Delete from the linked list, which is also efficient to do here. + self.__deleteNode(node) + return node.value + + def __deleteNode(self, b: StackNode | None): + assert b is not None + + a = b.previous + c = b.next + + # This shouldn't be necessary, but mypy cannot tell if these values are not None. + if a and c: + a.next = c + c.previous = a + + def __insertNode(self, a: StackNode | None, b: StackNode, c: StackNode | None): + assert a is not None + assert b is not None + assert c is not None + + # Update the pointers around the node. + a.next = b + c.previous = b + + # Update the pointers for the node itself. + b.previous = a + b.next = c + + def __deleteMax(self): + while self.max_heap: + (_, _, node) = self.max_heap[0] + + # If the front of the heap is actually something we have deleted, then any peekMax or popMax operation will + # return the wrong value. If that is the case, remove them from the heap in an efficient way right now. + if node.key in self.deleted: + heappop(self.max_heap) + self.deleted.remove(node.key) + # If the front of the heap hasn't been deleted, then we don't have to do anything as peekMax or popMax will + # still be valid. + else: + return diff --git a/src/leetcode/interval/interval_list_intersections.py b/src/leetcode/interval/interval_list_intersections.py new file mode 100644 index 0000000..9d1e989 --- /dev/null +++ b/src/leetcode/interval/interval_list_intersections.py @@ -0,0 +1,101 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given two lists of closed intervals, firstList and secondList, where firstList[i] = [starti, endi] and +# secondList[j] = [startj, endj]. Each list of intervals is pairwise disjoint and in sorted order. +# +# Return the intersection of these two interval lists. +# +# A closed interval [a, b] (with a <= b) denotes the set of real numbers x with a <= x <= b. +# +# The intersection of two closed intervals is a set of real numbers that are either empty or represented as a closed +# interval. For example, the intersection of [1, 3] and [2, 4] is [2, 3]. +# +# See https://leetcode.com/problems/interval-list-intersections +class Solution: + def intervalIntersection(self, firstList: list[list[int]], secondList: list[list[int]]) -> list[list[int]]: + """ + SOLUTION + -------- + + This is a very straightforward problem except there's a very strange wrinkle in that intersections can be empty + intervals. Because of this we can't just advance both pointers when we find an intersection. Instead, we have + to only advance the one with the smaller endpoint. + + COMPLEXITY + ---------- + + Time complexity is O(m + n) since it iterates through both lists once each. + + Space complexity is O(min(m, n)) since the intersections array is limited by the size of the smaller list. + """ + + def isIntersecting(a: list[int], b: list[int]) -> bool: + """ + The two intervals will intersect if we have a situation like this: + + A: [.......] + B: [.......] + + ...where B[x] is greater than A[x] and B[x] is less than A[y]. Also, we can get an intersection like this: + + A: [.......] + B: [.......] + + ...where A[x] is greater than B[x] and A[x] is less than B[y]. + """ + [ax, ay] = a + [bx, by] = b + return (ax <= bx <= ay) or (bx <= ax <= by) + + # Now we can iterate through both and check if they intersect. If no intersection exists, then we must have a + # a situation like one of these: + # + # A: [..] + # B: [.....] + # + # ...or... + # + # A: [.....] + # B: [..] + # + # In these cases, whichever of A[y] or B[y] is SMALLER is the one that should be dropped and advanced. That's + # because the intervals are disjoint, so the smaller of the y values will be the one that is further to the + # left, and not able to intersect with anything. + # + # We can stop as soon as one list is exhausted because there can't be any more intersections after that. + intersections: list[list[int]] = [] + i = 0 + j = 0 + while i < len(firstList) and j < len(secondList): + a = firstList[i] + b = secondList[j] + ax, ay = a[0], a[1] + bx, by = b[0], b[1] + + if isIntersecting(a, b): + # Calculate the intersection. + # + # A: [...|....] + # B: [....|...] + # + # We'll want the max of the two start points and the min of the two end points. + # + # It could also be the case that we have something like this: + # + # A: [....| + # B: |....] + # + # In which case the intersection is an empty interval. This is weird. But it will become relevant in + # the next step. We shouldn't just advance BOTH pointers because of this empty interval situation. + # Instead, we should just advance the smaller of the two end points. + c = [max(ax, bx), min(ay, by)] + intersections.append(c) + + # Advance the one that has the smaller end point. + if ay < by: + i += 1 + else: + j += 1 + + return intersections diff --git a/src/leetcode/interval/meeting_rooms.py b/src/leetcode/interval/meeting_rooms.py new file mode 100644 index 0000000..2055b7c --- /dev/null +++ b/src/leetcode/interval/meeting_rooms.py @@ -0,0 +1,38 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given an array of meeting time intervals where intervals[i] = [starti, endi], determine if a person could attend all +# meetings. +# +# See https://leetcode.com/problems/meeting-rooms +class Solution: + def canAttendMeetings(self, intervals: list[list[int]]) -> bool: + """ + SOLUTION + -------- + + This is a very straightforward problem. We just need to sort the intervals by start time (and end time as a tie + breaker), then check if there is any overlap. + + COMPLEXITY + ---------- + + Time complexity is O(n log n) since we have to sort the intervals. + + Space complexity is O(1) since we don't use any extra space. + """ + # No overlapping intervals are possible if 0 or 1 meetings. + if len(intervals) <= 1: + return True + + # Sorts by start time, then end time as a tie breaker. + intervals.sort(key=lambda x: (x[0], x[1])) + + for i in range(1, len(intervals)): + previous = intervals[i - 1] + current = intervals[i] + # Overlaps exist if the previous end > current start. + if previous[1] > current[0]: + return False + + return True diff --git a/src/leetcode/interval/meeting_scheduler.py b/src/leetcode/interval/meeting_scheduler.py new file mode 100644 index 0000000..313b178 --- /dev/null +++ b/src/leetcode/interval/meeting_scheduler.py @@ -0,0 +1,46 @@ +class Solution: + def minAvailableDuration(self, slots1: list[list[int]], slots2: list[list[int]], duration: int) -> list[int]: + """ + SOLUTION + -------- + + To solve this, sort the intervals and use the two pointer technique to find a time slot that works for + everybody. + + COMPLEXITY + ---------- + + Time complexity is O(n log n) where n is the number of slots. + + Space complexity is O(1). + """ + # Sort by start time, then end time as a tie breaker. + slots1.sort(key=lambda x: (x[0], x[1])) + slots2.sort(key=lambda x: (x[0], x[1])) + + i = 0 + j = 0 + while i < len(slots1) and j < len(slots2): + a = slots1[i] + b = slots2[j] + + # The start time is the max of the two individuals start times; we can't start before the other person is + # ready. The end time is the min of the two individuals end times; we can't end later than the other + # person's hard stop. + start = max(a[0], b[0]) + end = min(a[1], b[1]) + + # If we can accomodate the availability we should return the start time plus the duration; we shouldn't keep the + # meeting longer than it needs to be. + if end - start >= duration: + return [start, start + duration] + + # Otherwise, we should move the pointer for the person who has the earlier ending time; these times + # represent availability, so if an earlier availability doesn't match the other person's later availability, + # we shouldn't keep the meeting longer than it needs to be. + if a[1] < b[1]: + i += 1 + else: + j += 1 + + return [] diff --git a/src/leetcode/interval/merge_intervals.py b/src/leetcode/interval/merge_intervals.py new file mode 100644 index 0000000..9d40dd7 --- /dev/null +++ b/src/leetcode/interval/merge_intervals.py @@ -0,0 +1,46 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array +# of the non-overlapping intervals that cover all the intervals in the input. +# +# See https://leetcode.com/problems/merge-intervals +class Solution: + def merge(self, intervals: list[list[int]]) -> list[list[int]]: + """ + SOLUTION + -------- + + Sort the intervals by start time, then merge overlapping intervals. + + COMPLEXITY + ---------- + + Time complexity is O(n log n) since we have to sort the intervals. + + Space complexity is O(n) since we have to store the merged intervals. + """ + # Sort by start time, then end time as a tie breaker. + intervals.sort(key=lambda x: (x[0], x[1])) + + merged: list[list[int]] = [] + for interval in intervals: + if len(merged) == 0: + merged.append(interval) + continue + + a = merged[-1] + b = interval + + # If the intervals are disjoint, then a[1] is going to be strictly less than b[0]. In which case, there is + # no overlap, and we just append the interval to the merged list. + # + # Note that we can make this assumption because the intervals are sorted. + if a[1] < b[0]: + merged.append(b) + # Otherwise, there is overlap, and we need to merge the intervals. + else: + # Because the intervals are sorted (a <= b), a[0] is always going to be smaller or the same as b[0]. + merged[-1] = [a[0], max(a[1], b[1])] + + return merged diff --git a/src/leetcode/linked_list/README.md b/src/leetcode/linked_list/README.md new file mode 100644 index 0000000..4930e02 --- /dev/null +++ b/src/leetcode/linked_list/README.md @@ -0,0 +1,14 @@ +# Linked List + +Its usually easy to identify a linked list problem. But there are some data structure combos that appear with common phrases. + +## Common Phrases (HashMap) + +These phrases indicate a hashmap might be useful: + +- cache +- LRU, MRU +- deep copy with pointers +- random pointers +- detect cycles +- check duplicates diff --git a/src/leetcode/linked_list/add_two_numbers.py b/src/leetcode/linked_list/add_two_numbers.py new file mode 100644 index 0000000..0d7b1b2 --- /dev/null +++ b/src/leetcode/linked_list/add_two_numbers.py @@ -0,0 +1,57 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse +# order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list. +# +# You may assume the two numbers do not contain any leading zero, except the number 0 itself. +# +# See https://leetcode.com/problems/add-two-numbers +from leetcode.linked_list.common.list_node import ListNode + + +class Solution: + def addTwoNumbers(self, a: ListNode, b: ListNode) -> ListNode: + """ + SOLUTION + -------- + + A naive solution of converting the nodes to numbers, adding them, and them, and then reconstructing the linked + list does work, but is a bunch more code. + + Since the lists are stored in reverse order, you can add the two head nodes, preserving a carry, and create a + new node, in the same way you would do elementary school addition. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the longer linked list. + + Space complexity is O(n) where n is the length of the longer linked list. + """ + + def add(u: ListNode | None, v: ListNode | None, carry: int) -> ListNode | None: + if not u and not v: + return ListNode(1) if carry > 0 else None + + # Compute the sum and carry for the current node. + u_val = u.val if u else 0 + v_val = v.val if v else 0 + sum_val = u_val + v_val + carry + next_val = sum_val if sum_val < 10 else sum_val - 10 + + # Compute the sum for the next node and attach it to this one. + u_next = u.next if u else None + v_next = v.next if v else None + carry_next = 1 if sum_val >= 10 else 0 + + node = ListNode() + node.val = next_val + node.next = add(u_next, v_next, carry_next) + + return node + + result = add(a, b, 0) + + assert result + return result diff --git a/src/leetcode/linked_list/all_one.py b/src/leetcode/linked_list/all_one.py new file mode 100644 index 0000000..30e3cd3 --- /dev/null +++ b/src/leetcode/linked_list/all_one.py @@ -0,0 +1,191 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Design a data structure to store the strings' count with the ability to return the strings with minimum and maximum +# counts. +# +# Implement the AllOne class: +# +# - AllOne() Initializes the object of the data structure. +# - inc(String key) Increments the count of the string key by 1. If key does not exist in the data structure, insert +# it with count 1. +# - dec(String key) Decrements the count of the string key by 1. If the count of key is 0 after the decrement, remove +# it from the data structure. It is guaranteed that key exists in the data structure before the decrement. +# - getMaxKey() Returns one of the keys with the maximal count. If no element exists, return an empty string "". +# - getMinKey() Returns one of the keys with the minimum count. If no element exists, return an empty string "". +# +# Note that each function must run in O(1) average time complexity. +# +# See https://leetcode.com/problems/all-oone-data-structure +import math +from typing import cast + + +class Node: + def __init__(self, count: float) -> None: + # This node represents a count/frequency and the keys that have this count. + self.count = count + # A set of keys that have this count. + self.keys: set[str] = set() + # For a doubly linked list, the non-sentinel nodes will have both a previous and next node. Only the sentinel + # nodes will have a pointer that points to None. + self.previous: "Node" = cast("Node", None) + self.next: "Node" = cast("Node", None) + + +class AllOne: + def __init__(self) -> None: + """ + SOLUTION + -------- + + Use a doubly linked list and hashmap to store the keys and their counts. Each node in the linked list will + represent a specific count, and the keys in that node have the count. + + The doubly linked list will let us quickly find the min and max counts, and the hashmap will let us quickly find + any key for inc/dec. + + COMPLEXITY + ---------- + + Time complexity is O(1) for all operations. + + Space complexity is O(n) where n is the number of keys in the data structure. + """ + + # Map of key -> node. + self.nodes: dict[str, Node] = {} + # We'll use this linked list to quickly find the min and max counts, which will lead us to the min and max keys. + # Create two sentinel nodes to simplify the logic. + self.head = Node(-math.inf) + self.tail = Node(math.inf) + self.head.next = self.tail + self.tail.previous = self.head + + def inc(self, key: str) -> None: + # If our key DOES NOT exist in the data structure, there are two cases: + # + # 1. The first node has count of 1. + # 2. The first node does not have a count of 1. + if key not in self.nodes: + # Find the first node (this could be the tail, but that's okay). Note that we choose a node name u so that + # the subsequent (next) node will be named v. + u = self.head.next + + # If the first node doesn't have a count of 1, we can't just add the key to it. Instead, we'll have to + # create a new node. + if u.count != 1: + u = self.__addNodeAfter(self.head, 1) + + # Now that we have the correct node (it cannot be the tail anymore). + u.keys.add(key) + self.nodes[key] = u + return + + # If our key DOES exist in the data structure, things get more complicated. + # + # 1. Find the current node with count k. + # 2. Find the next node with count k + 1, or create it if it doesn't exist, then add the key to it. + # 3. Remove the key from the current node, and remove the current node if it has no keys. + # 5. Update the key -> node mapping. + u = self.nodes[key] + + # Find the next node, or create it with the incremented count. If the next node has a count of k + 1, then we + # have found the correct one. Otherwise, we need to create a new node. + v = u.next + if v.count != u.count + 1: + v = self.__addNodeAfter(u, u.count + 1) + v.keys.add(key) + + # Remove the key from the current node, and remove the current node if it has no keys. + u.keys.remove(key) + if not u.keys: + self.__removeNode(u) + + # Update the key -> node mapping. + self.nodes[key] = v + + def dec(self, key: str) -> None: + # The problem guarantees that our key WILL exist in the data structure. + # + # 1. Find the current node with count k. + # 2. Find the previous node with count k - 1, or create it if it doesn't exist, then add the key to it. + # 3. Remove the key from the current node, and remove the current node if it has no keys. + # 4. Update the key -> node mapping. + # + # Note that we choose a name v for the current node, and u for the previous node. + v = self.nodes[key] + + # Find the previous node, or create it with the decremented count. If the previous node has a count of k - 1, + # then we have found the correct one. Otherwise, we need to create a new node. + # + # Unless! If k - 1 is 0, we don't create a new node, because we don't store keys with a count of 0. Instead, + # we just remove the key from the current node and remove the current node if it has no keys. + u = v.previous + if v.count == 1: + self.__removeKey(key) + return + # Otherwise, proceed in the same way as we handled inc. + elif u.count != v.count - 1: + u = self.__addNodeAfter(v.previous, v.count - 1) + u.keys.add(key) + + # Remove the key from the current node, and remove the current node if it has no keys. + v.keys.remove(key) + if not v.keys: + self.__removeNode(v) + + # Update the key -> node mapping. + self.nodes[key] = u + + def getMinKey(self) -> str: + if not self.nodes: + return "" + + return next(iter(self.head.next.keys)) + + def getMaxKey(self) -> str: + if not self.nodes: + return "" + + return next(iter(self.tail.previous.keys)) + + def __addNodeAfter(self, prev: Node, count: float) -> Node: + node = Node(count) + + # Line up the nodes in the desired order: a -> b -> c + a = prev + b = node + c = prev.next + + # Update pointers for node b. + b.previous = a + b.next = c + + # Update pointers for nodes a and c. + a.next = b + c.previous = b + + return node + + def __removeNode(self, node: Node) -> None: + # Line up the nodes in the desired order: a -> b -> c + a = node.previous + c = node.next + + # Update pointers for nodes a and c. + a.next = c + c.previous = a + + def __removeKey(self, key: str) -> None: + if key not in self.nodes: + return + + # Remove the key from the node. + node = self.nodes[key] + node.keys.remove(key) + + # Remove the node itself if it has no keys. + self.nodes.pop(key) + if not node.keys: + self.__removeNode(node) diff --git a/src/leetcode/linked_list/common/list_node.py b/src/leetcode/linked_list/common/list_node.py new file mode 100644 index 0000000..46f4757 --- /dev/null +++ b/src/leetcode/linked_list/common/list_node.py @@ -0,0 +1,37 @@ +# This class definition comes from the problem itself and we cannot change it, or else our submission will not be +# accepted. +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + + +def list2array(node: ListNode | None) -> list[int]: + if not node: + return [] + + xs: list[int] = [] + current = node + while current: + xs.append(current.val) + current = current.next + + return xs + + +def array2list(xs: list[int]) -> ListNode | None: + if not xs: + return None + + root: ListNode | None = None + current: ListNode | None = None + for x in xs: + if not current: + current = ListNode(x) + root = current + continue + else: + current.next = ListNode(x) + current = current.next + + return root diff --git a/src/leetcode/linked_list/common/nested_integer.py b/src/leetcode/linked_list/common/nested_integer.py new file mode 100644 index 0000000..35a3b79 --- /dev/null +++ b/src/leetcode/linked_list/common/nested_integer.py @@ -0,0 +1,22 @@ +# This class definition comes from the problem itself and we cannot change it, or else our submission will not be +# accepted. +class NestedInteger: + def __init__(self, value=None) -> None: + self.value: int | None = value + self.values: "list[NestedInteger]" = [] + + def isInteger(self) -> bool: + return self.value is not None + + def add(self, e: "NestedInteger") -> None: + self.value = None + self.values.append(e) + + def setInteger(self, value: int): + self.value = value + + def getInteger(self) -> int | None: + return self.value + + def getList(self) -> "list[NestedInteger]": + return self.values diff --git a/src/leetcode/linked_list/common/random_node.py b/src/leetcode/linked_list/common/random_node.py new file mode 100644 index 0000000..e926dc1 --- /dev/null +++ b/src/leetcode/linked_list/common/random_node.py @@ -0,0 +1,7 @@ +# This class definition comes from the problem itself and we cannot change it, or else our submission will not be +# accepted. +class Node: + def __init__(self, x: int, next: "Node | None" = None, random: "Node | None" = None): + self.val = int(x) + self.next = next + self.random = random diff --git a/src/leetcode/linked_list/copy_list_with_random_pointers.py b/src/leetcode/linked_list/copy_list_with_random_pointers.py new file mode 100644 index 0000000..abdf439 --- /dev/null +++ b/src/leetcode/linked_list/copy_list_with_random_pointers.py @@ -0,0 +1,71 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# A linked list of length n is given such that each node contains an additional random pointer, which could point to +# any node in the list, or null. +# +# Construct a deep copy of the list. The deep copy should consist of exactly n brand new nodes, where each new node has +# its value set to the value of its corresponding original node. Both the next and random pointer of the new nodes +# should point to new nodes in the copied list such that the pointers in the original list and copied list represent +# the same list state. None of the pointers in the new list should point to nodes in the original list. +# +# For example, if there are two nodes X and Y in the original list, where X.random --> Y, then for the corresponding +# two nodes x and y in the copied list, x.random --> y. +# +# Return the head of the copied linked list. +# +# The linked list is represented in the input/output as a list of n nodes. Each node is represented as a pair of +# [val, random_index] where: +# +# - val: an integer representing Node.val +# - random_index: the index of the node (range from 0 to n-1) that the random pointer points to, or null if it does not +# point to any node. +# +# Your code will only be given the head of the original linked list. +# +# See https://leetcode.com/problems/copy-list-with-random-pointer +from leetcode.linked_list.common.random_node import Node + + +class Solution: + def copyRandomList(self, head: "Node | None") -> "Node | None": + """ + SOLUTION + -------- + + If there's no random pointer you can iterate through the nodes and create a copy of each node. With the random + pointer you'll have to maintain a map of original node to copied node so you can assign the random pointers + later. + + First iterate through the list once to make copies of each node in a map. Then iterate again and assign the + next and random pointers. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of nodes in the linked list. + + Space complexity is O(n) because we are storing the new nodes in a dictionary. + """ + if not head: + return None + + # Create a map of original nodes to new nodes. + mapping: dict[Node | None, Node] = {} + + # First, create a map of each node to its copy. + current = head + while current: + mapping[current] = Node(current.val) + current = current.next + + # Second, assign the pointers. + current = head + while current: + copy = mapping[current] + copy.next = mapping.get(current.next, None) + copy.random = mapping.get(current.random, None) + current = current.next + + # Return the head of the copied list. + return mapping[head] diff --git a/src/leetcode/linked_list/merge_two_sorted_lists.py b/src/leetcode/linked_list/merge_two_sorted_lists.py new file mode 100644 index 0000000..85e69f0 --- /dev/null +++ b/src/leetcode/linked_list/merge_two_sorted_lists.py @@ -0,0 +1,52 @@ +# DIFFICULTY: EASY +# ---------------- +# +# You are given the heads of two sorted linked lists list1 and list2. +# +# Merge the two lists into one sorted list. The list should be made by splicing together the nodes of the first two lists. +# +# Return the head of the merged linked list. +# +# See https://leetcode.com/problems/merge-two-sorted-lists +from leetcode.linked_list.common.list_node import ListNode + + +class Solution: + def mergeTwoLists(self, list1: "ListNode | None", list2: "ListNode | None") -> "ListNode | None": + """ + SOLUTION + -------- + + Start with a sentinel node and continue appending the smaller value of the two lists to the merged list. If + there are any remaining nodes in either list, append them to the end of the list (they are already sorted). + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of nodes in the merged list. + + Space complexity is O(1). + """ + # Create a sentinel head node to simplify the logic. + head = ListNode(-1) + node = head + + # While there are nodes in both list1 and list2, compare the values and append the smaller value to the merged + # list. + while list1 and list2: + if list1.val < list2.val: + node.next = list1 + list1 = list1.next + else: + node.next = list2 + list2 = list2.next + node = node.next + + # If there are any remaining nodes in list1 or list2, append them to the merged list; they are already sorted. + if list1: + node.next = list1 + if list2: + node.next = list2 + + # Return the head node of the merged list. + return head.next diff --git a/src/leetcode/linked_list/nested_list_weighted_sum.py b/src/leetcode/linked_list/nested_list_weighted_sum.py new file mode 100644 index 0000000..b6283a4 --- /dev/null +++ b/src/leetcode/linked_list/nested_list_weighted_sum.py @@ -0,0 +1,46 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given a nested list of integers nestedList. Each element is either an integer or a list whose elements may +# also be integers or other lists. +# +# The depth of an integer is the number of lists that it is inside of. For example, the nested list [1,[2,2],[[3],2],1] +# has each integer's value set to its depth. +# +# Return the sum of each integer in nestedList multiplied by its depth. +# +# See https://leetcode.com/problems/nested-list-weight-sum +from leetcode.linked_list.common.nested_integer import NestedInteger + + +class Solution: + def depthSum(self, nestedList: list[NestedInteger]) -> int: + """ + SOLUTION + -------- + + This is essentially a depth first search problem. However, because it a nested list, there's no need to keep + track of visited elements. Instead we'll just keep track of the depth as we traverse the list. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of total integers and lists in the data structure. + + Space complexity is O(d) where d is the depth of the nested list, since we need a stack frame for each level of + recursion. + """ + x = NestedInteger() + for e in nestedList: + x.add(e) + + def compute(x: NestedInteger, depth: int) -> int: + # If we've encountered an integer, compute the weighted value according to the depth. + if x.isInteger(): + value = x.getInteger() or 0 + return value * depth + + # Otherwise, we have a list, so we'll recursively compute the weighted sum of the list. + return sum(compute(y, depth + 1) for y in x.getList()) + + return compute(x, 0) diff --git a/src/leetcode/linked_list/nested_list_weighted_sum_ii.py b/src/leetcode/linked_list/nested_list_weighted_sum_ii.py new file mode 100644 index 0000000..b8a9769 --- /dev/null +++ b/src/leetcode/linked_list/nested_list_weighted_sum_ii.py @@ -0,0 +1,66 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given a nested list of integers nestedList. Each element is either an integer or a list whose elements may +# also be integers or other lists. +# +# The depth of an integer is the number of lists that it is inside of. For example, the nested list [1,[2,2],[[3],2],1] +# has each integer's value set to its depth. Let maxDepth be the maximum depth of any integer. +# +# The weight of an integer is maxDepth - (the depth of the integer) + 1. +# +# Return the sum of each integer in nestedList multiplied by its weight. +# +# See https://leetcode.com/problems/nested-list-weight-sum-ii +from collections import defaultdict +from leetcode.linked_list.common.nested_integer import NestedInteger + + +class Solution: + def depthSumInverse(self, nestedList: list[NestedInteger]) -> int: + """ + SOLUTION + -------- + + Unlike the previous problem where we just multiply the integer by its depth, we need to multiply by the weight + of the integer. Since we won't know the weight until after we've traversed the entire list, we'll need to keep + track of depth to integers, then calculate the weight and sum up at the end. + + However, like before, this is still going to be a DFS search, except we don't need to keep track of visited + nodes. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of total integers and lists in the data structure. + + Space complexity is O(d) where d is the depth of the nested list. + """ + # Define a list of depth -> integers; each integer at that depth will have the same weight. We'll compute the + # max depth as we traverse. + mapping: dict[int, list[int]] = defaultdict(list[int]) + max_depth = 0 + + def compute(x: NestedInteger, depth: int) -> None: + nonlocal max_depth + max_depth = max(depth, max_depth) + + if x.isInteger(): + value = x.getInteger() or 0 + mapping[depth].append(value) + else: + values = x.getList() + for value in values: + compute(value, depth + 1) + + for nested in nestedList: + compute(nested, 0) + + # Now that we have the map, just multiply each list of integers at each depth by the computed weight, and sum. + result = 0 + for depth, values in mapping.items(): + weight = max_depth - depth + 1 + partial = sum(values) * weight + result += partial + + return result diff --git a/src/leetcode/linked_list/remove_nth_node_from_end_of_list.py b/src/leetcode/linked_list/remove_nth_node_from_end_of_list.py new file mode 100644 index 0000000..9790533 --- /dev/null +++ b/src/leetcode/linked_list/remove_nth_node_from_end_of_list.py @@ -0,0 +1,50 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given the head of a linked list, remove the nth node from the end of the list and return its head. +# +# See https://leetcode.com/problems/remove-nth-node-from-end-of-list +from leetcode.linked_list.common.list_node import ListNode + + +class Solution: + def removeNthFromEnd(self, head: "ListNode | None", n: int) -> "ListNode | None": + """ + SOLUTION + -------- + + The naive solution that accomplishes this in one iteration is to throw all the nodes into an array. Then we can + easily find the nth node from the end of the array by indexing into it. This solution requires O(m) space, + where m is the length of the list. + + There is a more efficient solution that uses two pointers. The first pointer will be n nodes ahead of the + second pointer. Move both pointers until the first pointer reaches the end of the list. At this point, the + second pointer will be pointing to the node that we want to remove. + + The problem says we won't have invalid inputs, so ignore pyright warnings. + + COMPLEXITY + ---------- + + Time complexity is O(m) where time because we are iterating through the list once. + + Space complexity is O(1). + """ + # Create a sentinel head node to simplify the logic. + sentinel = ListNode(-1) + sentinel.next = head + faster: ListNode | None = sentinel + slower: ListNode | None = sentinel + + # Move the faster pointer n nodes ahead of the slower pointer. + for _ in range(n + 1): + faster = faster.next # pyright: ignore + + # Move both pointers until the faster pointer reaches the end of the list. + while faster: + faster = faster.next # pyright: ignore + slower = slower.next # pyright: ignore + + # Remove the slower node from the list. + slower.next = slower.next.next # pyright: ignore + return sentinel.next diff --git a/src/leetcode/math/number_of_one_bits.py b/src/leetcode/math/number_of_one_bits.py new file mode 100644 index 0000000..e7f4bcd --- /dev/null +++ b/src/leetcode/math/number_of_one_bits.py @@ -0,0 +1,35 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Write a function that takes the binary representation of a positive integer and returns the number of set bits it has +# (also known as the Hamming weight). +# +# See https://leetcode.com/problems/number-of-1-bits +class Solution: + def hammingWeight(self, n: int) -> int: + """ + SOLUTION + -------- + + Use the bitwise AND operator to check if the rightmost bit is set. If it is, increment the count of one bits, + then right shift by one to remove the rightmost bit. Repeat this process until there are no more bits. + + COMPLEXITY + ---------- + + Time complexity is O(1) because the number of bits in an integer is technically fixed. Or it's O(m) where m is + the number of bits to check. + + Space complexity is O(1). + """ + count = 0 + + while n: + # This will be true if the least significant bit is set. + if n & 1: + count += 1 + + # Right shift by one to remove the least significant bit. + n >>= 1 + + return count diff --git a/src/leetcode/math/pow_x_n.py b/src/leetcode/math/pow_x_n.py new file mode 100644 index 0000000..6471dbf --- /dev/null +++ b/src/leetcode/math/pow_x_n.py @@ -0,0 +1,68 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Implement pow(x, n), which calculates x raised to the power n (i.e., xn). +# +# See https://leetcode.com/problems/powx-n +class Solution: + def myPow(self, x: float, n: int) -> float: + """ + SOLUTION + -------- + + The naive solution is to multiply x by itself n times. This is O(n) time complexity. + + The faster solution is based on the concept of exponentiation by squaring. Basically, we can reduce the number + of multiplications if we notice that: + + x^n = (x^2)^(n/2) if x n is even + x^n = x * (x^(n-1)/2) if n is odd + + Exponentiation by squaring is faster because the naive method requires n multiplications, but squaring each + time only requires log(n) multiplications. To implement this: + + 1. Check if n is negative. If it is, then we can just invert x and make n positive. + 2. Check if n === 0. If it is, then we can just return 1. + 3. Check if n is even. If it is, then we can return myPow(x * x, n / 2). That is, square the number and half + the exponent. + 4. Check if n is odd. If it is, we can do the same thing, except just return x * myPow(x * x, (n - 1) / 2). + That is, we half the exponent minus 1 to make it even (so we can do our squaring), then multiply the result + by x. + + This can be done even faster if we use bit shift operations and convert the recursive solution to an iterative + one. + + COMPLEXITY + ---------- + + Time complexity is O(log n). + + Space complexity is O(1). + """ + if n == 0: + return 1 + + xi = x + ni = n + + # Check if n is negative. If it is, then we can just invert x and make n positive, then continue as normal. + if ni < 0: + xi = 1 / xi + ni = -ni + + result = 1 + while ni > 0: + # If n is odd, multiply the result by x as we discussed above. Afterwards we can proceed with the division + # by two (note that by doing >>, the division rounds down giving us the correct result). + # + # Rather than use ni % 2 to check if a number is even, use bitwise and. If the result is 1, then the least + # significant bit is a 1. Otherwise, the bit is 0. + if ni & 1 != 0: + result *= xi + + # Perform the squaring operation and halve the exponent; happens regardless of whether n is even or odd. + xi *= xi + # Rather than divide by 2, we can use the right bit shift operator to do a faster division. + ni >>= 1 + + return result diff --git a/src/leetcode/math/reverse_integer.py b/src/leetcode/math/reverse_integer.py new file mode 100644 index 0000000..1638605 --- /dev/null +++ b/src/leetcode/math/reverse_integer.py @@ -0,0 +1,46 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a signed 32-bit integer x, return x with its digits reversed. If reversing x causes the value to go outside the +# signed 32-bit integer range [-231, 231 - 1], then return 0. +# +# Assume the environment does not allow you to store 64-bit integers (signed or unsigned). +# +# See https://leetcode.com/problems/reverse-integer +class Solution: + def reverse(self, x: int) -> int: + """ + SOLUTION + -------- + + The medium level solution assumes we are reversing with arithmetic operations and not converting to a string. + + COMPLEXITY + ---------- + + Time complexity is O(log n) where n is the number of digits in x. + + Space complexity is O(1). + """ + min_value = -1 * 2**31 + max_value = 2**31 - 1 + sign = 1 if x >= 0 else -1 + + n = abs(x) + r = 0 + while n: + # Get the least significant digit of n; this will be the most significant digit of r in the first iteration, + # the second most in the next iteration, and so on. + last = n % 10 + + if r == 0: + # When r === 0, just make last digit the most significant digit. + r = last + else: + # Otherwise shift r to the left by 10, then add the last digit we got from n. + r = r * 10 + last + + n = n // 10 + + value = sign * r + return value if min_value < value < max_value else 0 diff --git a/src/leetcode/math/rotate_image.py b/src/leetcode/math/rotate_image.py new file mode 100644 index 0000000..d767dbd --- /dev/null +++ b/src/leetcode/math/rotate_image.py @@ -0,0 +1,77 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an n x n 2D matrix representing an image, rotate the image by 90 degrees (clockwise). +# +# You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate +# another 2D matrix and do the rotation. +# +# See https://leetcode.com/problems/rotate-image +class Solution: + def rotate(self, matrix: list[list[int]]) -> None: + """ + SOLUTION + -------- + + To rotate an image by 90 degrees, we can transpose the matrix (exchange [i, j] with [j, i]), then reverse each + row. + + Generally you can accomplish any number of different rotations by transposition and reversing. + + 90 degrees clockwise: transpose, then reverse rows. + 90 degrees counter-clockwise: transpose, then reverse columns. + 180 degrees: reverse rows, reverse columns. + 270 degrees clockwise: same as 90 degrees counter-clockwise. + 270 degrees counter-clockwise: same as 90 degrees clockwise. + + The above approach requires a transposition then reversing each element. It is O(n^2) for the transpose + operation but then O(n^2) again for reversals. A more complicated algorithm can achieve the transposition in a + single pass through the matrix. + + For a 4 element matrix: + + - The element at (0, 0) moves to (0, 3). + - The element at (0, 3) moves to (3, 3). + - The element at (3, 3) moves to (3, 0). + - The element at (3, 0) moves to (0, 0). + + This pattern applies for the outer layer of the square matrix, then the next layer below that, and so on. Here + a "layer" means an square (so the outer square), then the next inner square and so on. + + COMPLEXITY + ---------- + + Time complexity is O(n^2) because we are iterating through all elements in the matrix. + + Space complexity is O(1) because we are modifying the matrix in place. + """ + # We'll have to apply our algorithm to each "layer" of our matrix. For a matrix of size N, there are N/2 + # layers. + n = len(matrix) + layers = n // 2 + + # Operate our algorithm for each layer; again, an NxN matrix will have N/2 layers. + for i in range(layers): + # By setting j = i, we will descend into an inner layer and process them one by one. That is, the first + # square starts at (0, 0), then the next square starts at (1, 1), and so on. + for j in range(i, n - i - 1): + # Lets define our 4 points so we can get our cells correctly. + left = i + right = n - i - 1 + top = j + bottom = n - j - 1 + + # Temporarily save the top left cell so we can swap it later. + t = matrix[left][top] + + # The (left row, top column) cell should get the (bottom row, left column) value. + matrix[left][top] = matrix[bottom][left] + + # The (bottom row, left column) cell should get the (right row, bottom column) value. + matrix[bottom][left] = matrix[right][bottom] + + # The (right row, bottom column) cell should get the (top row, right column) value. + matrix[right][bottom] = matrix[top][right] + + # The (top row, right column) cell should get the (top row, left column) value (which we saved). + matrix[top][right] = t diff --git a/src/leetcode/math/sequential_digits.py b/src/leetcode/math/sequential_digits.py new file mode 100644 index 0000000..8992932 --- /dev/null +++ b/src/leetcode/math/sequential_digits.py @@ -0,0 +1,53 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# An integer has sequential digits if and only if each digit in the number is one more than the previous digit. +# +# Return a sorted list of all the integers in the range [low, high] inclusive that have sequential digits. +# +# See https://leetcode.com/problems/sequential-digits +class Solution: + def sequentialDigits(self, low: int, high: int) -> list[int]: + """ + SOLUTION + -------- + + Rather than take a list from low to high and filtering out the numbers that match our criteria, it will be much + faster to generate a list of numbers with this quality with the correct number of digits, then filter out by low + and high ranges. + + COMPLEXITY + ---------- + + Time complexity is O(1) because the number of digits in an integer is fixed. Or it's O(m) where m is the number + of digits. + + Space complexity is O(1). + """ + results: list[int] = [] + + # Sequential digits end at 9, so the largest sequential number you can have is 123456789. Start generating them + # at i = 1. We'll handle 0 in a special case. + for i in range(1, 10): + # Set the value to be xxxi, and the next digit following xxxi to be i + 1. We will append i + 1 to the end + # of this number. + value = i + next_digit = i + 1 + + while next_digit <= 9 and value <= high: + # Shift the current number's digits over to the left, leaving a 0 in the one's place. + value *= 10 + + # Add the next digit to the one's place. + value += next_digit + next_digit += 1 + + # If it's within bounds, add it to the array. + if low <= value <= high: + results.append(value) + + # Handle the special case where low is 0. + if low == 0: + results.append(0) + + return sorted(results) diff --git a/src/leetcode/matrix/robot_bounded_in_circle.py b/src/leetcode/matrix/robot_bounded_in_circle.py new file mode 100644 index 0000000..463288e --- /dev/null +++ b/src/leetcode/matrix/robot_bounded_in_circle.py @@ -0,0 +1,77 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# On an infinite plane, a robot initially stands at (0, 0) and faces north. Note that: +# +# - The north direction is the positive direction of the y-axis. +# - The south direction is the negative direction of the y-axis. +# - The east direction is the positive direction of the x-axis. +# - The west direction is the negative direction of the x-axis. +# +# The robot can receive one of three instructions: +# +# - "G": go straight 1 unit. +# - "L": turn 90 degrees to the left (i.e., anti-clockwise direction). +# - "R": turn 90 degrees to the right (i.e., clockwise direction). +# - The robot performs the instructions given in order, and repeats them forever. +# +# Return true if and only if there exists a circle in the plane such that the robot never leaves the circle. +# +# See https://leetcode.com/problems/robot-bounded-in-circle +class Solution: + def isRobotBounded(self, instructions: str) -> bool: + """ + SOLUTION + -------- + + The problem is worded a little confusingly, but the gist is that if you can draw any circle around the robot's + path then return true. Otherwise, return false. + + Okay, so there are a few possibilities that can be put into two categories: + + 1. It follows the path and returns to the starting position. + 2. It follows the path and does not return to the starting position. + + In the first case, regardless of what direction the robot is facing, it will follow the same path and return to + the starting position again and again, so we can bound it with a circle. + + In the second case, it is *possible* that even if it's not in the starting position, it can follow a path that + is bounded by a circle. Let's explore the possibilities. + + - The direction the robot is facing is the same as the initial direction. In this case, the robot will continue + to move in that same direction forever, making the path unbounded. + - The direction the robot is facing is different from the initial direction. In this case, the robot will + follow along a path that can be bounded by a circle. + + The second condition is true because the new direction is the new "north" for the robot, and it will eventually + return to the origin following that path for 4 iterations. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of instructions. + + Space complexity is O(1). + """ + directions = {"N": (0, 1), "E": (1, 0), "S": (0, -1), "W": (-1, 0)} + + leftTurns = {"N": "W", "W": "S", "S": "E", "E": "N"} + + rightTurns = {"N": "E", "E": "S", "S": "W", "W": "N"} + + (x, y) = (0, 0) + facing = "N" + + for instruction in instructions: + if instruction == "G": + dx, dy = directions[facing] + x += dx + y += dy + elif instruction == "L": + facing = leftTurns[facing] + elif instruction == "R": + facing = rightTurns[facing] + + # If the robot returned to the origin, or it's facing a different direction than the initial direction, then we + # are good to go. + return (x == 0 and y == 0) or facing != "N" diff --git a/src/leetcode/matrix/valid_word_square.py b/src/leetcode/matrix/valid_word_square.py new file mode 100644 index 0000000..7d52c46 --- /dev/null +++ b/src/leetcode/matrix/valid_word_square.py @@ -0,0 +1,33 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given an array of strings words, return true if it forms a valid word square. +# +# A sequence of strings forms a valid word square if the kth row and column read the same string, where +# 0 <= k < max(numRows, numColumns). +# +# See https://leetcode.com/problems/valid-word-square +class Solution: + def validWordSquare(self, words: list[str]) -> bool: + """ + SOLUTION + -------- + + A naive solution works. + + COMPLEXITY + ---------- + + Time complexity is O(m * n). + + Space complexity is O(1). + """ + for i in range(len(words)): + for j in range(len(words[i])): + try: + if words[i][j] != words[j][i]: + return False + except IndexError: + return False + + return True diff --git a/src/leetcode/prefix_sum/README.md b/src/leetcode/prefix_sum/README.md new file mode 100644 index 0000000..92725e4 --- /dev/null +++ b/src/leetcode/prefix_sum/README.md @@ -0,0 +1,11 @@ +# Prefix Sum + +## Common Phrases + +These phrases indicate a prefix sum might be useful: + +- range sum query +- sum from index to index +- difference between sums +- rolling sum +- running total diff --git a/src/leetcode/prefix_sum/continuous_subarray_sum.py b/src/leetcode/prefix_sum/continuous_subarray_sum.py new file mode 100644 index 0000000..4d37da1 --- /dev/null +++ b/src/leetcode/prefix_sum/continuous_subarray_sum.py @@ -0,0 +1,92 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums and an integer k, return true if nums has a good subarray or false otherwise. +# +# A good subarray is a subarray where: +# +# - its length is at least two, and +# - the sum of the elements of the subarray is a multiple of k. +# +# Note that: +# +# - A subarray is a contiguous part of the array. +# - An integer x is a multiple of k if there exists an integer n such that x = n * k. 0 is always a multiple of k. +# +# See https://leetcode.com/problems/continuous-subarray-sum +class Solution: + def checkSubarraySum(self, nums: list[int], k: int) -> bool: + """ + SOLUTION + -------- + + The brute force solution is to calculate the sum of all subarrays of length 2 or more and check if it's a + multiple of of k. To do so, compute the prefix sum of the array and use it to calculate the sum of the subarray + from [i, j]. + + A subarray sum from [i, j] can be calculated as prefixSum[j] - prefixSum[i] + nums[i]. We can check each + subarray sum to see if it's a multiple of k and return true if we find one. It is; however, O(n^2) to calculate + subarray sums in this way. Unfortunately, this will exceed the runtime limit for large arrays in LeetCode. + + To get a better solution we have to note that: + + sum[i, j] = prefixSum[j] - prefixSum[i] + nums[i] + sum[i, j] = prefixSum[j] - prefixSum[i - 1] + + This is because the sum[i, j] is inclusive, and subtracting prefixSum[i] subtracts out nums[i]. That's why we + either have to add it back in or use i - 1 as the index instead. Afterwards, we can look at the sum modulo k: + + sum[i, j] (mod k) = (prefixSum[j] - prefixSum[i - 1]) (mod k) + + If we want sum[i, j] % k === 0, then we should write: + + (prefixSum[j] - prefixSum[i - 1]) (mod k) = 0 + prefixSum[j] (mod k) = prefixSum[i - 1] (mod k) + + In other words, if prefixSums at positions (i - 1) and j have the same remainder modulo k, then the subarray sum + from [i, j] has remainder 0 modulo k. + + COMPLEXITY + ---------- + + Time complexity is O(n) because we are iterating through the list once. + + Space complexity is O(n) because we are storing the prefix sum array. + """ + # Use a map of remainders -> index to store the prefix sum remainders, so we can check later if we have seen + # any remainders that are the same modulo k. + remainders: dict[int, int] = {} + + # Maintain a running prefix sum as we loop through the array. + prefix_sum = 0 + for i, num in enumerate(nums): + prefix_sum += num + r = prefix_sum % k + + # It might be the case that the remainder is negative, just as a quirk of how JavaScript handles the modulo + # operator. + # + # For example, -5 % 3 = -2. But this is wrong because -2 is not congruent to -5 modulo 3. Instead: + # + # -5 (mod 3) = -5 + 3 (mod 3) = -2 (mod 3) = -2 + 3 (mod 3) = 1 (mod 3) + # + # That is, we want -5 % 3 = 1 instead. To fix this, we half to add the modulus to the remainder if it's + # negative. + if r < 0: + r += k + + # It's possible that the prefix sum already is a multiple of k. And it's possible that the remainder 0 + # isn't in the remainders map yet. In this case, just check if we have a subarray of length 2 or more. + if r == 0 and i >= 1: + return True + + # If this is a previously seen remainder, we can check if we have a "good" subarray. + if r in remainders: + j = remainders[r] + if abs(i - j) >= 2: + return True + # If we're seeing this remainder for the first time, store it in the map. + else: + remainders[r] = i + + return False diff --git a/src/leetcode/prefix_sum/max_sum_obtained_of_any_permutation.py b/src/leetcode/prefix_sum/max_sum_obtained_of_any_permutation.py new file mode 100644 index 0000000..854d92d --- /dev/null +++ b/src/leetcode/prefix_sum/max_sum_obtained_of_any_permutation.py @@ -0,0 +1,76 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# We have an array of integers, nums, and an array of requests where requests[i] = [starti, endi]. The ith request asks +# for the sum of nums[starti] + nums[starti + 1] + ... + nums[endi - 1] + nums[endi]. Both starti and endi are +# 0-indexed. +# +# Return the maximum total sum of all requests among all permutations of nums. +# +# Since the answer may be too large, return it modulo 10^9 + 7. +# +# See https://leetcode.com/problems/maximum-sum-obtained-of-any-permutation +class Solution: + def maxSumRangeQuery(self, nums: list[int], requests: list[list[int]]) -> int: + """ + SOLUTION + -------- + + This question is asking for a very weird thing. + + Here, the request ranges don't change; they want to sum the values of the range. However, the nums array can be + in any order you want. The ask is to find the order of nums that gives you the maximum sum across ALL requests, + and then return that maximum sum (not the specific order). + + This will happen if the most frequently asked for numbers are matched up with the largest numbers. So + basically, sort the nums in descending order by frequency. + + COMPLEXITY + ---------- + + Time complexity is O(n log n) because we are sorting the requests. + + Space complexity is O(n) because we are storing the frequency of each index. + """ + # First, calculate for each index i, how many times that index is requested. + freq = [0] * len(nums) + for request in requests: + [start, end] = request + + # This is the difference array technique, usable when we want to efficiently apply multiple operations to a + # single array. Here, the naive way to compute frequency would be to do an inner loop and and increment + # each element between start and end by x (here x = 1). + # + # Instead, use the difference array and prefix sums to compute the actual frequencies. A prefix sum will + # carry the value of x (here x = 1) from start through to the end of the array. However, if we set + # freq[end+1] = -x, then the propagated sum effectively gets "turned off" after adding x to freq[end]. + freq[start] += 1 + if end + 1 < len(nums): + freq[end + 1] -= 1 + + # Next, we can calculate the actual frequency of each element by taking the prefix sum of the array. This + # propagates sums of values at i through to the end of the array (except for spots where we have strategically + # "turned off" the propagation to make it only apply to a range). + for i in range(1, len(freq)): + freq[i] += freq[i - 1] + + # Now we want to sort the frequencies AND nums in descending order, so we can match up the highest frequency + # asks with the largest number. + # + # Note that this makes us lose track of the indices, but that's okay. Every single element in freq represents + # the number of times some value is asked for. Every request is honored, so every value will be asked for. + # + # On top of that, the question asks for the maximum sum across ALL queries, so it's not even necessary to keep + # track of any individual query. In the end we'll sum everything together anyways. + nums.sort(reverse=True) + freq.sort(reverse=True) + + # Now it's just a matter of summing every single request value by matching it up with the largest number. + # Remember that the totality of freq's request values will cover all range queries. Since we don't care about + # the max for any individual query, we can just add them ALL up. + modulus = 1e9 + 7 + result = 0 + for i in range(len(nums)): + result = (result + nums[i] * freq[i]) % modulus + + return int(result) diff --git a/src/leetcode/prefix_sum/product_of_array_except_self.py b/src/leetcode/prefix_sum/product_of_array_except_self.py new file mode 100644 index 0000000..1833924 --- /dev/null +++ b/src/leetcode/prefix_sum/product_of_array_except_self.py @@ -0,0 +1,58 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums, return an array answer such that answer[i] is equal to the product of all the elements +# of nums except nums[i]. +# +# The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer. +# +# You must write an algorithm that runs in O(n) time and without using the division operation. +# +# See https://leetcode.com/problems/product-of-array-except-self +class Solution: + def productExceptSelf(self, nums: list[int]) -> list[int]: + """ + SOLUTION + -------- + + If we could use division, we could just compute the product of the array (excluding zeroes), then we can divide + the non-zero product by the current element if there were no zeroes, return the non-zero product if there were + zeroes, or just return zero if there were multiple zeroes. + + Without using division, we'll have to use more space to compute the product-except-self in each array index. To + do this, we have to construct two new arrays: + + - One array to hold the partial product of all elements before, but not including element i, called `prefix`. + This array is a "prefix product", analogous to the "prefix sum" computational technique used preprocess an + array for quick range queries. + - One array to hold the partial product of all elements after, but not including element i, called `suffix`. + This array is a "suffix product", analogous to the "suffix sum" computational technique used to preprocess an + array for quick range queries. + + To compute the product-except-self, the ith value will be `befores[i] * afters[i]`. + + This isn't exactly a prefix sum problem, but well, close enough. + + COMPLEXITY + ---------- + + Time complexity is O(n) because we are iterating through the list twice. + + Space complexity is O(n) because we are storing the prefix and suffix product arrays. + """ + if not nums: + return [] + + # Compute the product up to the ith index. + prefix = [1] * len(nums) + for i in range(1, len(nums)): + prefix[i] = prefix[i - 1] * nums[i - 1] + + # Compute the product after the ith index. + suffix = [1] * len(nums) + for i in range(len(nums) - 2, -1, -1): + suffix[i] = suffix[i + 1] * nums[i + 1] + + # Compute the product except for the element at the ith index. + products = [prefix[i] * suffix[i] for i in range(len(nums))] + return products diff --git a/src/leetcode/prefix_sum/random_pick_with_weight.py b/src/leetcode/prefix_sum/random_pick_with_weight.py new file mode 100644 index 0000000..bb71d62 --- /dev/null +++ b/src/leetcode/prefix_sum/random_pick_with_weight.py @@ -0,0 +1,83 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given a 0-indexed array of positive integers w where w[i] describes the weight of the ith index. +# +# You need to implement the function pickIndex(), which randomly picks an index in the range [0, w.length - 1] +# (inclusive) and returns it. The probability of picking an index i is w[i] / sum(w). +# +# For example, if w = [1, 3], the probability of picking index 0 is 1 / (1 + 3) = 0.25 (i.e., 25%), and the +# probability of picking index 1 is 3 / (1 + 3) = 0.75 (i.e., 75%). +# +# See https://leetcode.com/problems/random-pick-with-weight +import random + + +class Solution: + """ + SOLUTION + -------- + + The naive way to do this is to take the weights and create a new array that's the size of the sum of the + weights. For example, if you have the input [1, 3], create an array [0, 1, 1, 1]. Then, generate a random + number using floor(random() * 4) to get a number between 0 and 3 inclusive. That's the index you should pick. + + This will work, but once you have a large number of weights, you'll be creating quite large arrays. Probably + not a good idea. + + Instead, we can compute a prefix sum to avoid expanding an array. In the previous example, you can turn the + input array [1, 3] into the prefix sum array [1, 4]. The cumulative weights divide the range in this way: + + [0, 1) -> Index 0 + [1, 4) -> Index 1 + + So if you generate a random number between [0, 4), you can do a linear scan of the prefix sum array to find the + index that you should pick. This works because each range's weight increases as you move to the right. + + Likewise, if you had the input array [1, 4, 2], the prefix sum array would be [1, 5, 7]. The ranges would be: + + [0, 1) -> Index 0 + [1, 5) -> Index 1 + [5, 7) -> Index 2 + + And again, picking a random number between [0, 7) would allow you to do a linear scan to find the index to pick. + + But wait! Instead of doing a linear scan, it's much more efficient to do a binary search, which is what this + solution does. + + COMPLEXITY + ---------- + + Time complexity in O(n). It is O(n) to create the prefix sum array, then O(n) to perform a linear scan, or + O(log n) to perform a binary search. + + Space complexity is O(n) because we are storing the prefix sum array. + """ + + def __init__(self, w: list[int]) -> None: + self.prefix_sum = [0] * len(w) + self.total = 0 + for i, weight in enumerate(w): + self.total += weight + self.prefix_sum[i] = self.total + + def pickIndex(self) -> int: + # Pick a random number between [0, total). Doesn't have to be an integer at all; it's just a number between 0 + # and the total. + target = random.randint(0, self.total - 1) + + # Use insertion point binary search instead of exact match. We aren't looking for an exact match of the element + # in the array. In fact, it's unlikely to even be in the array. Instead we'll use insertion point binary + # search which is more conceptually appropriate. + left = 0 + right = len(self.prefix_sum) + + while left < right: + mid = (left + right) // 2 + if self.prefix_sum[mid] <= target: + left = mid + 1 + else: + right = mid + + # The "insertion point" is the index that corresponds to the random number. + return left diff --git a/src/leetcode/prefix_sum/range_sum_query_immutable.py b/src/leetcode/prefix_sum/range_sum_query_immutable.py new file mode 100644 index 0000000..7b3f774 --- /dev/null +++ b/src/leetcode/prefix_sum/range_sum_query_immutable.py @@ -0,0 +1,45 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given an integer array nums, handle multiple queries of the following type: +# +# Calculate the sum of the elements of nums between indices left and right inclusive where left <= right. +# Implement the NumArray class: +# +# - NumArray(int[] nums) Initializes the object with the integer array nums. +# - int sumRange(int left, int right) Returns the sum of the elements of nums between indices left and right inclusive +# (i.e. nums[left] + nums[left + 1] + ... + nums[right]). +# +# See https://leetcode.com/problems/range-sum-query-immutable +from itertools import accumulate + + +class NumArray: + """ + SOLUTION + -------- + + This can be done with a prefix sum array. + + COMPLEXITY + ---------- + + Time complexity is O(n) because we are iterating through the list once. + + Space complexity is O(n) because we are storing the prefix sum array. + """ + + def __init__(self, nums: list[int]) -> None: + self.prefix_sum = list(accumulate(nums)) + + def sumRange(self, left: int, right: int) -> int: + # This is the sum of the elements from 0 to right, inclusive. + rvalue = self.prefix_sum[right] + + # We'll want to subtract out the sum of elements from 0 to left, excluding the left element. This means we + # should calculate the sum from 0 to left - 1, giving us prefixSum[left - 1]. + # + # If the left index is 0, then we don't need to subtract anything, so the value is 0. + lvalue = self.prefix_sum[left - 1] if left > 0 else 0 + + return rvalue - lvalue diff --git a/src/leetcode/prefix_sum/running_sum_of_1d_array.py b/src/leetcode/prefix_sum/running_sum_of_1d_array.py new file mode 100644 index 0000000..23550dd --- /dev/null +++ b/src/leetcode/prefix_sum/running_sum_of_1d_array.py @@ -0,0 +1,30 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given an array nums. We define a running sum of an array as runningSum[i] = sum(nums[0]…nums[i]). +# +# Return the running sum of nums. +# +# See https://leetcode.com/problems/running-sum-of-1d-array +from itertools import accumulate + + +class Solution: + """ + SOLUTION + -------- + + This is known as a prefix sum. + + See https://en.wikipedia.org/wiki/Prefix_sum + + COMPLEXITY + ---------- + + Time complexity is O(n) because we are iterating through the list once. + + Space complexity is O(n) because we are storing the prefix sum array. + """ + + def runningSum(self, nums: list[int]) -> list[int]: + return list(accumulate(nums)) diff --git a/src/leetcode/prefix_sum/subarray_sum_equals_k.py b/src/leetcode/prefix_sum/subarray_sum_equals_k.py new file mode 100644 index 0000000..4c2c9b8 --- /dev/null +++ b/src/leetcode/prefix_sum/subarray_sum_equals_k.py @@ -0,0 +1,68 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. +# +# A subarray is a contiguous non-empty sequence of elements within an array. +# +# See https://leetcode.com/problems/subarray-sum-equals-k +class Solution: + def subarraySum(self, nums: list[int], k: int) -> int: + """ + SOLUTION + -------- + + Many fixed size subarray problems can be solved using the sliding window technique. Here, if we ONLY had + positive numbers, we could use the sliding window technique to find subarrays that sum to k. However, there + could be negative numbers and zeroes, so we can't do it. + + The brute force approach is to generate all subarrays and check if the sum equals k. This is O(n^2) time + complexity. Instead, we'll use a prefix sum and hashmap to reduce the time complexity to O(n). + + - Use a prefix sum to track sums up to each index. + - Use a hashmap to track the frequency of each sum. + - Check if a prefix sum difference equals k to find valid subarrays. + + That is, if prefix[j] - prefix[i] = k, then we know that a subarray from (i, j] has a sum of k. Rearranging, we + get then prefix[j] = prefix[i] - k. If prefix[i] exists in our map, we know at least one subarray exists that + sums to k. + + COMPLEXITY + ---------- + + Time complexity is O(n) because we are iterating through the list once. + + Space complexity is O(n) because we are storing the prefix sum array. + """ + # This is the number of subarrays that sum to k. + result = 0 + + # This is prefix sum; it's not actually necessary to create an array to store the prefix sum. We can just keep + # a running sum. + pj = 0 + + # This is our map of prefix sums to their frequency. + # + # Note that we do need to maintain a map here, instead of just a set of prefix sums. This is because the array + # could have negative numbers, repeated values, and zeroes. This would cause prefix sums to repeat. + # + # Don't forget to seed the map with a prefix sum of 0 with frequency 1. Without this seed, we'd miss subarrays + # that start at the beginning of the array. + mapping: dict[int, int] = {0: 1} + + for num in nums: + pj += num + + # If prefix[j] - prefix[i] = k, then we know for sure that the subarray from (i, j] has a sum of k. We can + # compute prefix[i] = prefix[j] - k. + pi = pj - k + + # If prefix[j] appears in the frequency map, we have at LEAST one subarray that sums to k. Add the total + # frequency to the result. + result += mapping.get(pi, 0) + + # Now update the frequency map to account for this new prefix sum up to j. + freq = mapping.get(pj, 0) + mapping[pj] = freq + 1 + + return result diff --git a/src/leetcode/recursion/generate_parentheses.py b/src/leetcode/recursion/generate_parentheses.py new file mode 100644 index 0000000..9790073 --- /dev/null +++ b/src/leetcode/recursion/generate_parentheses.py @@ -0,0 +1,52 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. +# +# See https://leetcode.com/problems/generate-parentheses +class Solution: + def generateParenthesis(self, n: int) -> list[int]: + """ + SOLUTION + -------- + + A naive way to do this would be to do something like generate all the parentheses for n-1, then add more + parentheses to each of the elements generated. For example, by transforming each element of the n-1 solution to + `${e}()`, `()${e}`, and `($e)`. + + This doesn't work for values like n=4, because you will miss solutions like `(())(())`. Instead, we'll + construct the string as we go along, keeping track of how many open and close parentheses we have used. In + total, the resulting strings will each have length n*2. + + This is because well formed strings have additional constraints; when generating a power set using the naive + method, for example, you don't need to worry about balancing the elements within the sub set. In contrast to + generating a power set, you would have far fewer resulting elements. + + COMPLEXITY + ---------- + + Time complexity is uh... we'll just go with O(4^n) since we can generate all possible 2 * n length strings using + characters '(' and ')'. With 2 characters, it's 2^(2 * n) = 4^n. + + Space complexity is O(4^n). + """ + if n == 0: + return [] + + result = [] + + def generate(text, opens: int, closes: int): + # The max length of the string is n * 2 because each open paren requires a close paren. We can't get fewer + # than n * 2 characters in a string either, because we have to generate all possible combinations. + if len(text) == n * 2: + result.append(text) + return + + if opens < n: + generate(text + "(", opens + 1, closes) + + if closes < opens: + generate(text + ")", opens, closes + 1) + + generate("", 0, 0) + return result diff --git a/src/leetcode/recursion/letter_combinations_phone_number.py b/src/leetcode/recursion/letter_combinations_phone_number.py new file mode 100644 index 0000000..48a90ae --- /dev/null +++ b/src/leetcode/recursion/letter_combinations_phone_number.py @@ -0,0 +1,66 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could +# represent. Return the answer in any order. +# +# A mapping of digits to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any +# letters. +# +# See https://leetcode.com/problems/letter-combinations-of-a-phone-number +class Solution: + def letterCombinations(self, s: str) -> list[str]: + """ + SOLUTION + -------- + + This can be done recursively; first generate all combinations for string of digits minus the head digit. Then + add letters to each combination of the rest. + + COMPLEXITY + ---------- + + Time complexity is O(4^n) where n is the number of digits in the input string. We are branching max 4 times per + digit. + + Space complexity is O(n) where n is the number of digits in the input string. + """ + table: dict[str, str] = { + "2": "abc", + "3": "def", + "4": "ghi", + "5": "jkl", + "6": "mno", + "7": "pqrs", + "8": "tuv", + "9": "wxyz", + } + + def generate(t: str) -> list[str]: + nonlocal table + + if len(t) == 0: + return [] + + if len(t) == 1: + return list(table[t]) + + # Separate the first digit and the rest of the digits. + first = t[0] + rest = t[1:] + + # Find all combinations of words possible just from the rest of the digits. + combinations = generate(rest) + + # Get the letters possible from the first digit. + letters = table[first] + + # Now insert the letters possible from the first digit in front of each combination. + result = [] + for combination in combinations: + for letter in letters: + result.append(letter + combination) + + return result + + return generate(s) diff --git a/src/leetcode/recursion/maximum_number_of_operations_with_the_same_score.py b/src/leetcode/recursion/maximum_number_of_operations_with_the_same_score.py new file mode 100644 index 0000000..50a0cc5 --- /dev/null +++ b/src/leetcode/recursion/maximum_number_of_operations_with_the_same_score.py @@ -0,0 +1,94 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an array of integers called nums, you can perform any of the following operation while nums contains at least +# 2 elements: +# +# - Choose the first two elements of nums and delete them. +# - Choose the last two elements of nums and delete them. +# - Choose the first and the last elements of nums and delete them. +# +# The score of the operation is the sum of the deleted elements. +# +# Your task is to find the maximum number of operations that can be performed, such that all operations have the same +# score. +# +# Return the maximum number of operations possible that satisfy the condition mentioned above. +# +# See https://leetcode.com/problems/maximum-number-of-operations-with-the-same-score-ii +class Solution: + def maxOperations(self, nums: list[int]) -> int: + """ + SOLUTION + -------- + + The range of possible scores is determined by applying the operation once. Subsequent operations must produce + the same score, so there are only three possible operations. For each operation, we should apply them, then + follow paths of operations that result in the same score until the array is exhausted. + + We COULD use the sliding window technique by applying each operation and then only following the operation paths + that have the same score as the one we are considering. However, this would only work if at each choice of + three operations, the scores are unique. If there are two or more choices with the same score, then we need to + explore multiple operation paths at once. + + To explore multiple operation paths at once, we'll need to use a backtracking approach. + + COMPLEXITY + ---------- + + Time complexity is O(3^n) because we are exploring all possible operations. + + Space complexity is O(n) because we are storing the operations in stack frames. + """ + if len(nums) < 2: + return 0 + + # These are the range of possible scores we can have as we truncate the array. + scores: set[int] = set() + scores.add(nums[0] + nums[1]) + scores.add(nums[0] + nums[-1]) + scores.add(nums[-1] + nums[-2]) + + # There is an opportunity for optimization when using backtracking. We can maintain a memoization table to keep + # track of previously computed values. + memo: dict[tuple[int, int, int], int] = {} + + # Adapt the sliding window approach so we can explore multiple paths at once by backtracking. + def backtrack(score: int, left: int, right: int, paths: int) -> int: + if left >= right: + return paths + + # Check the memoization table to see if we've previously computed this value. + if (score, left, right) in memo: + return memo[(score, left, right)] + + # This is the local max count, after exploring all possible paths. + local = paths + + # Check if the first 2 elements are equal to the score, and if the left and right indices are in range. + # + # To be in range, the array must at least look like this: [left, left + 1/right]. + if left + 1 <= right and nums[left] + nums[left + 1] == score: + local = max(local, backtrack(score, left + 2, right, paths + 1)) + + # Check if the last 2 elements are equal to the score, and if the left and right indices are in range. + # + # To be in range, the array must at least look like this: [left/right - 1, right] + if left <= right - 1 and nums[right] + nums[right - 1] == score: + local = max(local, backtrack(score, left, right - 2, paths + 1)) + + # Check if the first and last elements are equal to the score, and if the left and right indices are in + # range. + # + # To be in range, the array must at least look like this: [left, right] + if left < right and nums[left] + nums[right] == score: + local = max(local, backtrack(score, left + 1, right - 1, paths + 1)) + + # Update the memoization table. + memo[(score, left, right)] = local + return local + + # For each score, check out how many times we can keep applying operations. + xs = list(scores) + maxes = [backtrack(x, 0, len(nums) - 1, 0) for x in xs] + return max(maxes) diff --git a/src/leetcode/recursion/optimal_account_balancing.py b/src/leetcode/recursion/optimal_account_balancing.py new file mode 100644 index 0000000..9f7ee91 --- /dev/null +++ b/src/leetcode/recursion/optimal_account_balancing.py @@ -0,0 +1,107 @@ +# DIFFICULTY: HARD +# ---------------- +# +# You are given an array of transactions transactions where transactions[i] = [fromi, toi, amounti] indicates that the +# person with ID = fromi gave amounti $ to the person with ID = toi. +# +# Return the minimum number of transactions required to settle the debt. +# +# See https://leetcode.com/problems/optimal-account-balancing +import math + + +class Solution: + def minTransfers(self, transactions: list[list[int]]) -> int: + """ + SOLUTION + -------- + + We can model this problem as a graph where each person is a node and the amount they owe or are owed is the + weight of the edge between them. The goal is to find the minimum number of transactions to settle all debts. + + We can use a recursive backtracking algorithm to find the minimum number of transactions. We will iterate + through each person and try to settle their debt with every other person. If the debt is settled, we will move + on to the next person. If the debt is not settled, we will try to settle the debt with another person. + + COMPLEXITY + ---------- + + Time complexity is O(2^n) because we are iterating through all possible transactions. + + Space complexity is O(n) because we are using recursion to solve the problem. + """ + # Create a map of person -> amount. + mapping: dict[int, int] = {} + + # First, figure out the balance that each person holds. + for transaction in transactions: + [from_person, to_person, amount] = transaction + mapping[from_person] = mapping.get(from_person, 0) - amount + mapping[to_person] = mapping.get(to_person, 0) + amount + + # Second, figure out all non-zero balances. If we needed to know who is paying whom to achieve the minimum + # number of transactions, we will need to have an array indexed by person. However, we don't need to know that. + # + # We just need to know the number of transactions, so we can just get a list of non-zero balances and try to + # zero them out. + balances: list[int] = [] + for value in mapping.values(): + if value != 0: + balances.append(value) + + # To settle debts efficiently, we should should match opposite balances together, and have them settle with each + # other. For example, if somebody has +5 balance, we should match then up with a -5 balance. Matching two + # balances with the same sign together will just result in extra transactions. + # + # Therefore, we should examine sequences of pairs that work towards an all zero balance sheet. However, + # different sequences of pairs could result in different numbers of transactions. So we will need to examine + # *all* possible pairs and keep track of the minimum tx's across all sequences. + # + # To do this effectively we need to use a recursive backtracking technique by trying to settle the ith balance + # with i+1, i+2, i+3, etc and seeing how many tx's we incur at each step. + def settle(left: int): + # Skip over the already settled balances; if we've skipped over all balances, the minimum number of tx's is + # zero. + while left < len(balances) and balances[left] == 0: + left += 1 + + if left == len(balances): + return 0 + + # Try to settle the ith balance with the first opposite signed balance. + min_transactions = math.inf + for right in range(left + 1, len(balances)): + # These balances were either both positive, or both negative, so do not bother executing a transaction + # with them. + if balances[left] * balances[right] > 0: + continue + + # Attempt to settle (i, j) by adding the ith balance to the jth balance. Technically we don't have to + # decrement balance[i] at all, since the next call to settle will start at j (and i will be skipped). + # But it is included here for clarity. + # + # Note that we also do not have to ensure that we only add the minimum amount to balance. For example, + # if # balances[i] = 10 and balances[j] = -5, we don't need to only add 5 to balances[j]. As we + # recursively move down the list, balances[j] will eventually zero out (and a more efficient path may be + # discovered). + # + # Also, we are guaranteed that the balances *do* eventually zero out because there isn't any extra money + # in the system. + value = balances[left] + balances[right] += value + balances[left] = 0 + + # The minimum number of transactions, if we've settled the ith balance, is 1 + the minimum after + # settling the i+1th balance. + # + # As a further optimization we can use dynamic programming to avoid redundant calculations. To do so + # we'll need to use a bit mask to keep track of balance state. + min_transactions = min(min_transactions, 1 + settle(left + 1)) + + # Backtrack by undoing the transaction. + balances[right] -= value + balances[left] = value + + return min_transactions + + return int(settle(0)) diff --git a/src/leetcode/recursion/permutations.py b/src/leetcode/recursion/permutations.py new file mode 100644 index 0000000..8c88521 --- /dev/null +++ b/src/leetcode/recursion/permutations.py @@ -0,0 +1,43 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order. +# +# See https://leetcode.com/problems/permutations +class Solution: + def permute(self, nums: list[int]) -> list[list[int]]: + """ + SOLUTION + -------- + + To do this, recursively generate all permutations of elements without the head. Then take all permutations + created and add x to the front of the list. This should result in n! number of arrays. + + The general strategy is to split the list into a head and a tail. Generate all the permutations of the list + without the head element, then put the head in front of each of the generated permutations. Do this recursively + for each element in the list will give all permutations. + + COMPLEXITY + ---------- + + Time complexity is O(n!) where n is the number of elements in the list. + + Space complexity is O(n) because we are storing the permutations in stack frames. + """ + if not nums: + return [] + + if len(nums) == 1: + return [nums] + + result: list[list[int]] = [] + for i, x in enumerate(nums): + # Generate all permutations of the list without x. + without = nums[:i] + nums[i + 1 :] + ps = self.permute(without) + + # For each of the permutations, put the ith element in front of the permutations. + for p in ps: + result.append([x] + p) + + return result diff --git a/src/leetcode/recursion/subsets.py b/src/leetcode/recursion/subsets.py new file mode 100644 index 0000000..7dc081c --- /dev/null +++ b/src/leetcode/recursion/subsets.py @@ -0,0 +1,46 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums of unique elements, return all possible subsets (the power set). +# +# The solution set must not contain duplicate subsets. Return the solution in any order. +# +# See https://leetcode.com/problems/subsets +class Solution: + def subsets(self, nums: list[int]) -> list[list[int]]: + """ + SOLUTION + -------- + To do this, recursively generate all combinations of elements of the set `xs - x`, then take all the subsets + created and add x to them. This should result in 2^n number of subsets. + + COMPLEXITY + ---------- + Time complexity is O(2^n) where n is the number of elements in the list. + + Space complexity is O(n) because we are storing the subsets in stack frames. + """ + + # Note that you cannot put a set inside of a set; sets are mutable and cannot be hashed. However, frozensets + # are immutable, so you can use those instead. + def generate(xs: set[int]) -> set[frozenset[int]]: + if not xs: + # Note that set(frozenset()) creates an empty set, not a set with an empty set in it. + return {frozenset()} + + # Find the first element of xs and call it x; remove it from the set and call it rest. + ys = list(xs) + x = ys[0] + rest = set(ys[1:]) + + # Generate all subsets of that set. + excludeds = generate(rest) + + # Now generate all subsets with x by adding x to each excluded subset. + includeds = {subset | {x} for subset in excludeds} + + # Combine the excluded and included subsets. + return excludeds | includeds + + result = generate(set(nums)) + return [list(subset) for subset in result] diff --git a/src/leetcode/recursion/word_break_ii.py b/src/leetcode/recursion/word_break_ii.py new file mode 100644 index 0000000..a95d907 --- /dev/null +++ b/src/leetcode/recursion/word_break_ii.py @@ -0,0 +1,71 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Given a string s and a dictionary of strings wordDict, add spaces in s to construct a sentence where each word is a +# valid dictionary word. Return all such possible sentences in any order. +# +# Note that the same word in the dictionary may be reused multiple times in the segmentation. +# +# See https://leetcode.com/problems/word-break-ii +class Solution: + def wordBreak(self, s: str, wordDict: list[str]) -> list[str]: + """ + SOLUTION + -------- + + To do this, we can use a recursive backtracking algorithm. + + COMPLEXITY + ---------- + + Time complexity is O(2^n) where n is the number of words in the string. + + Space complexity is O(n) because we are storing the words in stack frames. + """ + uniques = set(wordDict) + memo: dict[int, list[str]] = {} + + # This generates all possible sentences using words that start at index i (if any exist) recursively. + # + # Since the only words we have available to us are the ones in the wordDict, we'll just have to loop through + # from i to the end of the string to see if any of those substrings form a 'word'. + def generate(i: int) -> list[str]: + # Because we are slicing from [i, j), and slice is exclusive on the second index, we will be starting at + # i + 1. Therefore, do the bounds check for i == len(s). + if i == len(s): + return [] + + if i in memo: + return memo[i] + + sentences: list[str] = [] + # Attempt to generate sentences beginning with the 'word' from i to j. Note that we want to slice from i to + # j, so we actually want j to start at i + 1, and we also want to include j === s.length, because the slice + # function is inclusive for the first index, and exclusive for the second index. + for j in range(i + 1, len(s) + 1): + word = s[i:j] + if word not in uniques: + continue + + # Now generate all possible sentences using words that start at index j (if any exist), and put the word + # from index i in front. + rest = generate(j) + + # If the problem allowed us to generate sentences using a partial selection of characters (e.g. if we + # couldn't make more words at this point), we could just add the current word to the result, even if + # there were unused characters remaining. + # + # However, the problem does not allow us to do this, so we only add the current word if there are no + # unused characters remaining. + if len(rest) == 0 and j == len(s): + sentences.append(word) + continue + + # If we could generate words starting at j, prepend the word starting at index i. + for sentence in rest: + sentences.append(word + " " + sentence) + + memo[i] = sentences + return sentences + + return generate(0) diff --git a/src/sliding-window/README.md b/src/leetcode/sliding_window/README.md similarity index 71% rename from src/sliding-window/README.md rename to src/leetcode/sliding_window/README.md index eddd942..02a59c9 100644 --- a/src/sliding-window/README.md +++ b/src/leetcode/sliding_window/README.md @@ -4,13 +4,13 @@ These phrases indicate the sliding window technique might be useful: -- 'fixed-size subarray with a condition' -- 'longest subarray with a condition' -- 'at most k', 'at least k', 'no more than k' -- 'max sum over a range', 'min sum over a range' -- 'continuous subarray' -- 'substring' -- 'dynamic constraints' +- fixed-size subarray with a condition +- longest subarray with a condition +- at most k, at least k, no more than k +- max sum over a range, min sum over a range +- continuous subarray +- substring +- dynamic constraints ## Notable Problems @@ -18,7 +18,7 @@ These phrases indicate the sliding window technique might be useful: The [sliding window maximum](https://leetcode.com/problems/sliding-window-maximum/) (hard) problem can be solved with a deque; the sliding window itself is implicitly maintained by the deque. -The above resembles the [maximum subarray](https://leetcode.com/problems/maximum-subarray/) (medium) problem, which can be solved optimally using [Kadane's Algorithm](https://en.wikipedia.org/wiki/Maximum_subarray_problem), or with prefix sum for a more direct and explicit solution. +The above resembles the [maximum subarray](https://leetcode.com/problems/maximum-subarray/) (medium) problem, which can be solved optimally using [Kadanes Algorithm](https://en.wikipedia.org/wiki/Maximum_subarray_problem), or with prefix sum for a more direct and explicit solution. ### Longest Substring with at Most K Distinct Characters diff --git a/src/leetcode/sliding_window/best_time_to_buy_and_sell_stock.py b/src/leetcode/sliding_window/best_time_to_buy_and_sell_stock.py new file mode 100644 index 0000000..20ae8d4 --- /dev/null +++ b/src/leetcode/sliding_window/best_time_to_buy_and_sell_stock.py @@ -0,0 +1,52 @@ +# DIFFICULTY: EASY +# ---------------- +# +# You are given an array prices where prices[i] is the price of a given stock on the ith day. +# +# You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future +# to sell that stock. +# +# Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0. +# +# See https://leetcode.com/problems/best-time-to-buy-and-sell-stock +import math + + +class Solution: + def maxProfit(self, prices: list[int]) -> int: + """ + SOLUTION + -------- + + The question is a bit contrived, as in reality this would never happen. Here, we are assuming we can go + backwards in time to be able to buy at the low point and sell at the high point. Just keep that in mind: we + have a time machine. + + This type of problem is easily solved with a modified version of the sliding window technique. We will maintain + a minimum price and update that value whenever we see a lower value, from there we can calculate proposed profit + on any given day. + + By iterating through the array we'll find the maximum profit at the end. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of prices in the list. + + Space complexity is O(1) because we are only storing the minimum price and maximum profit. + """ + min_price = math.inf + max_profit = 0 + + for price in prices: + # If the price ends up being lower than what we've ever seen, buy it and see if we can profit. + if price < min_price: + min_price = price + continue + + # Unfortunately, it can be the case that we bought at a low point but the stock continues or fall or trades + # flat. If that is the case, we'll keep our previous value of max profit. + profit = price - min_price + max_profit = max(max_profit, profit) + + return int(max_profit) diff --git a/src/leetcode/sliding_window/count_subarrays_where_max_element_appears.py b/src/leetcode/sliding_window/count_subarrays_where_max_element_appears.py new file mode 100644 index 0000000..813626f --- /dev/null +++ b/src/leetcode/sliding_window/count_subarrays_where_max_element_appears.py @@ -0,0 +1,50 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an integer array nums and a positive integer k. +# +# Return the number of subarrays where the maximum element of nums appears at least k times in that subarray. +# +# A subarray is a contiguous sequence of elements within an array. +# +# See https://leetcode.com/problems/count-subarrays-where-max-element-appears-at-least-k-times +class Solution: + def countSubarrays(self, nums: list[int], k: int) -> int: + """ + SOLUTION + -------- + + Use the sliding window technique to count the number of subarrays with at least k elements. Expand the window + from the right until we have k elements, counting occurrences of the max element. Shrink the window from the + left when the maximum element appears at least k times in the window. + + COMPLEXITY + ---------- + Time complexity is O(n) where n is the number of elements in the list. + + Space complexity is O(n) because we are storing the number of times each element appears in the window. + """ + max_element = max(nums) + + # The number of subarrays where the maximum element appears at least k times. + subarrays = 0 + + # The number of times max_element appears in the current window. + count = 0 + + # Use the sliding window technique to count the number of subarrays with at least k elements. + left = 0 + right = 0 + for right in range(len(nums)): + if nums[right] == max_element: + count += 1 + + while count >= k: + subarrays += len(nums) - right + + if nums[left] == max_element: + count -= 1 + + left += 1 + + return subarrays diff --git a/src/leetcode/sliding_window/length_of_longest_substring.py b/src/leetcode/sliding_window/length_of_longest_substring.py new file mode 100644 index 0000000..b80eb3e --- /dev/null +++ b/src/leetcode/sliding_window/length_of_longest_substring.py @@ -0,0 +1,55 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a string s, find the length of the longest substring without repeating characters. +# +# See https://leetcode.com/problems/longest-substring-without-repeating-characters +class Solution: + def lengthOfLongestSubstring(self, s: str) -> int: + """ + SOLUTION + -------- + + This can be solved using the sliding window technique by maintaining, always, a window where we have a substring + without repeating characters. + + To ensure we can check this constraint, we'll keep track of the last time we have seen a character, so we can + move the left pointer to the right of the left seen index and maintain our no-repeat constraint. + + Once we move the left pointer just one step to the right of the last seen index, we'll update that seen index + to the rightmost position we've seen it, so we can leverage the map again next time. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of characters in the string. + + Space complexity is O(1) because the number of characters in the alphabet is fixed. + """ + # Keep a map of last seen character -> index. + seen: dict[str, int] = {} + + # The length of the longest substring without repeating characters. + max_length = 0 + + # Use a sliding window approach by moving the right pointer along the string's characters, and updating the left + # pointer any time we see a repeated character. + left = 0 + right = 0 + while right < len(s): + c = s[right] + + # If we've already seen this character, update the left pointer and move it just right of the last seen + # occurrence to ensure no repeated characters. + if c in seen: + last = seen[c] + left = max(left, last + 1) + + # Update our asumption about the current max. + max_length = max(max_length, right - left + 1) + + # Set the current character to be seen and advance the right pointer. + seen[c] = right + right += 1 + + return max_length diff --git a/src/leetcode/sliding_window/longest_continuous_subarray.py b/src/leetcode/sliding_window/longest_continuous_subarray.py new file mode 100644 index 0000000..09ca1c5 --- /dev/null +++ b/src/leetcode/sliding_window/longest_continuous_subarray.py @@ -0,0 +1,94 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an array of integers nums and an integer limit, return the size of the longest non-empty subarray such that the +# absolute difference between any two elements of this subarray is less than or equal to limit. +# +# See https://leetcode.com/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit +from collections import deque + + +class Solution: + def longestSubarray(self, nums: list[int], limit: int) -> int: + """ + SOLUTION + -------- + + Use a sliding window approach to find the max length subarray. We'll have to keep track of what the min/max + elements in the subarray while adjusting our window size. To do so we'll use two deques. + + We'll have a right pointer that moves forward through the array as normal. The left pointer will move forward + through the array only when the subarray constraint (under limit) is violated. As the pointers move, we'll need + to keep track of the smallest and largest elements; that's what the deques are for. + + We can't simply store the max element or min element by itself; as the pointers move, these elements will + change. Instead, we'll have to use our deques to maintain the max/min elements at every window size. The + `minDeque` will have elements in increasing order, and the `maxDeque` will have elements in decreasing order. + We keep the deques organized like this so that the largest element will always be at the front of the + `maxDeque`, and the smallest element will always be at the front of the `minDeque`. + + As we advance the right pointer, we'll have to pop off elements at the back of the deque and then push the right + pointer onto the end of the deque, while maintaining the ascending/descending order. As we advance the left + pointer, we'll shift off the element at the front of the deque if it is less than what we just passed. + + COMPLEXITY + ---------- + Time complexity is O(n) where n is the number of elements in the list. + + Space complexity is O(n) because we are storing the maximum and minimum elements in the window. + """ + # Keeps track of indices whose values are in descending order. This will allow us to quickly find the index of + # the largest element as the window shifts. + max_deque: deque[int] = deque() + + # Keeps track of indices whose values are in ascending order. This will allow us to quickly find the index of + # the smallest element as the window shifts. + min_deque: deque[int] = deque() + + # The current max length of a continuous subarray. + result = 0 + + # Expand the right window while updating our deques. Each element we encounter from the right side will be used + # to update our deques. + left = 0 + for right in range(len(nums)): + value = nums[right] + + # Update the max deque by removing all elements at the end of the deque that are smaller, so we can maintain + # a deque in descending order. + while max_deque and nums[max_deque[-1]] <= value: + max_deque.pop() + max_deque.append(right) + + # Update the min deque by removing all elements at the end of the deque that are bigger, so we can maintain + # a deque in ascending order. + while min_deque and nums[min_deque[-1]] >= value: + min_deque.pop() + min_deque.append(right) + + # Check the conditions of our subarray; if we have exceeded the limit, move the left pointer until we are + # under the limit. + # + # After moving the left pointer, it could be the case that we have to update our min/max deque as it is no + # longer being considered as part of the subarray + min_index = min_deque[0] + max_index = max_deque[0] + while abs(nums[max_index] - nums[min_index]) > limit: + left += 1 + + # If we've moved past the min index, remove the min index from the front of the deque. Note that we are + # comparing indices here, not actual values. + if min_deque[0] < left: + min_deque.popleft() + + # Alternatively, if we've moved past the max index, remove the max index from the front of the deque. + # Note that we are comparing indices here, not actual values. + if max_deque[0] < left: + max_deque.popleft() + + min_index = min_deque[0] + max_index = max_deque[0] + + result = max(result, right - left + 1) + + return result diff --git a/src/leetcode/sliding_window/max_sum_distinct_subarray_of_size_k.py b/src/leetcode/sliding_window/max_sum_distinct_subarray_of_size_k.py new file mode 100644 index 0000000..9df356a --- /dev/null +++ b/src/leetcode/sliding_window/max_sum_distinct_subarray_of_size_k.py @@ -0,0 +1,89 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an integer array nums and an integer k. Find the maximum subarray sum of all the subarrays of nums that +# meet the following conditions: +# +# - The length of the subarray is k, and +# - All the elements of the subarray are distinct. +# +# Return the maximum subarray sum of all the subarrays that meet the conditions. If no subarray meets the conditions, +# return 0. +# +# A subarray is a contiguous non-empty sequence of elements within an array. +# +# See https://leetcode.com/problems/maximum-sum-of-distinct-subarrays-with-length-k +class Solution: + def maximumSubarraySum(self, xs: list[int], k: int) -> int: + """ + SOLUTION + -------- + + Without the distinct requirement, we can use the sliding window technique with a window of size k, shifting the + window as we look for the maximum sum. + + With the distinct element requirement, we need to maintain a set and disregard sub arrays that don't have unique + elements. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of elements in the list. + + Space complexity is O(n) because we are storing the elements in the window. + """ + # Keep track of the current sum of the sub array, as well as the maximum sum we've seen so far. + current_sum = 0 + max_sum = 0 + + # Define a set to check if we have a distinct sub array. We will update the window based on uniqueness and + # compute a sum when we have k elements. + uniques = set() + + # Build up our window by moving the right pointer. We'll update the left pointer along the way in case we + # detect a sub array with duplicate elements. + left = 0 + right = 0 + while right < len(xs): + value = xs[right] + + # If the current element is already in the sub array, advance the left pointer until we no longer have a + # duplicate element. We'll have to decrement the current sum to account for sliding the left pointer + # forward. + # + # Note that xs.remove() will raise if the element is not present, so use xs.discard() instead. + while value in uniques: + uniques.discard(xs[left]) + current_sum -= xs[left] + left += 1 + + # Now that the sub array is free of dupes, we can add the current element to the sum. Let's not update the + # right pointer just yet; if we are over k elements, we want to leave the right pointer where it is for the + # moment. + current_sum += value + uniques.add(value) + + # If, by adding this element, we've gone over k elements, shrink the window by moving the left pointer, + # which should put us at exactly k elements. + # + # Note that we can check if we've violated the size constraint by checking the size of the `uniques` set, + # which forms our contiguous set. By adding a single element, the most we could've gone over by is one + # element, so just check, and if we've done so, slide the left pointer forward again. + # + # Once within constraints we can fall through to the next block. + if len(uniques) > k: + uniques.discard(xs[left]) + current_sum -= xs[left] + left += 1 + + # If the sub array has exactly k elements at this iteration (or after we've shrink the window), update our + # assumptions about the max sum and update the right pointer. + if len(uniques) == k: + max_sum = max(max_sum, current_sum) + right += 1 + + # If the sub array has fewer than k elements, grow the window by increasing the right pointer. + if len(uniques) < k: + right += 1 + + return max_sum diff --git a/src/leetcode/sliding_window/minimum_window_substring.py b/src/leetcode/sliding_window/minimum_window_substring.py new file mode 100644 index 0000000..b249ef0 --- /dev/null +++ b/src/leetcode/sliding_window/minimum_window_substring.py @@ -0,0 +1,113 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Given two strings s and t of lengths m and n respectively, return the minimum window substring of s such that every +# character in t (including duplicates) is included in the window. If there is no such substring, return the empty +# string "". +# +# The testcases will be generated such that the answer is unique. +# +# See https://leetcode.com/problems/minimum-window-substring +from collections import defaultdict +import math + + +class Solution: + def minWindow(self, s: str, t: str) -> str: + """ + SOLUTION + -------- + + This can be solved using the sliding window technique with a bunch of extra bookkeeping. The right pointer will + be expanded until we have a valid window, then the left pointer will be shrunk to the minimum size window that + still satisfies the requirements. + + COMPLEXITY + ---------- + + Time complexity is O(m + n) where m and n are the lengths of the source and target strings. + + Space complexity is O(m + n) because we store character frequency of the source and target strings. + """ + # It is not possible to return a minimum window here; the target substring MUST be shorter than the source + # string. + if len(t) > len(s): + return "" + + # First we need to create a frequency map, so we can easily check if a substring has all the characters in t. + # This is required because the characters in t can be duplicated, and we require the same number of duplicates + # in the source substring. + want: dict[str, int] = defaultdict(int) + for c in t: + want[c] += 1 + + # Define a map to keep track of the characters we have seen so far in the window. This will let us know when we + # have a valid window. + # + # We also keep track of which characters we've 'gotten' so far. That is, if we have a requirement to contain X + # number of 'a' characters, and we have seen X characters, then we have 'gotten' that character. When we have + # 'gotten' all characters in the string, we have a valid window. + got: dict[str, int] = defaultdict(int) + gotten = 0 + + # Keep track of our minimum window substring. The left pointer should point to the start, and the size will + # tell us the length of the window, allowing us to call slice the string later. + # + # If we run through the algorithm and don't get a valid window, because min_size never got updated, then we + # return ''. + min_left = 0 + min_size = math.inf + + # Define both pointers to start at the source string, and start by expanding the right pointer. Once a valid + # window is discovered, we contract the left pointer. + left = 0 + right = 0 + + # Begin by expanding the right pointer until we have a valid window. + while right < len(s): + # Update the frequency of the character that we have got. + c = s[right] + got[c] += 1 + + # If it turns out that we got exactly the frequency of character c that we wanted, then the requirement to + # contain X number of character c has been satisfied. + if c in want and got[c] == want[c]: + gotten += 1 + + # If we've gotten all the characters at exactly the right frequency, then we have a valid window. In that + # case, we can begin shrinking the window to see if it stays valid. + while gotten == len(want): + k = s[left] + + # Calculate the size of the window. Note that left and right are INCLUSIVE indexes, so if we want the + # window size of say [0, 1, 2, 3] with left = 0 and right = 3 (a window size of 4), we should do + # 3 - 0 + 1 = 4. + # + # So make sure to add 1 here to get the correct size. + size = right - left + 1 + if size < min_size: + min_size = size + min_left = left + + # Now try to contract the window by moving the left pointer. When we do this, we have to update the + # frequency of characters we've gotten so far, and update the gotten count. + # + # Update the frequency of the character that we are about to lose. Then if we have got fewer characters + # than wanted, we decrement the 'gotten' count because we no longer have a valid window. + got[k] -= 1 + if k in want and got[k] < want[k]: + gotten -= 1 + + # Move the left pointer to the right. + left += 1 + + # Keep expanding the right window until we have a valid window. + right += 1 + + # This means we didn't find any valid window. + if min_size == math.inf: + return "" + + i = min_left + j = min_left + min_size + return s[i:j] diff --git a/src/leetcode/sliding_window/moving_average_from_data_stream.py b/src/leetcode/sliding_window/moving_average_from_data_stream.py new file mode 100644 index 0000000..65a2066 --- /dev/null +++ b/src/leetcode/sliding_window/moving_average_from_data_stream.py @@ -0,0 +1,40 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given a stream of integers and a window size, calculate the moving average of all integers in the sliding window. +# +# Implement the MovingAverage class: +# +# - MovingAverage(int size) Initializes the object with the size of the window size. +# - double next(int val) Returns the moving average of the last size values of the stream. +# +# See https://leetcode.com/problems/moving-average-from-data-stream +from collections import deque + + +class MovingAverage: + def __init__(self, size: int) -> None: + """ + SOLUTION + -------- + + This can be solved with a queue or sliding window. + + COMPLEXITY + ---------- + + Time complexity is O(1) for next. + + Space complexity is O(n) where n is the size of the window. + """ + self.size = size + self.window: deque[int] = deque() + self.total = 0 + + def next(self, val: int) -> float: + if len(self.window) == self.size: + self.total -= self.window.popleft() + + self.window.append(val) + self.total += val + return self.total / len(self.window) diff --git a/src/leetcode/sliding_window/repeated_dna_sequences.py b/src/leetcode/sliding_window/repeated_dna_sequences.py new file mode 100644 index 0000000..0c4b7aa --- /dev/null +++ b/src/leetcode/sliding_window/repeated_dna_sequences.py @@ -0,0 +1,55 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# The DNA sequence is composed of a series of nucleotides abbreviated as 'A', 'C', 'G', and 'T'. +# +# For example, "ACGAATTCCG" is a DNA sequence. +# When studying DNA, it is useful to identify repeated sequences within the DNA. +# +# Given a string s that represents a DNA sequence, return all the 10-letter-long sequences (substrings) that occur +# more than once in a DNA molecule. You may return the answer in any order. +# +# See https://leetcode.com/problems/repeated-dna-sequences +class Solution: + def findRepeatedDnaSequences(self, s: str) -> list[str]: + """ + SOLUTION + -------- + + We can use a sliding window to iterate through the string and keep track of the substrings we've seen so far. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the string. + + Space complexity is O(n) because we are storing the substrings in a set. + """ + if len(s) < 10: + return [] + + seen: set[str] = set() + repeated: set[str] = set() + + # We can use a sliding window to keep track of 10 character sequences, then if we have seen the sequence, add it + # to the result. + left = 0 + for right in range(9, len(s)): + # Two options exist here, because substring takes the leftmost element (inclusive) and rightmost element + # (exclusive). + # + # i) We could calculate right - left + 1 to account for us slicing from [0, 9 + 1). + # ii) We could start right = 10, then set the loop condition to be right <= s.length instead of just <. + # + # The second approach makes it easier to avoid off by 1, but we do this here to show you have to tackle the + # off by 1 head on. + if right - left + 1 > 10: + left += 1 + + sequence = s[left : right + 1] + if sequence in seen: + repeated.add(sequence) + else: + seen.add(sequence) + + return list(repeated) diff --git a/src/leetcode/stack/README.md b/src/leetcode/stack/README.md new file mode 100644 index 0000000..3f79985 --- /dev/null +++ b/src/leetcode/stack/README.md @@ -0,0 +1,9 @@ +# Stack + +## Common Phrases + +These phrases indicate a stack might be useful: + +- balanced delimiter (like parentheses), valid delimiters (like brackets) +- next greater, previous smaller +- undo diff --git a/src/leetcode/stack/basic_calculator.py b/src/leetcode/stack/basic_calculator.py new file mode 100644 index 0000000..462fc4d --- /dev/null +++ b/src/leetcode/stack/basic_calculator.py @@ -0,0 +1,83 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Given a string s representing a valid expression, implement a basic calculator to evaluate it, and return the result +# of the evaluation. +# +# Note: You are not allowed to use any built-in function which evaluates strings as mathematical expressions, such as +# eval(). +# +# See https://leetcode.com/problems/basic-calculator +class Solution: + def calculate(self, s: str) -> int: + """ + SOLUTION + -------- + + This is way harder than basic calculator ii, even though we don't consider multiplication and division. Here, + we have to consider parentheses. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the string. + + Space complexity is O(n) because we are storing the numbers in a stack. + """ + stack: list[int] = [] + + # The sign of the current number. + sign = 1 + + # The current number. + number = 0 + + # The current sub expression value. + expr = 0 + + for c in s: + if c.isdigit(): + number = number * 10 + int(c) + elif c == "+": + # Whatever the current number is, add it to the current expression. The sign will account for + # subtractions. + expr += sign * number + + # Now we reset the sign to be POSITIVE and number to be 0. + sign = 1 + number = 0 + elif c == "-": + # Whatever the current number is, add it to the current expression. The sign will account for + # subtractions. + expr += sign * number + + # Now we reset the sign to be NEGATIVE and number to be 0. + sign = -1 + number = 0 + elif c == "(": + # Opening brace means we are evaluating an expression. Whatever the current expression and sign are, + # push it onto the stack. + stack.append(expr) + stack.append(sign) + + # Now we reset the entire calculator (result, sign, current number) to evaluate the expression. + expr = 0 + sign = 1 + number = 0 + elif c == ")": + # Closing brace means we are finished evaluating an expression. Resolve the current operation and + # add it to the current expression result. + expr += sign * number + + # Pop off the sign. If we had a NEGATIVE sign when opening the brace, the entire expression needs to be + # negated. That is, '-(1+2)' would require us to do result *= -1. + expr *= stack.pop() + + # Pop off the original result and add it to the expression. + expr += stack.pop() + + # Now reset the calculator. + sign = 1 + number = 0 + + return expr + sign * number diff --git a/src/leetcode/stack/basic_calculator_ii.py b/src/leetcode/stack/basic_calculator_ii.py new file mode 100644 index 0000000..3c0d9b9 --- /dev/null +++ b/src/leetcode/stack/basic_calculator_ii.py @@ -0,0 +1,126 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a string s which represents an expression, evaluate this expression and return its value. +# +# The integer division should truncate toward zero. +# +# You may assume that the given expression is always valid. All intermediate results will be in the range of +# [-231, 231 - 1]. +# +# Note: You are not allowed to use any built-in function which evaluates strings as mathematical expressions, such as +# eval(). +# +# See https://leetcode.com/problems/basic-calculator-ii +class Solution: + def calculate(self, s: str) -> int: + """ + SOLUTION + -------- + + Assumption is that we only have numbers, +, -, *, and / in the expression. We also assume no parentheses for + grouping expressions. + + To do this we iterate through the string and use a stack to store intermediate results of computation. Because + the '*' and '/' operators have higher precedence, we'll resolve them immediately. Those intermediate results + will be stored onto the stack and we'll resolve the '+' and '-' operators at the end. + + Take note that the '-' operator can be a unary operator. + + Here's an example evaluation. Let's suppose we have '33+2*2'. Whenever we resolve an operator, OR start the + calculator, we'll assume the first number is 0 (no value) and the last operator is '+'. + + - When we see c = '3', we'll store it as the current value as '3'. + - When we see c = '3' again, we'll set the current value as '3 *10 + 3' or '33'. + - When we see c = '+', we'll resolve the current value '33' on the stack. In this step, the current operator c + is '+' and the last operator is also '+'. Based on the last operator being '+', we push onto the stack. Now + we set the last operator to '+'. + - When we see c = '2', we'll store it as the current value as '2'. + - When we see c = '*', we'll resolve the current value '2'. In this step, the current operator 'c' is '*' and + the last operator is '+'. Based on the last operator being '+', we push onto the stack. Now we set the last + operator to '*'. + - When we see c = '2', we'll store it as the current value as '2'. + - Because it is the last character, we resolve the last operator, which is '*'. So we pop from the stack ('2'), + and multiply by '2', giving a result '4', which we push to the stack. + - Finally we sum the stack. + + In a situation where the last operator is '-', we would push a negative value onto the stack. For example, if + we have '3-2', when we see '2', the last operator is '-', so we push '-2' onto the stack. + + Similarly, if we just have '-2', we will see the '2' and push '-2' onto the stack, since the last operator is + '-'. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the string. + + Space complexity is O(n) because we are storing the numbers in a stack. + """ + # Keep a stack of numbers to add up; the multiply and divide operations will be applied immediately since they + # have higher precedence. + stack: list[int] = [] + + # Keep track of the current value as we iterate through the string. Every time we see a digit, we can multiply + # the number by 10 and add the digit to it. + value = 0 + + # Note that the action we take when we see an operator depends on the LAST operator we saw, not the current + # operator we see. + # + # Before we've seen any operators, the default behavior is to add (not subtract or something), so set the last + # operator to '+'. + last = "+" + + for i, c in enumerate(s): + # If we see a space, we can safely skip it. UNLESS, it is the last character in a string. In that case, we + # do actually need to resolve the last operation we saw. For example, '3 * 2 ' should resolve '3 * 2'. + # Here, we'll just skip spaces if they are not the last character. + if c == " " and i != len(s) - 1: + continue + + # If we find a digit, build up the current number by multiplying the current value by 10 and adding the + # digit. + if c.isdigit(): + value = value * 10 + int(c) + + # Normally, we could just continue, but if the digit is the very last character, like '3 * 2', we do + # actually need to fall through to the operation resolution section below. + if i != len(s) - 1: + continue + + # Okay, now we have a non-digit character that could potentially be an operation. Either way we need to + # resolve an expression. For example, reading up to the last character in both strings results in some + # evaluation: + # + # - '3 * 2 +' -> Results in resolving '3 * 2' first, then adding the result to the stack. + # - '3 * 2 ' -> Results in resolving '3 * 2' because we are at the last value, but it's a string and not an + # operator! + # + # Remember, the current operator tells us what to do LATER. What we do now depends on the last operator. + # If the last operator was '+', we delay the summation until the end. + if last == "+": + stack.append(value) + # Similarly if the operator was '-', we delay the summation until the end. + elif last == "-": + stack.append(-value) + # If the operator was '*', we do want to resolve it immediately with the current value and the value on the + # stack. Imagine we've hit a case like '2 * 2 +' and we've just read the '+'. We'll want to resolve + # '2 * 2' first. + elif last == "*": + left = stack.pop() + right = value + stack.append(left * right) + # If the last operator was '/', we similarly want to resolve it immediately. Take care to do left / right + # where the left value is on the stack and the right value is what number we just read. + elif last == "/": + left = stack.pop() + right = value + stack.append(int(left / right)) + + # Now, after resolving an operation, reset the current value and store the current operator as the last one + # we have seen. + last = c + value = 0 + + return sum(stack) diff --git a/src/leetcode/stack/key_value_store_nested_transactions.py b/src/leetcode/stack/key_value_store_nested_transactions.py new file mode 100644 index 0000000..15b0444 --- /dev/null +++ b/src/leetcode/stack/key_value_store_nested_transactions.py @@ -0,0 +1,90 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Implement a key-value store with the following functionality: +# +# - get(key: string) +# - set(key: string, value: number) +# - delete(key: string) +# - begin() starts a transaction +# - commit() commits a transaction +# - rollback() rolls back a transaction +# +# This structure supports nested transactions, and the topmost transaction should be able to see the values in the +# transactions below it. +# +# See https://leetcode.com/discuss/interview-question/279913/Bloomberg-or-Onsite-or-Key-Value-Store-with-transactions +class KeyValueStore: + def __init__(self) -> None: + """ + SOLUTION + -------- + + Use a stack to store the transactions. Each transaction is a dictionary that maps keys to values. + + COMPLEXITY + ---------- + + Time complexity is O(1) for all operations. + + Space complexity is O(n) where n is the number of keys in the data structure. + """ + self.deletions: list[set[str]] = [] + self.txs: list[dict[str, int]] = [] + self.main: dict[str, int] = {} + + def get(self, key: str) -> int | None: + if not self.txs: + return self.main.get(key, None) + + frame = len(self.txs) - 1 + while frame >= 0: + if key in self.deletions[frame]: + return None + if key in self.txs[frame]: + return self.txs[frame].get(key, None) + frame -= 1 + + return None + + def set(self, key: str, value: int) -> None: + if not self.txs: + self.main[key] = value + return + + self.txs[-1][key] = value + + def delete(self, key: str) -> None: + if not self.txs: + self.main.pop(key, None) + return + + self.txs[-1].pop(key, None) + self.deletions[-1].add(key) + + def begin(self) -> None: + self.txs.append({}) + self.deletions.append(set()) + + def commit(self) -> None: + if not self.txs: + return + + target = self.main if len(self.txs) == 1 else self.txs[-2] + source = self.txs[-1] + + for key, value in source.items(): + target[key] = value + + for key in self.deletions[-1]: + target.pop(key, None) + + self.txs.pop() + self.deletions.pop() + + def rollback(self) -> None: + if not self.txs: + return + + self.txs.pop() + self.deletions.pop() diff --git a/src/leetcode/stack/longest_absolute_file_path.py b/src/leetcode/stack/longest_absolute_file_path.py new file mode 100644 index 0000000..46122ef --- /dev/null +++ b/src/leetcode/stack/longest_absolute_file_path.py @@ -0,0 +1,94 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Suppose we have a file system that stores both files and directories. An example of one system is represented in the +# following picture: +# +# Here, we have `dir` as the only directory in the root. `dir` contains two subdirectories, `subdir1` and `subdir2`. +# `subdir1` contains a file `file1.ext` and subdirectory `subsubdir1`. `subdir2` contains a subdirectory `subsubdir2`, +# which contains a file `file2.ext`. +# +# In text form, it looks like this (with ⟶ representing the tab character): +# +# dir +# ⟶ subdir1 +# ⟶ ⟶ file1.ext +# ⟶ ⟶ subsubdir1 +# ⟶ subdir2 +# ⟶ ⟶ subsubdir2 +# ⟶ ⟶ ⟶ file2.ext +# +# If we were to write this representation in code, it will look like this: +# +# - "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext". +# +# Note that the '\n' and '\t' are the new-line and tab characters. +# +# Every file and directory has a unique absolute path in the file system, which is the order of directories that must +# be opened to reach the file/directory itself, all concatenated by '/'s. Using the above example, the absolute path +# to file2.ext is "dir/subdir2/subsubdir2/file2.ext". Each directory name consists of letters, digits, and/or spaces. +# Each file name is of the form name.extension, where name and extension consist of letters, digits, and/or spaces. +# +# Given a string input representing the file system in the explained format, return the length of the longest absolute +# path to a file in the abstracted file system. If there is no file in the system, return 0. +# +# Note that the testcases are generated such that the file system is valid and no file or directory name has length 0. +# +# See https://leetcode.com/problems/longest-absolute-file-path +class Solution: + def lengthLongestPath(self, input: str) -> int: + """ + SOLUTION + -------- + + A solution can be efficient by using a stack to keep track of directory depth. However, this only works if the + input does not contain duplicate sub directories, and all of the files appear right after the subdirectory they + are in. If it turns out the input can vary such that files and subdirectories appear out of order, or the + subdirectories can contain duplicates, we'll have to actually build out a real filesystem structure using a + graph of FileNode (with strings mapping to subdirectory FileNodes). + + In this solution, we will assume that the input is constrained. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the input string. + + Space complexity is O(n) because we are storing the file path in a stack. + """ + # This is a stack that will keep track of where we are in the directory structure. We'll assume that every + # directory has a trailing slash (no leading slash). We never calculate directory lengths, so that won't mess + # up our calculation. + stack: list[str] = [] + + # The current and max file lengths. + current_len = 0 + max_len = 0 + + parts = input.split("\n") + for part in parts: + # If the string contents a "\t" character, that means that this part of the text is in a subdirectory. We + # can locate the subdirectory by popping directories off of the stack. + # + # We can figure out how many directories to pop by counting the number of tabs in the string. + levels = part.count("\t") + while len(stack) > levels: + # Adjust the current length by subtracting the length of the directory name; note that the directory + # name doesn't include a trailing slash but the length should account for it. + current_len -= len(stack.pop()) + 1 + + # To get the name of the file, we replace all the tabs with empty string. + name = part.replace("\t", "") + + # All directories have no extension, and all files have an extension. If we see a dot in the name, that + # means its a file. + if "." in name: + max_len = max(max_len, current_len + len(name)) + else: + # Otherwise, we are looking at a directory, and we should push it onto the stack. The stack doesn't need to + # store the directory with the trailing slash, but make sure to account for it in the current length + # calculation. + stack.append(name) + current_len += len(name) + 1 + + return max_len diff --git a/src/leetcode/stack/max_chunks_to_make_sorted_ii.py b/src/leetcode/stack/max_chunks_to_make_sorted_ii.py new file mode 100644 index 0000000..e5ea6de --- /dev/null +++ b/src/leetcode/stack/max_chunks_to_make_sorted_ii.py @@ -0,0 +1,65 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an integer array arr. +# +# We split arr into some number of chunks (i.e., partitions), and individually sort each chunk. After concatenating +# them, the result should equal the sorted array. +# +# Return the largest number of chunks we can make to sort the array. +# +# See https://leetcode.com/problems/max-chunks-to-make-sorted-ii +class Solution: + def maxChunksToSorted(self, xs: list[int]) -> int: + """ + SOLUTION + -------- + + We can use a stack to keep track of chunks. Rather than store the *entire* chunk on the stack, we just store + the largest element. That's because if we have chunks [1, 3] and [4, 5, 9], it's enough to know that [? to 3] + and [? to 9] form chunks. + + When we encounter a new number, like 10, we make a new chunk (since we want as many chunks as possible). When + we encounter a new number, like 7, we include it as part of the chunk from [? to 9]. But because we might have + seen a 7 or 6 or some other number in between earlier, we'll have to go down the stack and prune out numbers we + don't need anymore. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of elements in arr. + + Space complexity is O(1). + """ + # The element stack[i] is the max element of chunk i. At the end, the length of the stack is the number of + # chunks we can create. + # + # We keep track of the max element of chunks, because the smallest element of the next chunk needs to be greater + # than the maximum element of the current chunk. + stack = [xs[0]] + + # Start iteration at 1 because you already pushed the first element onto the stack. + for x in xs[1:]: + # If this element is larger than top of stack, then xs[i] is part of a new chunk. Therefore, push it onto + # the stack and begin the new chunk. + # + # Conceptually, if you have chunks [1], [3], [5] then you see a [9], then this starts a new chunk. + if x > stack[-1]: + stack.append(x) + continue + + # Otherwise, xs[i] is part of the chunk which is made up of all chunks where stack[j] > xs[i]. Pop these + # chunks off the stack so you can "merge" them. + # + # Conceptually, if you have chunks [1], [3], [5], [9], then you see a [4], then you can't begin a new chunk. + # Instead, you know that [4, 5, 9] constitute a chunk. However, we don't need to store all numbers + # [4, 5, 9] in # the stack; we only need to store the largest element in the chunk [9]. + # + # Therefore, go down the stack and pop off elements in the stack we don't want. + max_value = stack[-1] + while stack and x < stack[-1]: + stack.pop() + + stack.append(max_value) + + return len(stack) diff --git a/src/leetcode/stack/min_stack.py b/src/leetcode/stack/min_stack.py new file mode 100644 index 0000000..33b67e9 --- /dev/null +++ b/src/leetcode/stack/min_stack.py @@ -0,0 +1,56 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. +# +# Implement the MinStack class: +# +# MinStack() initializes the stack object. +# void push(int val) pushes the element val onto the stack. +# void pop() removes the element on the top of the stack. +# int top() gets the top element of the stack. +# int getMin() retrieves the minimum element in the stack. +# +# You must implement a solution with O(1) time complexity for each function. +# +# See https://leetcode.com/problems/min-stack +class MinStack: + def __init__(self): + """ + SOLUTION + -------- + + Use one stack to store the values and another stack to store the minimum values. + + It appears that the problem will not call your solution with an empty stack, so we don't need to worry about that + case. + + COMPLEXITY + ---------- + + Time complexity is O(1) for all operations. + + Space complexity is O(n) where n is the number of elements in the stack. + """ + self.stack: list[int] = [] + self.min_values: list[int] = [] + + def push(self, value: int) -> None: + self.stack.append(value) + + if not self.min_values: + self.min_values.append(value) + return + + min_value = min(value, self.getMin()) + self.min_values.append(min_value) + + def pop(self) -> None: + self.stack.pop() + self.min_values.pop() + + def top(self) -> int: + return self.stack[-1] + + def getMin(self) -> int: + return self.min_values[-1] diff --git a/src/leetcode/stack/minimum_add_to_make_valid_parentheses.py b/src/leetcode/stack/minimum_add_to_make_valid_parentheses.py new file mode 100644 index 0000000..91b7ce9 --- /dev/null +++ b/src/leetcode/stack/minimum_add_to_make_valid_parentheses.py @@ -0,0 +1,68 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# A parentheses string is valid if and only if: +# +# - It is the empty string, +# - It can be written as AB (A concatenated with B), where A and B are valid strings, or +# - It can be written as (A), where A is a valid string. +# - You are given a parentheses string s. In one move, you can insert a parenthesis at any position of the string. +# +# For example, if s = "()))", you can insert an opening parenthesis to be "(()))" or a closing parenthesis to be +# "())))". +# +# Return the minimum number of moves required to make s valid. +# +# See https://leetcode.com/problems/minimum-add-to-make-parentheses-valid +class Solution: + def minAddToMakeValid(self, s: str) -> int: + """ + SOLUTION + -------- + + One way to do this is to use a stack and push open braces onto it. Then, when we encounter a closing brace, we + can pop it off. If we don't have closing braces left, then increment opensRequired. At the end, count up the + number of elements still left on the stack (the unmatched closing braces), and add that to the number of opening + braces tallied up. + + However! Notice that we don't even need a stack for this. We just have to keep track of any open and close + braces, which can be done with a simple counter for each variable. + + COMPLEXITY + ---------- + + Time complexity is O(n) because we are iterating through all elements in the string. + + Space complexity is O(1). + """ + # Every time we see '(', increment this counter. Every time we see ')', decrement this counter, making it + # similar to our stack without using one. + # + # Likewise, at the end, if this variable isn't 0, then it's the same as the situation where the stack still has + # elements in it, which indicates some number of unmatched closing braces. + closes_required = 0 + + # If we encounter a ')' and we don't have a matching '(' (aka the stack is empty), then we have to insert an + # opening brace later. + opens_required = 0 + + for c in s: + # If we encounter an opening brace, we'll need to require that we close it eventually. + if c == "(": + closes_required += 1 + continue + # If we encounter a closing brace, there are two possibilities: + # + # 1. We already have an opening brace to match it, which means we can decrement the number of closing braces + # we . + # 2. We don't have an opening brace to match it, which means we have to insert an opening brace later. + elif c == ")": + # This is case 1; we encountered '(' earlier, which caused us to increment closesRequired. But we + # encountered a closing brace, so we can decrement the number of closing braces we need to insert. + if closes_required > 0: + closes_required -= 1 + # This is case 2; we don't have an opening brace to match it, so we have to remember to insert one. + else: + opens_required += 1 + + return closes_required + opens_required diff --git a/src/leetcode/stack/minimum_remove_to_make_valid_parentheses.py b/src/leetcode/stack/minimum_remove_to_make_valid_parentheses.py new file mode 100644 index 0000000..3083bfe --- /dev/null +++ b/src/leetcode/stack/minimum_remove_to_make_valid_parentheses.py @@ -0,0 +1,89 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a string s of '(' , ')' and lowercase English characters. +# +# Your task is to remove the minimum number of parentheses ( '(' or ')', in any positions ) so that the resulting +# parentheses string is valid and return any valid string. +# +# Formally, a parentheses string is valid if and only if: +# +# - It is the empty string, contains only lowercase characters, or +# - It can be written as AB (A concatenated with B), where A and B are valid strings, or +# - It can be written as (A), where A is a valid string. +# +# See https://leetcode.com/problems/minimum-remove-to-make-valid-parentheses +class Solution: + def minRemoveToMakeValid(self, s: str) -> str: + """ + SOLUTION + -------- + + Imagine if we were just asked to validate the parentheses. This can be done with a stack pretty easily. In + this problem, we can push open parens onto the stack, then pop off close parens when we encounter them. If we + ever encounter a close parens while the stack is empty, OR if we have remaining open parens on the stack, then + we know the parentheses are invalid. + + Here, though, we are asked to remove invalid parentheses. This means that instead of failing immediately, every + time we encounter a close without an open we need to mark it for removal. In addition, any open parens left on + the stack at the end should also be marked for removal. + + Once we have indices that are marked for removal, we'll go through the string again, and copy it to a new + string, but avoid any indices that are marked for removal. + + COMPLEXITY + ---------- + + Time complexity is O(n) for each pass through the string. Two passes means O(n) overall. + + Space complexity is O(n) because we are using the stack (which is size of the string) and the removable set, + which is also size of the string. + """ + # For the validate parens problem, we'll have a stack of '(' strings. However, notice that in that case we + # never push any ')' onto the stack; as soon as ')' we pop the item off the stack or we fail. That is, in the + # validation # problem, we only ever have '(' characters on the stack. + # + # Therefore we can reclaim that space by pushing the index of the '(' character onto the stack. It is not + # necessary to declare both: + # + # stack: list[str] = [] + # opens: set[int] = set() + # + # One array data structure is enough to handle both. Note that having a set would cause problems because we'd + # at some point need to pop the last open paren off the stack, and we'd have to search the set for it (or + # convert the set into an array). + opens: list[int] = [] + + # Remove these indices at the end; these are unmatched close parens. + closes: set[int] = set() + + # Find indices to remove. + for i, c in enumerate(s): + # If we encounter open paren, just push it onto the stack. + if c == "(": + opens.append(i) + + # If we encounter a non-close paren, just skip it. It's not relevant for the removal process. + if c != ")": + continue + + # If we have a matched close paren, pop the associated open from the stack and just continue. + if opens: + opens.pop() + continue + + # Uh oh! At this point, we have an unmatched close paren, so mark this index for removal. + closes.add(i) + + # It's possible we are left with some unmatched open parens, so mark them for removal as well. + for open in opens: + closes.add(open) + + # Copy the string, skipping any indices that are marked for removal. + result = "" + for i, c in enumerate(s): + if i in closes: + continue + result += c + + return result diff --git a/src/leetcode/stack/simplify_path.py b/src/leetcode/stack/simplify_path.py new file mode 100644 index 0000000..32c7b5b --- /dev/null +++ b/src/leetcode/stack/simplify_path.py @@ -0,0 +1,62 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an absolute path for a Unix-style file system, which always begins with a slash '/'. Your task is to +# transform this absolute path into its simplified canonical path. +# +# The rules of a Unix-style file system are as follows: +# +# - A single period '.' represents the current directory. +# - A double period '..' represents the previous/parent directory. +# - Multiple consecutive slashes such as '//' and '///' are treated as a single slash '/'. +# - Any sequence of periods that does not match the rules above should be treated as a valid directory or file name. +# For example, '...' and '....' are valid directory or file names. +# +# The simplified canonical path should follow these rules: +# +# - The path must start with a single slash '/'. +# - Directories within the path must be separated by exactly one slash '/'. +# - The path must not end with a slash '/', unless it is the root directory. +# - The path must not have any single or double periods ('.' and '..') used to denote current or parent directories. +# +# Return the simplified canonical path. +# +# See https://leetcode.com/problems/simplify-path +class Solution: + def simplifyPath(self, path: str) -> str: + """ + SOLUTION + -------- + + Just iterate through the path and use a stack to keep track of directories. The only one we need to be careful + with is the '..' name; this means we need to pop the last directory from the stack to "go up" one level. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the input string. + + Space complexity is O(n) to store the string segments on the stack. + """ + stack: list[str] = [] + names = path.split("/") + + for name in names: + # Presumably these can just get ignored and thrown away. + if name == "" or name == ".": + continue + + # If we are going up a level, we need to pop the last directory from the stack. + # + # Wait, what happens if we are already at the root directory and we can't go up a directory anymore? The + # instructions are not clear, but we definitely do get inputs like '/../' and the result should be '/', + # indicating that the desired behavior is to do nothing. + if name == "..": + if stack: + stack.pop() + continue + + # Otherwise we should just push the directory onto the stack as normal. + stack.append(name) + + return "/" + "/".join(stack) diff --git a/src/leetcode/stack/valid_number.py b/src/leetcode/stack/valid_number.py new file mode 100644 index 0000000..3d2c7fa --- /dev/null +++ b/src/leetcode/stack/valid_number.py @@ -0,0 +1,120 @@ +# DIFFICULTY: HARD +# ---------------- +# +# A valid number can be split up into these components (in order): +# - A decimal number or an integer. +# - (Optional) An 'e' or 'E', followed by an integer. + + +# A decimal number can be split up into these components (in order): +# - (Optional) A sign character (either '+' or '-'). +# +# One of the following formats: +# - One or more digits, followed by a dot '.'. +# - One or more digits, followed by a dot '.', followed by one or more digits. +# - A dot '.', followed by one or more digits. +# +# An integer can be split up into these components (in order): +# - (Optional) A sign character (either '+' or '-'). +# - One or more digits. +# +# For example, all the following are valid numbers: +# ["2", "0089", "-0.1", "+3.14", "4.", "-.9", "2e10", "-90E3", "3e+7", "+6e-1", "53.5e93", "-123.456e789"] +# +# While the following are not valid numbers: +# ["abc", "1a", "1e", "e3", "99e2.5", "--6", "-+3", "95a54e53"]. +# +# Given a string s, return true if s is a valid number. +# +# See https://leetcode.com/problems/valid-number +class Solution: + def isNumber(self, s: str) -> bool: + """ + SOLUTION + -------- + + I don't know that this is a particularly hard problem, but it is a bit tricky to get all the edge cases right. + It doesn't seem like a particularly fair problem to ask in a 45m interview though. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the input string. + + Space complexity is O(1). + """ + # Store some flags to keep track of what we have seen so far. + digits = False + decimal = False + exponent = False + + t = s.strip().lower() + for i, c in enumerate(t): + # Get the previous character, if it exists. + p = "" if i == 0 else t[i - 1] + + match c: + case "+" | "-": + if exponent: + # After seeing the E symbol, signs are only valid immediately after the E symbol. + if p != "e": + return False + + # However, signs are not valid if they are the last symbol. + if i == len(t) - 1: + return False + else: + # Before seeing the E symbol, signs are not valid if they aren't in the first position. + if i != 0: + return False + + # Signs are invalid after we have encountered a decimal. + if decimal: + return False + + # Signs are invalid if they are the last symbol, and no digits have been seen. For example, "9." is + # valid, but "." by itself is not. + if i == len(t) - 1 and not digits: + return False + case "e": + # If the E symbol is encountered again after already seeing one, the number is automatically + # invalid. + if exponent: + return False + + # The E symbol may not appear as the first symbol or the last symbol. + if i == 0 or i == len(t) - 1: + return False + + # The E symbol may not appear just after the sign symbol. For example, 46+e3 is invalid. + if p == "+" or p == "-": + return False + + # The E symbol may not appear after a decimal symbol, if no digits have been seen. For example, + # 9.e3 is valid, but .e3 is not valid. + if p == "." and not digits: + return False + + exponent = True + case ".": + # The decimal symbol may not appear after the E symbol has appeared. + if exponent: + return False + + # The decimal symbol may not appear after the decimal symbol has appeared. + if decimal: + return False + + # The decimal symbol cannot be the last symbol, unless digits have already been seen. + if i == len(t) - 1 and not digits: + return False + + decimal = True + case _: + # Any other character that is not a digit does not constitute a number. + if not c.isdigit(): + return False + + digits = True + + return True diff --git a/src/leetcode/stack/valid_parentheses.py b/src/leetcode/stack/valid_parentheses.py new file mode 100644 index 0000000..bec4194 --- /dev/null +++ b/src/leetcode/stack/valid_parentheses.py @@ -0,0 +1,43 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. +# +# An input string is valid if: +# +# Open brackets must be closed by the same type of brackets. +# Open brackets must be closed in the correct order. +# Every close bracket has a corresponding open bracket of the same type. +# +# See https://leetcode.com/problems/valid-parentheses +class Solution: + def isValid(self, text: str) -> bool: + """ + SOLUTION + -------- + + Simple stack based solution. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of characters in the string. + + Space complexity is O(n). + """ + map = {"(": ")", "{": "}", "[": "]"} + stack = [] + + for c in text: + if c in map: + stack.append(c) + continue + + if c in map.values(): + if not stack: + return False + open_c = stack.pop() + if map[open_c] != c: + return False + + return not stack diff --git a/src/leetcode/string/find_the_closest_palindrome.py b/src/leetcode/string/find_the_closest_palindrome.py new file mode 100644 index 0000000..4583c14 --- /dev/null +++ b/src/leetcode/string/find_the_closest_palindrome.py @@ -0,0 +1,146 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Given a string n representing an integer, return the closest integer (not including itself), which is a palindrome. +# If there is a tie, return the smaller one. +# +# The closest is defined as the absolute difference minimized between two integers. +# +# Constraints: 1 <= len(str(n)) <= 18 +# +# See https://leetcode.com/problems/find-the-closest-palindrome +from collections import defaultdict +import math + + +class Solution: + def nearestPalindromic(self, text: str) -> str: + """ + SOLUTION + -------- + + To devise a strategy, first consider a few examples: + + "123" => "121" + "1234" => "1221" + "1000" => "1001" + "1" => "0" <-- It's not "1" because we can't return the number itself. + "999" => "1001" <-- It's not "888" because "1001" is "closer" to "999" by 2. + "1221" => "1111" <-- It's not "1221" because we can't return the number itself. + + Generally, we can either mirror the left side of the number (since that should result in the "closer" number) + and use that, or we may have to do some incrementing/decrementing to get a part of the number we can mirror. We + also need to handle edge cases like "1" or "0". + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of the input string. + + Space complexity is O(n). + """ + n = int(text) + + # If it's just a single digit, return that digit minus one (since we can't return the original digit). + if len(text) == 1: + return str(n - 1) + + # Split up the string so we have the left prefix (and middle) in case we need it. + (left, mid) = self.__findPrefix(text) + + # Find possible candidates for the closest palindrome. + candidates = self.__findCandidates(text, left, mid) + + # Find the actual closest. + closest = self.__findClosest(candidates, n) + + return closest + + def __findPrefix(self, text: str) -> tuple[str, str]: + # Find the left half of the number, which we may need to mirror. If the number has an odd number of digits, we + # don't take the midpoint because we won't want to mirror it anyways. + k = len(text) // 2 + left = text[:k] + mid = text[k] if len(text) % 2 == 1 else "" + return (left, mid) + + def __findCandidates(self, text: str, left: str, mid: str) -> list[str]: + # Generate candidate palindromes; if the original number is already a palindrome, this won't work and we'll have + # to increment or decrement the left side to find the nearest palindrome. + # + # In some cases, we may also have to increment or decrement the middle digit. + uniques: set[str] = set() + for i in [-1, 0, 1]: + prefix = str(int(left) + i) + suffix = prefix[::-1] + uniques.add(prefix + mid + suffix) + + # Skip middle digit variation if there is no middle digit. + if not mid: + continue + + # There are cases where the middle digit needs to be incremented or decremented as well. For example, take + # the following cases: + # + # "11911" => "11811" + # "10001" => "11111" + # + # To handle these cases we'll vary the middle digit as well. However, we need to be careful about the + # middle digit going out of bounds, so we'll wrap around if it does. + for j in [-1, 0, 1]: + m = int(mid) + j + if m == 10: + m = 0 + if m == -1: + m = 9 + uniques.add(prefix + str(m) + suffix) + + # These candidates will work for the vast majority of numbers, but sometimes we'll get edge cases where simply + # incrementing or decrementing won't work. For example: + # + # "101" => "99" + # "99" => "101" + # + # In these situations, just special case them by building special numbers like all 9's or 100...001. + for length in [len(text) - 1, len(text), len(text) + 1]: + xs = [9] * length + + # Add all 9s. + value = "".join(map(str, xs)) + uniques.add(value) + + # Add 100..001. + xs = [0] * length + xs[0] = 1 + xs[-1] = 1 + value = "".join(map(str, xs)) + uniques.add(value) + + # Finally prune the candidate list of numbers that don't make sense. + candidates: list[str] = [] + for candidate in uniques: + if candidate.startswith("0"): + continue + + if candidate == text: + continue + + candidates.append(candidate) + + return candidates + + def __findClosest(self, candidates: list[str], n: int) -> str: + # Find the closest elements to the original number. Since we need to find the smallest to break a tie, just map + # deltas to their candidates. + deltas: dict[int, list[int]] = defaultdict(list) + lowest = math.inf + for candidate in candidates: + c = int(candidate) + delta = abs(n - c) + lowest = min(lowest, delta) + deltas[delta].append(c) + + # If there are multiple closest candidates, return the smallest one. + xs = deltas[int(lowest)] + xs.sort() + return str(xs[0]) diff --git a/src/leetcode/string/int_to_roman.py b/src/leetcode/string/int_to_roman.py new file mode 100644 index 0000000..e0c573d --- /dev/null +++ b/src/leetcode/string/int_to_roman.py @@ -0,0 +1,98 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. +# +# Symbol Value +# I 1 +# V 5 +# X 10 +# L 50 +# C 100 +# D 500 +# M 1000 +# For example, 2 is written as II in Roman numeral, just two one's added together. 12 is written as XII, which is +# simply X + II. The number 27 is written as XXVII, which is XX + V + II. +# +# Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. +# Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same +# principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: +# +# I can be placed before V (5) and X (10) to make 4 and 9. +# X can be placed before L (50) and C (100) to make 40 and 90. +# C can be placed before D (500) and M (1000) to make 400 and 900. +# Given an integer, convert it to a roman numeral. +# +# See https://leetcode.com/problems/integer-to-roman +class Solution: + def intToRoman(self, n: int) -> str: + """ + SOLUTION + -------- + + A straightforward solution works. + + COMPLEXITY + ---------- + + Time complexity is O(n). However, there is an upper limit of what roman numerals can represent, so you can also + argue that the time complexity is O(1). + + Space complexity is O(1). + """ + + def convert(digit: int, ones: str, fives: str, tens: str) -> str: + match digit: + case 1 | 2 | 3: + return ones * digit + case 4: + return ones + fives + case 5: + return fives + case 6 | 7 | 8: + times = digit - 5 + return fives + (ones * times) + case 9: + return ones + tens + case 0: + return "" + case _: + raise ValueError("not a digit") + + cs = str(n) + + # If we have a number like 1, we don't know how many digits remain. We could run two pointers through the + # array, converting the least significant digits first, or we could just pad the number so that the hundreds and + # thousands logic will skip over the few digits with zeroes. Choosing the latter here. + if len(cs) < 4: + times = 4 - len(cs) + cs = ("0" * times) + cs + + result: list[str] = [] + for i, c in enumerate(cs): + digit = int(c) + + if i == 0: + # We can't have any numbers over 3999, so we don't need to specify any numerals for fives or tens. + roman = convert(digit, "M", "", "") + result.append(roman) + continue + + if i == 1: + roman = convert(digit, "C", "D", "M") + result.append(roman) + continue + + if i == 2: + roman = convert(digit, "X", "L", "C") + result.append(roman) + continue + + if i == 3: + roman = convert(digit, "I", "V", "X") + result.append(roman) + continue + + raise ValueError("too many digits") + + return "".join(result) diff --git a/src/leetcode/string/integer_to_english_words.py b/src/leetcode/string/integer_to_english_words.py new file mode 100644 index 0000000..d90d072 --- /dev/null +++ b/src/leetcode/string/integer_to_english_words.py @@ -0,0 +1,153 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Convert a non-negative integer num to its English words representation. +# +# See https://leetcode.com/problems/integer-to-english-words +class Solution: + def numberToWords(self, num: int) -> str: + """ + SOLUTION + -------- + + To get an idea of how to approach this problem, first start with a few examples, as there are going to be a lot + of edges cases: + + 1 => One + 10 => Ten + 11 => Eleven + 19 => Nineteen + 100 => One Hundred + 101 => One Hundred One + 1_000 => One Thousand + 1_001 => One Thousand One + 100_000 => One Hundred Thousand + 1_000_000 => One Million + 1_000_000_000 => One Billion + + A few notes: + + - 2^32 is the limit, and it is approximately 4 billion, so we don't need to describe numbers over 4 billion. + - The word "And" isn't required between words, so "One Hundred One", not "One Hundred And One". + - The numbers 1-20 need to be handled in a special way due to how English works. + - It's easier to deal with numbers 3 digits at a time. + + When looking at a number XYZ_123_UWV, the 123 part will always be translated into "One Hundred Twenty Three", + and after that, we will append "Thousand", "Million", or "Billion". For this reason, it's best to split the + number into segments of 3, translate that part directly, and then append the correct word afterwards. + + COMPLEXITY + ---------- + + """ + if num == 0: + return "Zero" + + # Each index represents how to say a number in English, if it applies. + under_20 = [ + # Zero is missing; we will never say it in a multi word number phrase. + "", + "One", + "Two", + "Three", + "Four", + "Five", + "Six", + "Seven", + "Eight", + "Nine", + "Ten", + "Eleven", + "Twelve", + "Thirteen", + "Fourteen", + "Fifteen", + "Sixteen", + "Seventeen", + "Eighteen", + "Nineteen", + ] + ones = under_20 + + # Each index represents how we'd say a tens digit in English. + over_20 = [ + # We don't say Zero. + "", + # We don't say Ten One; we say Eleven. This is covered by the under_20 array. + "", + "Twenty", + "Thirty", + "Forty", + "Fifty", + "Sixty", + "Seventy", + "Eighty", + "Ninety", + ] + tens = over_20 + + # Each index represents how we'd say increasingly larger 3 segment chunks of numbers. For example: + # + # 111 => One Hundred Eleven + # 111_000 => One Hundred Eleven Thousand + # 111_000_000 => One Hundred Eleven Million + # 111_000_000_000 => One Hundred Eleven Billion + thousands = [ + # For numbers under 1000, we don't say anything. + "", + "Thousand", + "Million", + "Billion", + ] + + # Converts a 3 digit number into a phrase. For example, 123 => One Hundred Twenty Three. We can then append + # the word Million, Billion, or Thousand afterwards. + def toWordInternal(n: int) -> str: + nonlocal ones, tens, thousands + + # If we have a number like 100_000_001, then the middle segment of 000 does not get translated to "Zero", + # but instead remains blank. + if n == 0: + return "" + # Handle numbers under 20; English has a special way of handling these numbers. + elif n < 20: + return under_20[n] + # Handle 2 digit numbers over 20. + elif n < 100: + first_digit = n // 10 + second_digit = n % 10 + phrase = tens[first_digit] + " " + ones[second_digit] + return phrase.strip() + # Finally, handle 3 digit numbers by figuring how to say the hundreds digit, then reducing the problem to + # the 2 digit case. + else: + first_digit = n // 100 + last_2_digits = n % 100 + first_part = ones[first_digit] + " Hundred" + second_part = toWordInternal(last_2_digits) + phrase = first_part + " " + second_part + return phrase.strip() + + result = "" + + # This will keep track of how many 3 digit segments we've seen so we can add Thousands, Millions, or Billions. + i = 0 + + # Handle the number 3 digits at a time; each 3 digit segment represents an increase to Thousands, Millions, or + # Billions. + while num > 0: + # Get the last 3 digits and figure out how to say them. Once we do that, figure out if we should append + # nothing, Thousand, Million, or Billion. + if num % 1000 != 0: + last_3_digits = num % 1000 + phrase = toWordInternal(last_3_digits) + " " + thousands[i] + + # Add the created phrase to the front of the result, since we are dealing with the last 3 (least + # significant) digits each time. + result = phrase + " " + result + + # Now truncate the number by 3 digits and repeat. + num //= 1000 + i += 1 + + return result.strip() diff --git a/src/leetcode/string/longest_palindromic_substring.py b/src/leetcode/string/longest_palindromic_substring.py new file mode 100644 index 0000000..0992403 --- /dev/null +++ b/src/leetcode/string/longest_palindromic_substring.py @@ -0,0 +1,77 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a string s, return the longest palindromic substring in s. +# +# See https://leetcode.com/problems/longest-palindromic-substring +class Solution: + def longestPalindrome(self, s: str) -> str: + """ + SOLUTION + -------- + + The naive solution is to iterate through each character, then treat that character as the center of a + palindrome, expanding outwards to check if the characters match. + + Note that for each character, we have to do two checks: + + 1. Check if there's a palindrome centered at i, for odd length palindromes. + 2. Check if there's a palindrome centered at i and i + 1, for even length palindromes. + + There is a dynamic programming solution that can solve this problem in O(n^2) time and O(n^2) space. + + There is a more sophisticated algorithm called Manacher's algorithm that can solve this problem in O(n) time, + but it's pretty complicated. + + COMPLEXITY + ---------- + + Each checkPalindrome() call is O(n) where n is the length of the string. We call this function twice for each + character in the string, so the total time complexity is O(n^2). + + The space complexity is O(1) because we only use a constant amount of space. + """ + start = 0 + max_length = 0 + + def checkPalindrome(left: int, right: int) -> None: + nonlocal start, max_length + + matched = False + + # Expand the left and right pointers outwards from the center. + while 0 <= left and right < len(s): + # If the characters match, expand the window, otherwise, break. + if s[left] == s[right]: + left -= 1 + right += 1 + matched = True + else: + break + + # If we didn't match any characters, then there's no need to update the length of the longest palindrome. + if not matched: + return + + # When the while loop ends, s[left] !== s[right], which means they don't match. Adjust them back to a valid + # position. If they didn't match, then there's no need to. + left += 1 + right -= 1 + + # The length of the palindrome we have is bounded by [left, right] inclusive. To get the length of this + # range, we need to do right - left + 1. + current_length = right - left + 1 + if current_length > max_length: + start = left + max_length = current_length + + if len(s) == 0: + return "" + + for i in range(len(s)): + # Check if there's a palindrome centered at i. + checkPalindrome(i, i) + # Check if there's a palindrome centered at i and i + 1. + checkPalindrome(i, i + 1) + + return s[start : start + max_length] diff --git a/src/leetcode/string/palindrome_number.py b/src/leetcode/string/palindrome_number.py new file mode 100644 index 0000000..bd72184 --- /dev/null +++ b/src/leetcode/string/palindrome_number.py @@ -0,0 +1,22 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given an integer x, return true if x is a palindrome, and false otherwise. +# +# See https://leetcode.com/problems/palindrome-number +class Solution: + def isPalindrome(self, x: int) -> bool: + """ + SOLUTION + -------- + + Convert the integer to a string and compare the string to its reverse. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of digits in x. + + Space complexity is O(n). + """ + return str(x) == str(x)[::-1] diff --git a/src/leetcode/string/remove_all_adjacent_duplicates.py b/src/leetcode/string/remove_all_adjacent_duplicates.py new file mode 100644 index 0000000..550bce5 --- /dev/null +++ b/src/leetcode/string/remove_all_adjacent_duplicates.py @@ -0,0 +1,31 @@ +# DIFFICULTY: EASY +# +# You are given a string s consisting of lowercase English letters. A duplicate removal consists of choosing two +# adjacent and equal letters and removing them. +# +# We repeatedly make duplicate removals on s until we no longer can. +# +# Return the final string after all such duplicate removals have been made. It can be proven that the answer is unique. +# +# See https://leetcode.com/problems/remove-all-adjacent-duplicates-in-string +class Solution: + def removeDuplicates(self, s: str) -> str: + """ + SOLUTION + -------- + Use a stack to keep track of the characters. If the next character is the same as the top of the stack, pop the + top of the stack. Otherwise, push the character onto the stack. + COMPLEXITY + ---------- + Time complexity is O(n) where n is the length of s. + Space complexity is O(n). + """ + stack: list[str] = [] + + for c in s: + if stack and c == stack[-1]: + stack.pop() + else: + stack.append(c) + + return "".join(stack) diff --git a/src/leetcode/string/remove_all_adjacent_duplicates_ii.py b/src/leetcode/string/remove_all_adjacent_duplicates_ii.py new file mode 100644 index 0000000..9538277 --- /dev/null +++ b/src/leetcode/string/remove_all_adjacent_duplicates_ii.py @@ -0,0 +1,58 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given a string s and an integer k, a k duplicate removal consists of choosing k adjacent and equal letters +# from s and removing them, causing the left and the right side of the deleted substring to concatenate together. +# +# We repeatedly make k duplicate removals on s until we no longer can. +# +# Return the final string after all such duplicate removals have been made. It is guaranteed that the answer is unique. +# +# See https://leetcode.com/problems/remove-all-adjacent-duplicates-in-string-ii +class Solution: + def removeDuplicates(self, s: str, k: int) -> str: + """ + SOLUTION + -------- + + In contrast with remove duplicates i, we need to keep track of not just the top element of the stack, but rather + if the top k elements constitute dupes. Once we remove k elements, it's possible the new stack now has k more + elements to remove. + + To account for this, we cannot simply count elements and remove when we see k, resetting out counter to 1. + Instead we will have to store, at each stack frame, the elements *and* what the counter was. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of s. + + Space complexity is O(n). + """ + stack: list[tuple[str, int]] = [] + freq = 1 + + for c in s: + # If the top of the stack and the current character match, increase our count. If they don't match, reset + # our freq so that it is 1 (since we always at least see 1 match; the character itself). + if stack and c == stack[-1][0]: + freq += 1 + else: + freq = 1 + + stack.append((c, freq)) + + # Only if we've seen k dupes, pop them all. Here, we cannot reset our counter back to 1; we have to + # reset the counter back to what it was when we first pushed the top element onto the stack. + if freq == k: + for _ in range(k): + stack.pop() + + # Reset our count. If we have no more stack elements, we can reset to 1. However, if we have stack + # elements, we have to reset to what it was previously. + if not stack: + freq = 1 + else: + freq = stack[-1][1] + + return "".join([c for c, _ in stack]) diff --git a/src/leetcode/string/reverse_words_in_a_string.py b/src/leetcode/string/reverse_words_in_a_string.py new file mode 100644 index 0000000..7121627 --- /dev/null +++ b/src/leetcode/string/reverse_words_in_a_string.py @@ -0,0 +1,29 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an input string s, reverse the order of the words. +# +# A word is defined as a sequence of non-space characters. The words in s will be separated by at least one space. +# +# Return a string of the words in reverse order concatenated by a single space. +# +# Note that s may contain leading or trailing spaces or multiple spaces between two words. The returned string should +# only have a single space separating the words. Do not include any extra spaces. +# +# See https://leetcode.com/problems/reverse-words-in-a-string +class Solution: + def reverseWords(self, s: str) -> str: + """ + SOLUTION + -------- + + Split the string into words, reverse the list of words, and join them back together. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of s. + + Space complexity is O(n). + """ + return " ".join(reversed(s.split())) diff --git a/src/leetcode/string/roman_to_int.py b/src/leetcode/string/roman_to_int.py new file mode 100644 index 0000000..7df4e52 --- /dev/null +++ b/src/leetcode/string/roman_to_int.py @@ -0,0 +1,68 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. +# +# Symbol Value +# I 1 +# V 5 +# X 10 +# L 50 +# C 100 +# D 500 +# M 1000 +# +# For example, 2 is written as II in Roman numeral, just two ones added together. 12 is written as XII, which is +# simply X + II. The number 27 is written as XXVII, which is XX + V + II. +# +# Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. +# Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same +# principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: +# +# - I can be placed before V (5) and X (10) to make 4 and 9. +# - X can be placed before L (50) and C (100) to make 40 and 90. +# - C can be placed before D (500) and M (1000) to make 400 and 900. +# +# Given a roman numeral, convert it to an integer. +# +# See https://leetcode.com/problems/roman-to-integer +class Solution: + def romanToInt(self, s: str) -> int: + """ + SOLUTION + -------- + + A straightforward solution works. + + + COMPLEXITY + ---------- + + Time complexity is O(n). However, there is an upper limit of what roman numerals can represent, so you can also + argue that the time complexity is O(1). + + Space complexity is O(1). + + """ + numerals: dict[str, int] = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000} + + result = 0 + for i, c in enumerate(s): + # Get the current roman numeral value. + current_val = numerals[c] + + # Get the next roman numeral value, if it exists. + next_val = None + if i + 1 < len(s): + next_numeral = s[i + 1] + next_val = numerals[next_numeral] + + # In this case, we have a situation like IV (4) or IX (9), where the current number is less than the next + # number. That means we want to do a subtraction. + if next_val and current_val < next_val: + result -= current_val + # Other cases like VI (6) or XI (11), we just want to add. + else: + result += current_val + + return result diff --git a/src/leetcode/string/time_needed_to_rearrange_binary_string.py b/src/leetcode/string/time_needed_to_rearrange_binary_string.py new file mode 100644 index 0000000..78a76e0 --- /dev/null +++ b/src/leetcode/string/time_needed_to_rearrange_binary_string.py @@ -0,0 +1,76 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given a binary string s. In one second, all occurrences of "01" are simultaneously replaced with "10". This +# process repeats until no occurrences of "01" exist. +# +# Return the number of seconds needed to complete this process. +# +# See https://leetcode.com/problems/time-needed-to-rearrange-a-binary-string +class Solution: + def secondsToRemoveOccurrences(self, s: str) -> int: + """ + SOLUTION + -------- + + Rather than simulate the replacement process, we can think about how the replacement affects a string. In a + simple case, "01" -> "10". However, a newly created "10" can create more replacements. Here's an example: + + "0001" -> "0010" -> "0100" -> "1000" (three 0's and three seconds to flip) + + Here' the "10" ripples to the left. The number of times it will "ripple" is determined by the number of 0's to + the left of the 1. Here there are three 0's before the 1, so it will take 3 seconds to complete the + replacement. + + Here's a more complicated example: + + "001001" -> "010010" -> "100100" -> "101000" -> "110000" (four 0's and four seconds to flip) + + Notice that the first "001" only takes two seconds to transform into "100", but we are not done yet. The second + "001" took 4 seconds to replace, which is the two 0's in front of "001", but also the two 0's that were in front + of the first "001". In essence, the first "001" -> "100" transferred the 0's to the right, which the second + "001" has to deal with. + + This means the number of seconds it takes to replace the rightmost "01" is equal to the number of 0's that + appeared to the left of it, in most cases. However, this won't work for a case like this: + + "011" -> "101" -> "110" (one 0 and two seconds to flip) + + Here, there is one 0 but two seconds needed to elapse. This is because the second 1 cannot flip until the first + "01" has flipped. We don't have this issue if we space out 0's and 1's: + + "101" -> "110" (one 0 and one second to flip) + "0101" -> "1010" -> "1100" (two 0's and two seconds to flip) + + This means that each 1 we see requires at LEAST one second to flip. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the length of s. + + Space complexity is O(1). + """ + count = 0 + seconds = 0 + + for c in s: + # This counts how many "0"'s we have seen so far. + if c == "0": + count += 1 + continue + + # If we see a 1 and we haven't seen any 0's before this, we don't need to update the number of seconds. + # That is, "1111..." will not result in increasing the number of seconds. + if count == 0: + continue + + # Here we have seen a 1, and there are some number of zeroes before it. Keep in mind that in most cases, we + # can calculate that count of zeroes = count of seconds. However, each 1 requires at LEAST one second to + # flip. + # + # To handle a case of "011" -> "101" -> "110" which has one 0, but requires TWO seconds to flip, we should + # compare: seconds + 1 (at LEAST one second to flip) versus the count of zeroes. + seconds = max(seconds + 1, count) + + return seconds diff --git a/src/leetcode/string/valid_word_abbreviation.py b/src/leetcode/string/valid_word_abbreviation.py new file mode 100644 index 0000000..944efc5 --- /dev/null +++ b/src/leetcode/string/valid_word_abbreviation.py @@ -0,0 +1,70 @@ +# DIFFICULTY: EASY +# ---------------- +# +# A string can be abbreviated by replacing any number of non-adjacent, non-empty substrings with their lengths. The +# lengths should not have leading zeros. +# +# For example, a string such as "substitution" could be abbreviated as (but not limited to): +# +# "s10n" ("s ubstitutio n") +# "sub4u4" ("sub stit u tion") +# "12" ("substitution") +# "su3i1u2on" ("su bst i t u ti on") +# "substitution" (no substrings replaced) +# The following are not valid abbreviations: +# +# "s55n" ("s ubsti tutio n", the replaced substrings are adjacent) +# "s010n" (has leading zeros) +# "s0ubstitution" (replaces an empty substring) +# Given a string word and an abbreviation abbr, return whether the string matches the given abbreviation. +# +# A substring is a contiguous non-empty sequence of characters within a string. +# +# See https://leetcode.com/problems/valid-word-abbreviation +class Solution: + def validWordAbbreviation(self, word: str, abbr: str) -> bool: + """ + SOLUTION + -------- + + To do this, we'll use two pointers, but not to iterate through a single string. Instead, each pointer iterates + through its own string. If we see an numerical abbreviation, we'll advance through the other string a the + corresponding number of characters. Because of that, this problem isn't categorized as a two-pointer problem. + + At each stage of advancement we'll check if the characters match. + + COMPLEXITY + ---------- + + Time complexity is O(max(m, n)) where m is the length of word, and n is the length of abbr. + + Space complexity is O(1). + """ + i, j = 0, 0 + + while i < len(word) and j < len(abbr): + # If we have a digit in abbr, figure out the entire number, then skip over that many characters from the + # word string. + if abbr[j].isdigit(): + # If we have leading zeroes, like "s010n", then the abbreviation can never be valid. + if abbr[j] == "0": + return False + + # Read in the number from the abbr string. + num = 0 + while j < len(abbr) and abbr[j].isdigit(): + num = num * 10 + int(abbr[j]) + j += 1 + + # Skip that many numbers from the word string. + i += num + + # If we don't have a digit in abbr, we just need to compare that the characters in each string match. + else: + if i >= len(word) or word[i] != abbr[j]: + return False + + i += 1 + j += 1 + + return i == len(word) and j == len(abbr) diff --git a/src/leetcode/string/vowel_spellchecker.py b/src/leetcode/string/vowel_spellchecker.py new file mode 100644 index 0000000..3acbce6 --- /dev/null +++ b/src/leetcode/string/vowel_spellchecker.py @@ -0,0 +1,90 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a wordlist, we want to implement a spellchecker that converts a query word into a correct word. +# +# For a given query word, the spell checker handles two categories of spelling mistakes: +# +# Capitalization: If the query matches a word in the wordlist (case-insensitive), then the query word is returned with +# the same case as the case in the wordlist. +# +# - Example: wordlist = ["yellow"], query = "YellOw": correct = "yellow" +# - Example: wordlist = ["Yellow"], query = "yellow": correct = "Yellow" +# - Example: wordlist = ["yellow"], query = "yellow": correct = "yellow" +# - Vowel Errors: If after replacing the vowels ('a', 'e', 'i', 'o', 'u') of the query word with any vowel +# individually, it matches a word in the wordlist (case-insensitive), then the query word is returned with the same +# case as the match in the wordlist. +# - Example: wordlist = ["YellOw"], query = "yollow": correct = "YellOw" +# - Example: wordlist = ["YellOw"], query = "yeellow": correct = "" (no match) +# - Example: wordlist = ["YellOw"], query = "yllw": correct = "" (no match) +# +# In addition, the spell checker operates under the following precedence rules: +# +# - When the query exactly matches a word in the wordlist (case-sensitive), you should return the same word back. +# - When the query matches a word up to capitlization, you should return the first such match in the wordlist. +# - When the query matches a word up to vowel errors, you should return the first such match in the wordlist. +# - If the query has no matches in the wordlist, you should return the empty string. +# - Given some queries, return a list of words answer, where answer[i] is the correct word for query = queries[i]. +# +# See https://leetcode.com/problems/vowel-spellchecker +import re + + +class Solution: + def spellchecker(self, wordlist: list[str], queries: list[str]) -> list[str]: + """ + SOLUTION + -------- + + We will use a set to store the wordlist and two dictionaries to store the case-insensitive and vowel-error + mappings. We will then iterate through the queries and check for matches in the hash set, case-insensitive + dictionary, and vowel-error dictionary in that order. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of queries. + + Space complexity is O(n). + """ + uniques: set[str] = set(wordlist) + lowers: dict[str, str] = {} + vowels: dict[str, str] = {} + + # We can normalize all inputs and words to a canonical form, then check if the inputs match the canonical form. + for word in wordlist: + lowered = word.lower() + + # Problem says to only use the first mapping; multiple may be given. So if we have a mapping, ignore the + # rest. + if lowered not in lowers: + lowers[lowered] = word + + # Problem is not 100% clear, but it seems we are allowed to change any number of vowels so that the input + # matches the word (not just a single vowel). That means we can just normalize all the vowel inputs to a + # canonical form with vowels replaced. + voweled = re.sub(r"[aeiou]", "#", lowered) + if voweled not in vowels: + vowels[voweled] = word + + result: list[str] = [] + + # Now just canonicalize all the words and check if they match a rule. + for query in queries: + if query in uniques: + result.append(query) + continue + + lowered = query.lower() + if lowered in lowers: + result.append(lowers[lowered]) + continue + + voweled = re.sub(r"[aeiou]", "#", lowered) + if voweled in vowels: + result.append(vowels[voweled]) + continue + + result.append("") + + return result diff --git a/src/leetcode/tree/binary_tree_level_order_traversal.py b/src/leetcode/tree/binary_tree_level_order_traversal.py new file mode 100644 index 0000000..c31658e --- /dev/null +++ b/src/leetcode/tree/binary_tree_level_order_traversal.py @@ -0,0 +1,54 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given the root of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, +# level by level). +# +# See https://leetcode.com/problems/binary-tree-level-order-traversal +from collections import deque +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def levelOrder(self, root: TreeNode | None) -> list[list[int]]: + """ + SOLUTION + -------- + + This is essentially a BFS algorithm from the root node. + + COMPLEXITY + ---------- + + Each node is visited once, so the time complexity is O(n). + """ + if not root: + return [] + + result: list[list[int]] = [] + queue: deque[TreeNode] = deque([root]) + + # Consume the nodes in the current level while noting the frontier nodes. The nodes at the current level will + # be recorded, then the frontier nodes are added to the queue. + while queue: + level: list[int] = [] + frontier: list[TreeNode] = [] + + # Consume all the nodes from the current level and record them. Don't use the queue.length in the for loop + # as we are going to be modifying the queue during the loop. + for _ in range(len(queue)): + node = queue.popleft() + level.append(node.val) + + if node.left: + frontier.append(node.left) + if node.right: + frontier.append(node.right) + + # Add the recorded nodes at this level to the result. + result.append(level) + + # Continue processing frontier nodes. + queue.extend(frontier) + + return result diff --git a/src/leetcode/tree/binary_tree_right_side_view.py b/src/leetcode/tree/binary_tree_right_side_view.py new file mode 100644 index 0000000..d8eb633 --- /dev/null +++ b/src/leetcode/tree/binary_tree_right_side_view.py @@ -0,0 +1,44 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given the root of a binary tree, imagine yourself standing on the right side of it, return the values of the nodes +# you can see ordered from top to bottom. +# +# See https://leetcode.com/problems/binary-tree-right-side-view +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def rightSideView(self, root: TreeNode | None) -> list[int]: + """ + SOLUTION + -------- + + We can use a simple DFS to traverse the tree, and prioritize the right child over the left child. At each + depth, we can add the first rightmost node to the result array, since that's the node we'll see when viewing it + from the right. + + COMPLEXITY + ---------- + + Time complexity is O(n) because we are visiting each node once. + + Space complexity is O(n) because we are storing the nodes in a list. + """ + depths: list[int] = [] + + def dfs(node: TreeNode | None, depth: int) -> None: + if not node: + return + + # If the current depth is the lowest depth we've seen, then it's the rightmost node at this depth, because + # we will always prioritize going down the right node. + if depth == len(depths): + depths.append(node.val) + + # Prioritize going down the right subtree first! + dfs(node.right, depth + 1) + dfs(node.left, depth + 1) + + dfs(root, 0) + return depths diff --git a/src/leetcode/tree/binary_tree_vertical_order_traversal.py b/src/leetcode/tree/binary_tree_vertical_order_traversal.py new file mode 100644 index 0000000..8188411 --- /dev/null +++ b/src/leetcode/tree/binary_tree_vertical_order_traversal.py @@ -0,0 +1,81 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given the root of a binary tree, return the vertical order traversal of its nodes' values. (i.e., from top to bottom, +# column by column). +# +# If two nodes are in the same row and column, the order should be from left to right. +# +# See https://leetcode.com/problems/binary-tree-vertical-order-traversal +from collections import defaultdict, deque +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def verticalOrder(self, root: TreeNode | None) -> list[list[int]]: + """ + SOLUTION + -------- + + There is no straightforward vertical order traversal. However, a standard level order traversal can be used to + get part of the way there. If we assume that the root is at row 0 and column 0, we can do some bookkeeping + while we traverse the nodes to assign a coordinate to every single node. + + While doing the traversal, we can keep track of a Map where each column is mapped to an + array of nodes in the order they were encountered. By doing a level order traversal (or BFS), we can ensure + that nodes in the same "row" are encountered from left to right. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of nodes. The result computation at the end will run in O(k) + time where k is the number of columns. However, you're always guaranteed to have more nodes than columns (or + the same) so overall time complexity is still O(n). + + Space complexity is O(n) because we are storing k columns mapping to lists of nodes, but the overall number of + nodes stored in the map is at most n. We are also using a queue, which has at most n nodes in it. + """ + if not root: + return [] + + # Keep a map of column to nodes values. Note that we don't actually need to store their row values, because if + # we visit the nodes in the right order, the array will already be sorted by row. The key is to push the left + # child before the right child. + mapping: dict[int, list[int]] = defaultdict(list) + + # We DO need to store the column values in our BFS queue though. That's because as we shift items off the + # queue, we will need to know which column it's in, so we can add it to the TreeNode array in the map above. + queue: deque[tuple[TreeNode, int]] = deque([(root, 0)]) + + # Finally, we have to keep track of the min and max columns so we know where to start with the vertical order + # list. + min_col = 0 + max_col = 0 + + # The rest of the algorithm is a standard BFS. We don't need to maintain a visited set because the nodes are + # guaranteed to be organized as a tree, so we can't visit the same node twice. + while queue: + (node, col) = queue.popleft() + + # Add the node to this map for bookkeeping. + mapping[col].append(node.val) + + # Update min/max columns. + min_col = min(min_col, col) + max_col = max(max_col, col) + + # Enqueue the children as usual in BFS, but make sure to enqueue the LEFT child first to keep our proper row + # order. + if node.left: + queue.append((node.left, col - 1)) + if node.right: + queue.append((node.right, col + 1)) + + # Now we have a map of Column -> TreeNode[], and for each column, we just print out the values in the row. + result: list[list[int]] = [] + for col in range(min_col, max_col + 1): + # These node values are already in row order because we visited the left child before the right. + values = mapping[col] + result.append(values) + + return result diff --git a/src/leetcode/tree/common/parent_node.py b/src/leetcode/tree/common/parent_node.py new file mode 100644 index 0000000..e255925 --- /dev/null +++ b/src/leetcode/tree/common/parent_node.py @@ -0,0 +1,44 @@ +from collections import deque + + +# This class definition comes from the problem itself and we cannot change it, or else our submission will not be +# accepted. +class Node: + def __init__(self, val=0): + self.val = val + self.left: "Node | None" = None + self.right: "Node | None" = None + self.parent: "Node | None" = None + + +def array2tree(array: list[int | None]) -> Node | None: + if not array or array[0] is None: + return None + + root = Node(array[0]) + queue = deque([root]) + i = 1 + + while queue and i < len(array): + node = queue.popleft() + + if node is None: + continue + + if i < len(array): + val = array[i] + if val is not None: + node.left = Node(val) + node.left.parent = node + queue.append(node.left) + i += 1 + + if i < len(array): + val = array[i] + if val is not None: + node.right = Node(val) + node.right.parent = node + queue.append(node.right) + i += 1 + + return root diff --git a/src/leetcode/tree/common/quad_tree.py b/src/leetcode/tree/common/quad_tree.py new file mode 100644 index 0000000..de19de4 --- /dev/null +++ b/src/leetcode/tree/common/quad_tree.py @@ -0,0 +1,18 @@ +# This class definition comes from the problem itself and we cannot change it, or else our submission will not be +# accepted. +class Node: + def __init__( + self, + val: bool = False, + isLeaf: bool = False, + topLeft: "Node | None" = None, + topRight: "Node | None" = None, + bottomLeft: "Node | None" = None, + bottomRight: "Node | None" = None, + ) -> None: + self.val = val + self.isLeaf = isLeaf + self.topLeft = topLeft + self.topRight = topRight + self.bottomLeft = bottomLeft + self.bottomRight = bottomRight diff --git a/src/leetcode/tree/common/tree_node.py b/src/leetcode/tree/common/tree_node.py new file mode 100644 index 0000000..4550282 --- /dev/null +++ b/src/leetcode/tree/common/tree_node.py @@ -0,0 +1,44 @@ +from collections import deque + + +# This class definition comes from the problem itself and we cannot change it, or else our submission will not be +# accepted. +class TreeNode: + def __init__(self, val=0, left: "TreeNode | None" = None, right: "TreeNode | None" = None): + self.val = val + self.left = left + self.right = right + + +def array2tree(array: list[int | None]) -> TreeNode | None: + if not array: + return None + + if not array[0]: + return None + + root = TreeNode(array[0]) + queue = deque([root]) + i = 1 + + while queue and i < len(array): + node = queue.popleft() + + if not node: + continue + + if i < len(array): + val = array[i] + if val is not None: + node.left = TreeNode(val) + queue.append(node.left) + i += 1 + + if i < len(array): + val = array[i] + if val is not None: + node.right = TreeNode(val) + queue.append(node.right) + i += 1 + + return root diff --git a/src/leetcode/tree/construct_quad_tree.py b/src/leetcode/tree/construct_quad_tree.py new file mode 100644 index 0000000..096ebc4 --- /dev/null +++ b/src/leetcode/tree/construct_quad_tree.py @@ -0,0 +1,62 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Given a n * n matrix grid of 0's and 1's only. We want to represent grid with a Quad-Tree. +# +# Return the root of the Quad-Tree representing grid. +# +# See https://leetcode.com/problems/construct-quad-tree +from leetcode.tree.common.quad_tree import Node + + +class Solution: + def construct(self, grid: list[list[int]]) -> Node | None: + """ + SOLUTION + -------- + + To make a quad tree, recursively divide the matrix into quadrants until each quadrant is all 1's or all 0's. + + It's not clear why, but in LeetCode, the Node class must be implement and pasted in the solution. + + COMPLEXITY + ---------- + + Time complexity is O(n^2) because we are visiting each cell in the grid. + + Space complexity is O(log n) because the recursion depth is limited by the size of the grid. + """ + if not grid or not grid[0]: + return None + + return self.__construct(grid, 0, 0, len(grid)) + + def __construct(self, grid: list[list[int]], row: int, column: int, size: int) -> Node | None: + node = Node() + + if self.__isUniform(grid, row, column, size): + node.val = bool(grid[row][column]) + node.isLeaf = True + return node + + # If the grid segment here isn't uniform, we'll need to create 4 quadrants and construct nodes out of all 4 + # of them. + # + # Setting the `node.val` of this node is irrelevant as it's not a leaf. We can just use the default value. + half = size // 2 + node.isLeaf = False + node.topLeft = self.__construct(grid, row, column, half) + node.topRight = self.__construct(grid, row, column + half, half) + node.bottomLeft = self.__construct(grid, row + half, column, half) + node.bottomRight = self.__construct(grid, row + half, column + half, half) + return node + + # Checks if a quadrant of the grid starting at [row, column] is uniformly all 1's or all 0's. + def __isUniform(self, grid: list[list[int]], row: int, column: int, size: int) -> bool: + value = grid[row][column] + for i in range(row, row + size): + for j in range(column, column + size): + if grid[i][j] != value: + return False + + return True diff --git a/src/leetcode/tree/design_in_memory_file_system.py b/src/leetcode/tree/design_in_memory_file_system.py new file mode 100644 index 0000000..614764e --- /dev/null +++ b/src/leetcode/tree/design_in_memory_file_system.py @@ -0,0 +1,109 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Design a data structure that simulates an in-memory file system. +# +# Implement the FileSystem class: +# +# - FileSystem() +# Initializes the object of the system. +# - List ls(String path) +# If path is a file path, returns a list that only contains this file's name. +# If path is a directory path, returns the list of file and directory names in this directory. +# The answer should in lexicographic order. +# - void mkdir(String path) +# Makes a new directory according to the given path. The given directory path does not exist. +# If the middle directories in the path do not exist, you should create them as well. +# - void addContentToFile(String filePath, String content) +# If filePath does not exist, creates that file containing given content. +# If filePath already exists, appends the given content to original content. +# - String readContentFromFile(String filePath) +# Returns the content in the file at filePath. +# +# See https://leetcode.com/problems/design-in-memory-file-system +class FileNode: + def __init__(self): + self.is_file = False + self.content: str | None = None + self.children: "dict[str, FileNode]" = {} + + +class FileSystem: + def __init__(self): + """ + SOLUTION + -------- + + The problem states that we can assume all inputs are valid, so we don't need to check if we try to mkdir on path + parts that contain files, or otherwise list children of files. We can also assume that all listed directories + and files exist. + + COMPLEXITY + ---------- + + Time complexity is O(n) for all operations. + + Space complexity is O(n) where n is the number of directories and files in the file system. + """ + self.root = FileNode() + + def ls(self, path: str) -> list[str]: + node = self.root + + parts = self.__normalize(path).split("/") + for part in parts: + if part not in node.children: + return [] + + node = node.children[part] + + # Note that the ls function only wants to know the name of the file, not the absolute path, so don't bother + # prefixing the parent path. + if node.is_file: + return [part] + + children = list(node.children.keys()) + children.sort() + return children + + def mkdir(self, path: str) -> None: + node = self.root + + parts = self.__normalize(path).split("/") + for part in parts: + if part not in node.children: + node.children[part] = FileNode() + + node = node.children[part] + + def addContentToFile(self, filePath: str, content: str) -> None: + node = self.root + + parts = self.__normalize(filePath).split("/") + for i, part in enumerate(parts): + # The problem isn't exactly clear here, but the assumption is that we also mkdir the paths if they do not + # exist. + if part not in node.children: + node.children[part] = FileNode() + + node = node.children[part] + + # The very last part is the filename, so append the content. + if i == len(parts) - 1: + value = node.content if node.content else "" + node.content = value + content + node.is_file = True + + def readContentFromFile(self, filePath: str) -> str: + node = self.root + + parts = self.__normalize(filePath).split("/") + for part in parts: + node = node.children[part] + + return node.content if node.content else "" + + def __normalize(self, path: str) -> str: + if path == "/": + return "" + return path[0:-1] if path[-1] == "/" else path diff --git a/src/leetcode/tree/diameter_of_binary_tree.py b/src/leetcode/tree/diameter_of_binary_tree.py new file mode 100644 index 0000000..72a2532 --- /dev/null +++ b/src/leetcode/tree/diameter_of_binary_tree.py @@ -0,0 +1,59 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given the root of a binary tree, return the length of the diameter of the tree. +# +# The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or +# may not pass through the root. +# +# The length of a path between two nodes is represented by the number of edges between them. +# +# See https://leetcode.com/problems/diameter-of-binary-tree +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def diameterOfBinaryTree(self, root: TreeNode | None) -> int: + """ + SOLUTION + -------- + + For any node, the longest path might: + + - Pass through the node, in which case the longest path is the longest path of the left and right subtrees, plus + one. + - Not pass through the node, in which case the longest path is the max of the longest path of the left and + right. + + We can use DFS to traverse the tree and keep track of the longest path we've seen so far. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of nodes in the tree. + + Space complexity is O(n) because of the recursive stack. + """ + max_path = 0 + + def dfs(node: TreeNode | None) -> int: + nonlocal max_path + + if not node: + return 0 + + # Figure out the max depth of the left and right subtrees. + left = dfs(node.left) + right = dfs(node.right) + + # The left and right depths represent the current diameter of the tree rooted at this node. We'll compare + # it with the global max diameter. + current_path = left + right + max_path = max(max_path, current_path) + + # Return the max depth starting from the current node. Whichever path (left or right) is longer will be the + # max depth, but we'll add one to account for the current node. + return max(left, right) + 1 + + dfs(root) + return max_path diff --git a/src/leetcode/tree/lowest_common_ancestor_of_a_binary_tree.py b/src/leetcode/tree/lowest_common_ancestor_of_a_binary_tree.py new file mode 100644 index 0000000..cd07ae9 --- /dev/null +++ b/src/leetcode/tree/lowest_common_ancestor_of_a_binary_tree.py @@ -0,0 +1,87 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given two nodes of a binary tree p and q, return their lowest common ancestor (LCA). +# +# According to the definition of LCA on Wikipedia: "The lowest common ancestor of two nodes p and q in a tree T is the +# lowest node that has both p and q as descendants (where we allow a node to be a descendant of itself)." +# +# See https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode: + """ + SOLUTION + -------- + + In this problem, we are not given the parent nodes. So we have to solve this problem using a recursive divide + and conquer approach. + + The idea is to recursively check the left and right subtrees for nodes p and q. There are three cases: + + 1. We find p and q in DIFFERENT subtrees. + 2. We find p and q BOTH in the left subtree. + 3. We find p and q BOTH in the right subtree. + + Well, in case 1, if we began this process from the root, that's it! The current node has to be the LCA. If + instead we discover that p and q are BOTH in the left or right subtree, make that subtree the new root and + continue the search. + + Note that this solution requires that both p and q exist in the tree. If one of them doesn't exist we could + return a false LCA. To guard against this we'd have to check the tree beforehand to ensure that both p and q + exist. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of nodes in the tree. + + Space complexity is O(n) because of the recursive stack. + """ + + # Recursively find p or q in the tree rooted at node. + def findNode(node: TreeNode | None, p: TreeNode | None, q: TreeNode | None) -> TreeNode | None: + # We DID NOT find p or q in either of the subtrees. + if not node: + return None + + # We DID find p or q in either of the subtrees, so return it. + if node == p or node == q: + return node + + # Otherwise, continue by checking for p and q in the left and right subtrees. + # + # - If left is non-null, then we found one of p or q in the left subtree. + # - If right is non-null, then we found one of p or q in the right subtree. + # - If both were non-null, then we found p and q in different subtrees. + left = findNode(node.left, p, q) + right = findNode(node.right, p, q) + + # If BOTH left and right are non-null, then we found p and q in different subtrees. If that is the case, + # the current node must be the LCA. + if left and right: + return node + + # We found ONE of nodes p or q in the LEFT subtree. However, we didn't find the other node in the right + # subtree so we assume that they were both in the left subtree. Note that we ASSUME that both p and q exist + # in the tree, or else we could return a false LCA. + if left: + return left + # We found ONE of nodes p or q in the RIGHT subtree. However, we didn't find the other node in the left + # subtree so we assume that they were both in the right subtree. Note that we ASSUME that both p and q + # exist in the tree, or else we could return a false LCA. + else: + return right + + # Technically, lowestCommonAncestor has the same signature as findNode, so we could've just implemented the LCA + # logic directly. + # + # However, lowestCommonAncestor will return to you the LCA always. In findNode, sometimes the intermediate + # result is not the LCA (e.g. when we are just looking for the node). + lca = findNode(root, p, q) + + # The problem appears to guarantee that p and q exist in the tree, and will be non-null. + assert lca + return lca diff --git a/src/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_iii.py b/src/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_iii.py new file mode 100644 index 0000000..dc3aa74 --- /dev/null +++ b/src/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_iii.py @@ -0,0 +1,63 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given two nodes of a binary tree p and q, return their lowest common ancestor (LCA). +# +# According to the definition of LCA on Wikipedia: "The lowest common ancestor of two nodes p and q in a tree T is the +# lowest node that has both p and q as descendants (where we allow a node to be a descendant of itself)." +# +# See https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree-iii +from leetcode.tree.common.parent_node import Node + + +class Solution: + def lowestCommonAncestor(self, p: Node, q: Node) -> Node: + """ + SOLUTION + -------- + + Note that this problem is quite simple since we are given parent nodes. All we have to do is traverse up to the + root for one node and collect ancestors. Then we traverse up to the root for the other node and check if any + parent is in the ancestor set. If it is, then we have found the lowest common ancestor. + + However, that solution involves the use of a set. We can optimize this by using the two pointer technique in a + clever way. Let's consider paths from p and q to the LCA. + + If path[p] == path[q], then all we have to do is advance both pointers one step at a time and they will + converge at the LCA eventually. + + If path[p] != path[q], then one of the pointers will reach past the root and become null first. + + The goal, then is to make BOTH pointers reach the LCA at the same time. So let's say len(path[p]) == 5, and + that len(path[q]) == 10. If we could craft an extended path for both p and q so that they both travel the same + distance, and end up at the LCA, then that would work. + + To create this extended path, envision an extended path2[p] that goes from p to the root then to q then to the + root (yes, imagine there's a direct path from root -> q). Likewise, envision an extended path2[q] that goes + from q to the root then from p to the root. + + Now if you make both the p and q pointers follow this path, they will both advance PAST the root, but then + eventually converge at the LCA before they hit the root again. + + It's not clear why, but in LeetCode, the Node class must be implement and pasted in the solution. + + COMPLEXITY + ---------- + + Time complexity is O(h) where h is the height of tree. + + Space complexity is O(1). + """ + # Create new pointers to advance; we need the original starting points p and q. + a, b = p, q + + # Eventually a and b will converge at the LCA. + while a != b: + # Advance a = p -> root -> q -> root. Pointers converge before hitting the root again. + a = a.parent if a else q + # Advance b = q -> root -> p -> root. Pointers converge before hitting the root again. + b = b.parent if b else p + + # Return either pointer; they are both at the LCA. + assert a + return a diff --git a/src/leetcode/tree/range_sum_of_bst.py b/src/leetcode/tree/range_sum_of_bst.py new file mode 100644 index 0000000..a3cbc1e --- /dev/null +++ b/src/leetcode/tree/range_sum_of_bst.py @@ -0,0 +1,39 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given the root node of a binary search tree and two integers low and high, return the sum of values of all nodes +# with a value in the inclusive range [low, high]. +# +# See https://leetcode.com/problems/range-sum-of-bst +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def rangeSumBST(self, root: TreeNode | None, low: int, high: int) -> int: + """ + SOLUTION + -------- + + A simple recursive solution will work. + + COMPLEXITY + ---------- + + The time complexity is O(n) where n is the number of nodes in the tree. + """ + range_sum = 0 + + def traverse(node: TreeNode | None): + nonlocal range_sum + + if not node: + return + + if low <= node.val <= high: + range_sum += node.val + + traverse(node.left) + traverse(node.right) + + traverse(root) + return range_sum diff --git a/src/leetcode/tree/same_tree.py b/src/leetcode/tree/same_tree.py new file mode 100644 index 0000000..14157d9 --- /dev/null +++ b/src/leetcode/tree/same_tree.py @@ -0,0 +1,34 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given the roots of two binary trees p and q, write a function to check if they are the same or not. +# +# Two binary trees are considered the same if they are structurally identical, and the nodes have the same value. +# +# See https://leetcode.com/problems/same-tree +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def isSameTree(self, p: TreeNode | None, q: TreeNode | None) -> bool: + """ + SOLUTION + -------- + + A simple recursive solution will work. + + COMPLEXITY + ---------- + + The time complexity is O(n) where n is the number of nodes in the tree. + """ + if not p: + return not q + + if not q: + return not p + + if p.val != q.val: + return False + + return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) diff --git a/src/leetcode/tree/sum_root_to_leaf_numbers.py b/src/leetcode/tree/sum_root_to_leaf_numbers.py new file mode 100644 index 0000000..cbf6ed9 --- /dev/null +++ b/src/leetcode/tree/sum_root_to_leaf_numbers.py @@ -0,0 +1,53 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given the root of a binary tree containing digits from 0 to 9 only. +# +# Each root-to-leaf path in the tree represents a number. +# +# For example, the root-to-leaf path 1 -> 2 -> 3 represents the number 123. +# +# Return the total sum of all root-to-leaf numbers. Test cases are generated so that the answer will fit in a 32-bit +# integer. +# +# A leaf node is a node with no children. +# +# See https://leetcode.com/problems/sum-root-to-leaf-numbers +from leetcode.tree.common.tree_node import TreeNode + + +class Solution: + def sumNumbers(self, root: TreeNode | None) -> int: + """ + SOLUTION + -------- + + This is a simple DFS problem. Just keep track of the current number as you traverse the tree. If you reach a + leaf, add it to the running tally and sum up all the numbers at the end. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of nodes in the tree. + + Space complexity is O(n) for the call stack. + """ + xs: list[int] = [] + + def dfs(node: TreeNode | None, sum: int) -> None: + nonlocal xs + + if not node: + return + + # Only add to the running list if we're at a leaf. + x = sum * 10 + node.val + if not node.left and not node.right: + xs.append(x) + return + + dfs(node.left, x) + dfs(node.right, x) + + dfs(root, 0) + return sum(xs) diff --git a/src/two-pointer/README.md b/src/leetcode/two_pointers/README.md similarity index 53% rename from src/two-pointer/README.md rename to src/leetcode/two_pointers/README.md index e7e8f0a..c8b8018 100644 --- a/src/two-pointer/README.md +++ b/src/leetcode/two_pointers/README.md @@ -4,20 +4,20 @@ These phrases indicate the two pointer technique might be useful: -- 'non-fixed size subarray with condition' -- 'sorted array', 'sorted list' -- 'find pairs', 'find triplets' -- 'maximize delta', 'minimize delta' +- non-fixed size subarray with condition +- sorted array, sorted list +- find pairs, find triplets +- maximize delta, minimize delta ## Boundaries A note on boundary conditions when you have two pointers that move towards each other. -Use the `left < right` condition if it's not necessary to process every element. For example: +Use the `left < right` condition if its not necessary to process every element. For example: - palindromes checking, because the middle element is guaranteed to match itself. -- container with most water, because the when `left === right`, you have a container that does not hold water. +- container with most water, because the when `left == right`, you have a container that does not hold water. Use the `left <= right` condition when it is necessary to process every element. For example: -- bag of tokens, because you must consider the ramifications of processing the token at `left === right`. +- bag of tokens, because you must consider the ramifications of processing the token at `left == right`. diff --git a/src/leetcode/two_pointers/bag_of_tokens.py b/src/leetcode/two_pointers/bag_of_tokens.py new file mode 100644 index 0000000..b40c7dd --- /dev/null +++ b/src/leetcode/two_pointers/bag_of_tokens.py @@ -0,0 +1,72 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You start with an initial power of `power`, an initial score of 0, and a bag of tokens given as an integer array +# tokens, where each tokens[i] denotes the value of tokeni. +# +# Your goal is to maximize the total score by strategically playing these tokens. In one move, you can play an unplayed +# token in one of the two ways (but not both for the same token): +# +# Face-up: If your current power is at least tokens[i], you may play tokeni, losing tokens[i] power and gaining 1 +# score. +# Face-down: If your current score is at least 1, you may play tokeni, gaining tokens[i] power and losing 1 score. +# +# Return the maximum possible score you can achieve after playing any number of tokens. +# +# See https://leetcode.com/problems/bag-of-tokens +class Solution: + def bagOfTokensScore(self, tokens: list[int], power: int) -> int: + """ + SOLUTION + -------- + + You basically are allowed to gain power at the cost of score, or gain score at the cost of power. You want to + maximize the score. The problem does not tell you this, but: + + 1. You may view all the tokens in the bag before making any plays. + 1. You may play tokens in the bag in any order. + 2. You may opt to not play any tokens at all. + + Because score is always gained one point at a time, but power can be be any value, we'll want to spend the least + amount of power for score. That is, sacrifice smaller power tokens face up for score, play higher power tokens + for their raw (power) value. + + COMPLEXITY + ---------- + + Time complexity is dominated by sorting, making it O(n log n). + + Space complexity is O(1). + """ + # Sort the tokens so we can take power from the right end (higher power), and sacrifice tokens for score from + # the left end. + tokens.sort() + + # This problem is well suited for using the two pointers technique, iterating through the array from both + # directions. + left = 0 + right = len(tokens) - 1 + score = 0 + max_score = 0 + + while left <= right: + # If we can, try to eat the smaller power tokens to build score until we run out of power. + if power >= tokens[left]: + power -= tokens[left] + score += 1 + left += 1 + # It can be the case that while running through the bag of tokens, we've achieved maximum score possible. + # However, we should continue going through the bag just to check. + max_score = max(max_score, score) + + # If we can't eat a smaller token, let's eat a bigger token to build power so we can eat more later. + elif score > 0: + power += tokens[right] + score -= 1 + right -= 1 + + # If we cannot eat any tokens, then simply quit. + else: + break + + return max_score diff --git a/src/leetcode/two_pointers/container_with_most_water.py b/src/leetcode/two_pointers/container_with_most_water.py new file mode 100644 index 0000000..9b79648 --- /dev/null +++ b/src/leetcode/two_pointers/container_with_most_water.py @@ -0,0 +1,66 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# You are given an integer array height of length n. There are n vertical lines drawn such that the two endpoints of +# the ith line are (i, 0) and (i, height[i]). +# +# Find two lines that together with the x-axis form a container, such that the container contains the most water. +# +# Return the maximum amount of water a container can store. +# +# Notice that you may not slant the container. +# +# See https://leetcode.com/problems/container-with-most-water +class Solution: + def maxArea(self, height: list[int]) -> int: + """ + SOLUTION + -------- + + Use the two pointers technique to maximize the area. We'll initialize at the left and right sides of the array, + then advance the pointers inward and update our knowledge of the maximum area. + + We'll want to go from outside to inside, because if we have the highest columns on the outside, it's trivial to + calculate the max area container. The problem is if we have shorter columns over a long width, but we can adjust + the pointers and max area as we move them inwards. + + To maximize the area, we should advance the pointer that points to the lowest height, in some hopes that we will + reach a higher height area and get a bigger container. + + Note that if we advance a pointer and the height ends up lower, we'll end up with an area calculation that does + not correspond to an actual container. However, that "phantom container" will have a smaller max area than + what was previously calculated, and it won't affect the actual result. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of heights in the array. + + Space complexity is O(1). + """ + max_area = 0 + left = 0 + right = len(height) - 1 + + # Use left < right because there's no need to process left === right (that's a container with no area). + while left < right: + # Set the width to be the difference between left and right pointers. The height should be the smaller of + # the two heights referenced by the pointers. + width = right - left + h = min(height[left], height[right]) + max_area = max(max_area, width * h) + + # Now advance the pointers. No matter which pointer we choose to advance, the width will decrease. + # + # To attempt to maximize the area, we should advance the pointer that points to the lowest height, in some + # hopes that we will reach a higher height area and get a bigger container. + # + # Note that if we advance a pointer and the height ends up lower, we'll end up with an area calculation that + # does not correspond to an actual container. However, that "phantom container" will have a smaller max + # area than what was previously calculated, and it won't affect the actual result. + if height[left] < height[right]: + left += 1 + else: + right -= 1 + + return max_area diff --git a/src/leetcode/two_pointers/longest_common_prefix.py b/src/leetcode/two_pointers/longest_common_prefix.py new file mode 100644 index 0000000..d632c8c --- /dev/null +++ b/src/leetcode/two_pointers/longest_common_prefix.py @@ -0,0 +1,55 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Write a function to find the longest common prefix string amongst an array of strings. +# +# If there is no common prefix, return an empty string "". +# +# See https://leetcode.com/problems/longest-common-prefix +class Solution: + def longestCommonPrefix(self, all: list[str]) -> str: + """ + SOLUTION + -------- + + Note that the longest prefix can only be, at most, the length of the shortest string. We can start with the + first string as a guess for the longest prefix, then tighten the bounds of that guess as we look the other + strings. + + COMPLEXITY + ---------- + + Time complexity is O(n * m) where n is the number of strings and m is the length of the shortest string. + + Space complexity is O(1). + """ + if not all: + return "" + + # The longest prefix can be at most the length of the shortest string. Therefore, we can set our best guess of + # the longest prefix to the first string. From there, we can tighten the bounds of the prefix and come up with + # an even shorter string as a result. + longest = all[0] + for i in range(1, len(all)): + current = all[i] + + # Compare the current string, character by character, against the longest prefix we've found so far. The + # length of matching characters will be used to update our assumption about the longest prefix afterwards. + j = 0 + while ( + # If the characters at the current position DO match, but we exceeded the length of either the current + # string or the longest prefix, we have to stop. There are no more characters to compare against each + # other. + j < min(len(current), len(longest)) + and + # If the characters at the current position DO NOT match, stop. The longest prefix will become where we + # have stopped. + longest[j] == current[j] + ): + j += 1 + + # The length j represents how many characters match between the current prefix and the longest string, so + # update that value. + longest = current[:j] + + return longest diff --git a/src/leetcode/two_pointers/remove_duplicates_from_sorted_array.py b/src/leetcode/two_pointers/remove_duplicates_from_sorted_array.py new file mode 100644 index 0000000..1b9e926 --- /dev/null +++ b/src/leetcode/two_pointers/remove_duplicates_from_sorted_array.py @@ -0,0 +1,58 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given an integer array nums sorted in non-decreasing order, remove the duplicates in-place such that each unique +# element appears only once. The relative order of the elements should be kept the same. Then return the number of +# unique elements in nums. +# +# Consider the number of unique elements of nums to be k, to get accepted, you need to do the following things: +# +# Change the array nums such that the first k elements of nums contain the unique elements in the order they were +# present in nums initially. The remaining elements of nums are not important as well as the size of nums. +# +# Return k. +# +# See https://leetcode.com/problems/remove-duplicates-from-sorted-array +class Solution: + def removeDuplicates(self, nums: list[int]) -> int: + """ + SOLUTION + -------- + + Use the two pointer technique to iterate through the array at different speeds. The first pointer moves through + the array as number. The second pointer, j, will point to the last unique element. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of elements in the array. + + Space complexity is O(1). + """ + if not nums: + return 0 + + # Initialize the first pointer that moves through the array at normal speed. The first element is always + # unique. + i = 1 + + # Initialize the second pointer to move only if we found a unique element. + j = 0 + + while i < len(nums): + current = nums[i] + last_unique = nums[j] + + # If the current element is different from the last unique element, then we have a new unique element. So + # advance the unique pointer, then copy the current element to the unique pointer. + if current != last_unique: + j += 1 + nums[j] = current + + # Now advance the other pointer to continue through the array. + i += 1 + + # The index j tracks the last unique element, so the number of unique elements is j + 1. Contrast this to the + # remove element problem. + k = j + 1 + return k diff --git a/src/leetcode/two_pointers/remove_element.py b/src/leetcode/two_pointers/remove_element.py new file mode 100644 index 0000000..d437b67 --- /dev/null +++ b/src/leetcode/two_pointers/remove_element.py @@ -0,0 +1,52 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given an integer array nums and an integer val, remove all occurrences of val in nums in-place. The order of the +# elements may be changed. Then return the number of elements in nums which are not equal to val. +# +# Consider the number of elements in nums which are not equal to val be k, to get accepted, you need to do the +# following things: +# +# - Change the array nums such that the first k elements of nums contain the elements which are not equal to val. +# The remaining elements of nums are not important as well as the size of nums. +# - Return k. +# +# See https://leetcode.com/problems/remove-element +class Solution: + def removeElement(self, nums: list[int], val: int) -> int: + """ + SOLUTION + -------- + + The solution is very similar to the remove duplicates from sorted array problem. We just need to maintain two + pointers; one that moves through the array at normal speed and another that moves only when we find an element + that is not equal to val. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of elements in the array. + + Space complexity is O(1). + """ + # Initialize the first pointer that moves through the array at normal speed. The first element is always + # unique. + i = 0 + + # Initialize the second pointer to move only if we found an element not equal to val. + j = 0 + + while i < len(nums): + current = nums[i] + + # If we found an element that's not equal to val, we should keep it. So copy it to the j pointer. + if current != val: + nums[j] = current + j += 1 + + # Now advance the other pointer to continue through the array. + i += 1 + + # The index j represents the number of elements that are not equal to val. That means j === k, so we can return + # it directly. Contrast this with remove duplicates from sorted array. + return j diff --git a/src/leetcode/two_pointers/string_compression.py b/src/leetcode/two_pointers/string_compression.py new file mode 100644 index 0000000..ab14ba5 --- /dev/null +++ b/src/leetcode/two_pointers/string_compression.py @@ -0,0 +1,59 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an array of characters chars, compress it using the following algorithm: +# +# Begin with an empty string s. For each group of consecutive repeating characters in chars: +# +# - If the group's length is 1, append the character to s. +# - Otherwise, append the character followed by the group's length. +# +# The compressed string s should not be returned separately, but instead, be stored in the input character array chars. +# Note that group lengths that are 10 or longer will be split into multiple characters in chars. +# +# After you are done modifying the input array, return the new length of the array. +# +# You must write an algorithm that uses only constant extra space. +# +# See https://leetcode.com/problems/string-compression +class Solution: + def compress(self, cs: list[str]) -> int: + """ + SOLUTION + -------- + + Because we cannot use extra space, we will need to read and write to the array at the same time. We can use the + two pointer approach for doing so. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of characters in the array. + + Space complexity is O(1). + """ + read = 0 + write = 0 + + while read < len(cs): + # Keep track of the current character and the number of times it has appeared. + c = cs[read] + n = 0 + + # Consume the characters until we reach a point where the character differs. + while read < len(cs) and cs[read] == c: + read += 1 + n += 1 + + # Write the current character and the number of times it appears. We'll always have enough room because + # we'll only need to write more than one digit if we have 10 or more characters. + cs[write] = c + write += 1 + + # Only write digits if the count > 1; otherwise just leave the character as is. + if n > 1: + for digit in str(n): + cs[write] = digit + write += 1 + + return write diff --git a/src/leetcode/two_pointers/three_sum.py b/src/leetcode/two_pointers/three_sum.py new file mode 100644 index 0000000..9ad48b2 --- /dev/null +++ b/src/leetcode/two_pointers/three_sum.py @@ -0,0 +1,93 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that +# i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0. +# +# Notice that the solution set must not contain duplicate triplets. +# +# See https://leetcode.com/problems/3sum +class Solution: + def threeSum(self, xs: list[int]) -> list[list[int]]: + """ + SOLUTION + -------- + + The two sum problem can be solved with a two pointer approach. This can be solved with a similar approach, but + you'll need to apply the two pointer approach to every element in the array. + + COMPLEXITY + ---------- + + Time complexity is O(n^2) where n is the number of elements in the array. + + Space complexity is O(1). + """ + result: list[list[int]] = [] + + # Sorting will help us more efficiently use the sliding window approach to find the triplets that sum to 0. + # + # Note that xs.sort() will sort by string value. So if you have negative numbers, something like [-1, -2, 0] is + # considered "sorted". To fix this, make sure to explicitly provide a compare function. + xs.sort() + + # For each of the elements we will use a sliding window approach to find the other two elements that will sum up + # to 0. + # + # There are two ways we can think of this: we can think of our current element as element b, then set the left + # point to 0 and the right pointer to the length of the array - 1. However, if we do this, we will reconsider + # elements we've already seen as the pointer advances. + # + # Instead, we'll consider the current element as a, then b starts at left = i + 1, and c starts at + # right = length - 1. + for i in range(len(xs) - 2): + # Because we can't have duplicate triples in the result, we should just skip over any duplicates. + if i > 0 and xs[i] == xs[i - 1]: + continue + + # For each element a, use the two pointers technique to find (b, c) such that a + b + c = 0. + a = xs[i] + + # We cannot have duplicate triples in the result. We can do this by setting the left pointer to 0, and the + # right pointer to the last element, tightening the bounds as we consider sums. However, this will + # reconsider duplicate triples as i advances. We would need a set to dedupe the triples. + # + # Starting the left pointer at i + 1 avoids this problem. + left = i + 1 + right = len(xs) - 1 + while left < right: + b = xs[left] + c = xs[right] + total = a + b + c + + # If the sum is less than 0, that means we need to increase the sum, so advance the left pointer. + if total < 0: + left += 1 + continue + + # If the sum is greater than 0, that means we need to decrease the sum, so advance the right pointer. + if total > 0: + right -= 1 + continue + + # If the sum is the target of 0, add the triple to the result array. However, there may yet still be + # more triplets we haven't found with our pointers! + # + # To find them, we should keep advancing the pointers past any dupes (as long as they are in range). + if total == 0: + result.append([a, b, c]) + + # Advance the left pointer to skip any dupes of b. + while left < right and xs[left + 1] == xs[left]: + left += 1 + + # Advance the right pointer to skip any dupes of c. + while left < right and xs[right - 1] == xs[right]: + right -= 1 + + # Advance both pointers to consider the next potential sum. We need to advance here and not before + # doing the while loops because advancing could have put us into a dupe! + left += 1 + right -= 1 + + return result diff --git a/src/leetcode/two_pointers/three_sum_closest.py b/src/leetcode/two_pointers/three_sum_closest.py new file mode 100644 index 0000000..1bc0485 --- /dev/null +++ b/src/leetcode/two_pointers/three_sum_closest.py @@ -0,0 +1,66 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums of length n and an integer target, find three integers in nums such that the sum is +# closest to target. +# +# Return the sum of the three integers. +# +# You may assume that each input would have exactly one solution. +# +# See https://leetcode.com/problems/3sum-closest +import math + + +class Solution: + def threeSumClosest(self, xs: list[int], target: int) -> int: + """ + SOLUTION + -------- + + We'll use the two pointer technique to zero in on the target sum while iterating through the array. To make + calculations easier, we have to sort the array. + + COMPLEXITY + ---------- + + Time complexity is O(n^2) where n is the number of elements in the array. + + Space complexity is O(1). + """ + xs.sort() + + closest_sum = math.inf + closest_values = [] + + for i in range(len(xs) - 2): + # Use the two pointer technique to find the closest triple. We will move the left pointer forward, or move + # the right pointer backwards as we attempt to get close to our target sum. + # + # Initialize the left pointer to the right of i, and initialize the right pointer at the end of the array. + # We will begin moving the left and right pointers closer to each other as we attempt to compute the triple. + left = i + 1 + right = len(xs) - 1 + + while left < right: + # If the current sum is closer than what we have, update the closest sum and the triple. + current_sum = xs[i] + xs[left] + xs[right] + current_distance = abs(current_sum - target) + closest_distance = abs(closest_sum - target) + if current_distance < closest_distance: + closest_sum = current_sum + closest_values = [xs[i], xs[left], xs[right]] + + # If we've matched the target sum, just return right away. + if closest_sum == target: + return sum(closest_values) + + # Update the left and right pointers, and try again. Because we've sorted the array, we can move the + # pointers accordingly. If the sum is too small, move the left pointer forwards. If the sum is too + # big, move the the right pointer backwards. + if current_sum < target: + left += 1 + else: + right -= 1 + + return sum(closest_values) diff --git a/src/leetcode/two_pointers/trapping_rain_water.py b/src/leetcode/two_pointers/trapping_rain_water.py new file mode 100644 index 0000000..8352f93 --- /dev/null +++ b/src/leetcode/two_pointers/trapping_rain_water.py @@ -0,0 +1,71 @@ +# DIFFICULTY: HARD +# ---------------- +# +# Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water +# it can trap after raining. +# +# See https://leetcode.com/problems/trapping-rain-water +class Solution: + def trap(self, elevation_map: list[int]) -> int: + """ + SOLUTION + -------- + + Note that for this question, the leftmost element of the elevation map doesn't trap any water, because there is + no land/bar to the left of the leftmost element. + + Similarly, for the rightmost element, any depression of the land/bar doesn't trap any water because there isn't + any elevation to the right of the rightmost element. + + To solve this we can use the two pointers technique to compute trapped rainwater on a pointer by pointer basis, + advancing the pointers. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of elements in the elevation map. + + Space complexity is O(1). + """ + # The difference between the lowest height and the smaller of the max height from the left and right sides + # contributes to that many units of trapped rainwater AT THE CURRENT INDEX. + # + # For example, a current elevation of zero with a max elevation of 4 and 6 on the left and right sides + # respectively will hold 4 units of trapped rainwater. We'll use this information to compute rainwater at each + # index. + trapped = 0 + + # Track the maximum height on the left side and right side, based on where our pointers land. The smaller of + # these two values will be the height we will use to compute trapped rainwater (since any rainwater over the + # smaller value will spill out). + left_max = 0 + right_max = 0 + max_value = 0 + + # Use the two pointers technique to compute the trapped rainwater. + left = 0 + right = len(elevation_map) - 1 + while left < right: + # Update the left and right max values, then compute the smaller of these two values to figure out the + # amount of rainwater we can actually trap. + left_max = max(left_max, elevation_map[left]) + right_max = max(right_max, elevation_map[right]) + + # This is the max amount we can trap with an elevation of zero. We compute the elevation next to figure out + # how much is really trapped. + max_value = min(left_max, right_max) + + # Compute the trapped rainwater from one of the pointers, then advance it. + # + # Choose to advance the pointer associated with the smaller height inward, so that we have the opportunity + # to move to a higher elevation and trap more water. + if elevation_map[left] < elevation_map[right]: + elevation = elevation_map[left] + trapped += max_value - elevation + left += 1 + else: + elevation = elevation_map[right] + trapped += max_value - elevation + right -= 1 + + return trapped diff --git a/src/leetcode/two_pointers/two_sum_ii_input_array_sorted.py b/src/leetcode/two_pointers/two_sum_ii_input_array_sorted.py new file mode 100644 index 0000000..c2b8584 --- /dev/null +++ b/src/leetcode/two_pointers/two_sum_ii_input_array_sorted.py @@ -0,0 +1,45 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given a 1-indexed array of integers numbers that is already sorted in non-decreasing order, find two numbers such +# that they add up to a specific target number. Let these two numbers be numbers[index1] and numbers[index2] where +# 1 <= index1 < index2 <= numbers.length. +# +# Return the indices of the two numbers, index1 and index2, added by one as an integer array [index1, index2] of +# length 2. +# +# The tests are generated such that there is exactly one solution. You may not use the same element twice. +# +# Your solution must use only constant extra space. +# +# See https://leetcode.com/problems/two-sum-ii-input-array-is-sorted +class Solution: + def twoSum(self, numbers: list[int], target: int) -> list[int]: + """ + SOLUTION + -------- + + Use the two pointer approach to narrow in on the target sum from both the left and the right. + + COMPLEXITY + ---------- + + Time complexity is O(n) where n is the number of elements in the array. + + Space complexity is O(1). + """ + left = 0 + right = len(numbers) - 1 + + while left < right: + sum = numbers[left] + numbers[right] + if sum == target: + return [left + 1, right + 1] + + if sum < target: + left += 1 + + if sum > target: + right -= 1 + + return [] diff --git a/src/leetcode/two_pointers/valid_palindrome.py b/src/leetcode/two_pointers/valid_palindrome.py new file mode 100644 index 0000000..f82f55e --- /dev/null +++ b/src/leetcode/two_pointers/valid_palindrome.py @@ -0,0 +1,42 @@ +# DIFFICULTY: EASY +# ---------------- +# +# A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all +# non-alphanumeric characters, it reads the same forward and backward. Alphanumeric characters include letters and +# numbers. +# +# Given a string s, return true if it is a palindrome, or false otherwise. +# +# See {@link https://leetcode.com/problems/valid-palindrome/} +import re + + +class Solution: + def isPalindrome(self, s: str) -> bool: + """ + SOLUTION + -------- + + Use the two pointer technique to compare elements from the ends of the array to see if you have a match. + + COMPLEXITY + ---------- + + Time complexity is O(n). + + Space complexity is O(1). + """ + # Replace all non-alphanumeric characters with an empty string. + text = re.sub(r"[^a-zA-Z0-9]", "", s).lower() + + # Use left < right here because it's not necessary to check if the middle character matches itself. + left = 0 + right = len(text) - 1 + while left < right: + if text[left] != text[right]: + return False + + left += 1 + right -= 1 + + return True diff --git a/src/leetcode/two_pointers/valid_palindrome_ii.py b/src/leetcode/two_pointers/valid_palindrome_ii.py new file mode 100644 index 0000000..32dfabd --- /dev/null +++ b/src/leetcode/two_pointers/valid_palindrome_ii.py @@ -0,0 +1,61 @@ +# DIFFICULTY: EASY +# ---------------- +# +# Given a string s, return true if the s can be palindrome after deleting at most one character from it. +# +# See https://leetcode.com/problems/valid-palindrome-ii +class Solution: + def validPalindrome(self, s: str) -> bool: + """ + SOLUTION + -------- + + Use the two pointer technique to check if the character from the left matches the character on the right. If + they do, just proceed as normal checking characters. + + If they do not match, we have one chance to check if deleting either the left character, or the right character, + results in a palindrome (we can only delete one character). To do this check, we only need to check a subset of + the characters. + + COMPLEXITY + ---------- + + Time complexity is O(n). The outer loop runs in O(n) time; if the word is a palindrome, the inner loop will + never be called, resulting in O(n) runtime. The inner loop from isPalindrome() will also run in O(n) time, and + they will be called at most twice. This results in time complexity of O(n + 2 * n) = O(n). + + Space complexity is O(1). + """ + + # Check if a string is a palindrome starting using the supplied left/right pointers. + def isPalindrome(left: int, right: int) -> bool: + while left < right: + if s[left] != s[right]: + return False + + left += 1 + right -= 1 + + return True + + # Use left < right here because it's not necessary to check if the middle character matches itself. + left = 0 + right = len(s) - 1 + while left < right: + # If characters match, proceed as normal. + if s[left] == s[right]: + left += 1 + right -= 1 + continue + + # Characters didn't match; check if deleting/skipping the left character results in a palindrome. + isSkipLeftOk = isPalindrome(left + 1, right) + + # Characters didn't match; check if deleting/skipping the right character results in a palindrome. + isSkipRightOk = isPalindrome(left, right - 1) + + # If deleting either was okay, we still have a "valid" palindrome. + return isSkipLeftOk or isSkipRightOk + + # We didn't skip any characters, so it is a palindrome. + return True diff --git a/src/leetcode/two_pointers/valid_triangle_number.py b/src/leetcode/two_pointers/valid_triangle_number.py new file mode 100644 index 0000000..c5bfa23 --- /dev/null +++ b/src/leetcode/two_pointers/valid_triangle_number.py @@ -0,0 +1,68 @@ +# DIFFICULTY: MEDIUM +# ------------------ +# +# Given an integer array nums, return the number of triplets chosen from the array that can make triangles if we take +# them as side lengths of a triangle. +# +# See https://leetcode.com/problems/valid-triangle-number +class Solution: + def triangleNumber(self, nums: list[int]) -> int: + """ + SOLUTION + -------- + + To form a triangle, 3 numbers have to satisfy the inequalities: + + a + b > c + a + c > b + b + c > a + + The most straightforward approach is to sort the numbers and use the two pointer technique to find triples. + + COMPLEXITY + ---------- + + Time complexity is O(n^2) where n is the number of elements in the array. + + Space complexity is O(1). + """ + if len(nums) <= 2: + return 0 + + # Once all elements are sorted, we know that if i < j < k, then nums[i] <= nums[j] <= nums[k]. Let a = nums[i], + # b = nums[j], and c = nums[k]. We know that a <= b <= c. + # + # If we find that a + b > c, then we have found a triplet. This is because: + # + # a + c > b, since c > b. + # b + c > a, since c > a. + # + # Once we find a triplet, we know that all elements between the left and right pointer are valid. + nums.sort() + + result = 0 + for i in range(len(nums) - 1, -1, -1): + # Loop backwards to find our c, which we'll check if it is greater than a + b. + c = nums[i] + + # Start at opposite ends of the array, [a, ...., b, c], and let a and b close in on each other to find a + # valid triplet. Starting this way lets us adjust a and b in the right directions to account for c. + left = 0 + right = i - 1 + while left < right: + a = nums[left] + b = nums[right] + + # We have found a triplet; all elements between left and right can form a triplet. + if a + b > c: + result += right - left + + # Since c is large compared to a + b, advance the right pointer and make c smaller to find more + # triplets. + right -= 1 + else: + # Since c is small compared to a + b, advance the left pointer and make c bigger to find more + # triplets. + left += 1 + + return result diff --git a/src/linked-list/README.md b/src/linked-list/README.md deleted file mode 100644 index 0fb5aa2..0000000 --- a/src/linked-list/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Linked List - -It's usually easy to identify a linked list problem. But there are some data structure combos that appear with common phrases. - -## Common Phrases (HashMap) - -These phrases indicate a hashmap might be useful: - -- 'cache' -- 'LRU', 'MRU' -- 'deep copy with pointers' -- 'random pointers' -- 'detect cycles' -- 'check duplicates' diff --git a/src/linked-list/add-two-numbers.ts b/src/linked-list/add-two-numbers.ts deleted file mode 100644 index 938a63c..0000000 --- a/src/linked-list/add-two-numbers.ts +++ /dev/null @@ -1,42 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse -// order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list. -// -// You may assume the two numbers do not contain any leading zero, except the number 0 itself. -// -// See {@link https://leetcode.com/problems/add-two-numbers/} -import { ListNode } from './common/list-node'; -export { addTwoNumbers }; - -// SOLUTION: -// -// A naive solution of converting the nodes to numbers, adding them, and them, and then reconstructing the linked list -// does work, but is a bunch more code. -// -// Since the lists are stored in reverse order, you can add the two head nodes, preserving a carry, and create a new -// node, in the same way you would do elementary school addition. -function addTwoNumbers(x: ListNode | null, y: ListNode | null): ListNode | null { - function add(u: ListNode | null, v: ListNode | null, carry: number) { - if (u === null && v === null) { - return carry > 0 ? new ListNode(1) : null; - } - - // Compute the sum and carry for the current node. - const a = u?.val ?? 0; - const b = v?.val ?? 0; - const sum = a + b + carry; - const val = sum < 10 ? sum : sum - 10; - const node = new ListNode(); - - // Compute the sum for the next node and attach it to this one. - const uNext = u?.next ?? null; - const vNext = v?.next ?? null; - const carryNext = sum < 10 ? 0 : 1; - node.val = val; - node.next = add(uNext, vNext, carryNext); - return node; - } - - return add(x, y, 0); -} diff --git a/src/linked-list/all-one.ts b/src/linked-list/all-one.ts deleted file mode 100644 index accedd6..0000000 --- a/src/linked-list/all-one.ts +++ /dev/null @@ -1,194 +0,0 @@ -// DIFFICULTY: HARD -// -// Design a data structure to store the strings' count with the ability to return the strings with minimum and maximum -// counts. -// -// Implement the AllOne class: -// -// - AllOne() Initializes the object of the data structure. -// - inc(String key) Increments the count of the string key by 1. If key does not exist in the data structure, insert -// it with count 1. -// - dec(String key) Decrements the count of the string key by 1. If the count of key is 0 after the decrement, remove -// it from the data structure. It is guaranteed that key exists in the data structure before the decrement. -// - getMaxKey() Returns one of the keys with the maximal count. If no element exists, return an empty string "". -// - getMinKey() Returns one of the keys with the minimum count. If no element exists, return an empty string "". -// -// Note that each function must run in O(1) average time complexity. -// -// See {@link https://leetcode.com/problems/all-oone-data-structure/} -export { AllOne }; - -// SOLUTION: -// -// For some reason, LeetCode does not permit the names TreeNode or ListNode; these interfaces are -// included for you somewhere... -interface OneNode { - count: number; - keys: Set; - previous: OneNode; - next: OneNode; -} - -class AllOne { - private readonly nodes: Map; - - private head: OneNode; - - private tail: OneNode; - - constructor() { - this.nodes = new Map(); - - // It's not necessary to have these sentinel values, but it makes things simpler in that you don't have to worry - // about undefined at all. The head and tail will refer to head and tail of the list, while min and max will - // refer to nodes that would've otherwise been the min and max values. - const head: Partial = { - count: Number.MIN_SAFE_INTEGER, - keys: new Set(), - previous: undefined, - next: undefined - }; - const tail: Partial = { - count: Number.MAX_SAFE_INTEGER, - keys: new Set(), - previous: undefined, - next: undefined - }; - - head.next = tail as OneNode; - tail.previous = head as OneNode; - - this.head = head as OneNode; - this.tail = tail as OneNode; - } - - inc(key: string): void { - // Node is present; move key from our node to next one. - if (this.nodes.has(key)) { - const node = this.nodes.get(key)!; - // Next node count === count + 1; just add to the next node and update. Note that head/tail have min/max values - // so this logic encompasses it. - if (node.next.count === node.count + 1) { - node.keys.delete(key); - node.next.keys.add(key); - this.nodes.set(key, node.next); - this.cleanup(node); - return; - } - - // Next node count < count + 1; add a new node. - const next = this.insertKeyBetween(node, key, node.next); - next.count = node.count + 1; - - // Delete the key from current node and possibly unlink the node. - node.keys.delete(key); - this.cleanup(node); - return; - } - - // No nodes at all; inserting new node as min/max node. - if (this.head.next === this.tail) { - this.insertKeyBetween(this.head, key, this.tail); - return; - } - - // At least one node is present, and node has count === 1 as well; update that node. - if (this.head.next.count === 1) { - const node = this.head.next; - node.keys.add(key); - this.nodes.set(key, node); - return; - } - - // At least one node is present, and node has count > 1, insert before that node. - this.insertKeyBetween(this.head, key, this.head.next); - } - - dec(key: string): void { - // Problem says this is guaranteed to be present. - const node = this.nodes.get(key)!; - - // Did we decrement to zero? Just delete the key from the node altogether. - if (node.count === 1) { - node.keys.delete(key); - this.nodes.delete(key); - this.cleanup(node); - return; - } - - // The previous node has count === count - 1, just move the key from our node to the previous one. - if (node.previous.count === node.count - 1) { - const { previous } = node; - previous.keys.add(key); - this.nodes.set(key, previous); - - node.keys.delete(key); - this.cleanup(node); - return; - } - - // The previous node has count too low; we need to make a new node for it. - const previous = this.insertKeyBetween(node.previous, key, node); - previous.count = node.count - 1; - - // Delete the key and cleanup the node. - node.keys.delete(key); - this.cleanup(node); - } - - getMaxKey(): string { - if (this.head.next === this.tail) { - return ''; - } - - return this.tail.previous.keys.values().next().value!; - } - - getMinKey(): string { - if (this.head.next === this.tail) { - return ''; - } - - return this.head.next.keys.values().next().value!; - } - - private insertKeyBetween(previous: OneNode, key: string, next: OneNode): OneNode { - const node: OneNode = { - count: 1, - keys: new Set(), - previous, - next - }; - - node.keys.add(key); - this.nodes.set(key, node); - - this.insertNodeBetween(previous, node, next); - return node; - } - - private insertNodeBetween(previous: OneNode, node: OneNode, next: OneNode) { - node.previous = previous; - node.next = next; - - previous.next = node; - - next.previous = node; - } - - private cleanup(node: OneNode) { - const { previous } = node; - const { next } = node; - - if (node === this.head || node === this.tail) { - return; - } - - if (node.keys.size > 0) { - return; - } - - previous.next = next; - next.previous = previous; - } -} diff --git a/src/linked-list/common/list-node.ts b/src/linked-list/common/list-node.ts deleted file mode 100644 index c487bd7..0000000 --- a/src/linked-list/common/list-node.ts +++ /dev/null @@ -1,53 +0,0 @@ -// This class definition comes from the problem itself and we cannot change it, or else our submission will not be -// accepted. -export class ListNode { - val: number; - - next: ListNode | null; - - constructor(val?: number, next?: ListNode | null) { - this.val = val === undefined ? 0 : val; - this.next = next === undefined ? null : next; - } -} - -export function list2array(node: ListNode | null): number[] { - if (node === null) { - return []; - } - - const xs: number[] = []; - let current: ListNode | null = node; - while (current !== null) { - xs.push(current.val); - current = current.next; - } - return xs; -} - -export function array2list(xs: number[] | null): ListNode | null { - if (xs === null) { - return null; - } - - let root: ListNode | null = null; - let current: ListNode | null = null; - for (let i = 0; i < xs.length; i++) { - if (current === null) { - current = { - val: xs[i], - next: null - }; - root = current; - continue; - } - - current.next = { - val: xs[i], - next: null - }; - current = current.next; - } - - return root; -} diff --git a/src/linked-list/common/nested-integer.ts b/src/linked-list/common/nested-integer.ts deleted file mode 100644 index 64dd994..0000000 --- a/src/linked-list/common/nested-integer.ts +++ /dev/null @@ -1,42 +0,0 @@ -// This class definition comes from the problem itself and we cannot change it, or else our submission will not be -// accepted. -export class NestedInteger { - private value: number | null; - - private values: NestedInteger[]; - - // If value is provided, then it holds a single integer - // Otherwise it holds an empty nested list - constructor(value?: number) { - this.value = value ?? null; - this.values = []; - } - - // Return true if this NestedInteger holds a single integer, rather than a nested list. - isInteger(): boolean { - return this.value !== null; - } - - // Return the single integer that this NestedInteger holds, if it holds a single integer - // Return null if this NestedInteger holds a nested list - getInteger(): number | null { - return this.value; - } - - // Set this NestedInteger to hold a single integer equal to value. - setInteger(value: number) { - this.value = value; - } - - // Set this NestedInteger to hold a nested list and adds a nested integer elem to it. - add(elem: NestedInteger) { - this.value = null; - this.values.push(elem); - } - - // Return the nested list that this NestedInteger holds, - // or an empty list if this NestedInteger holds a single integer - getList(): NestedInteger[] { - return this.values; - } -} diff --git a/src/linked-list/common/random-node.ts b/src/linked-list/common/random-node.ts deleted file mode 100644 index 1afd4b8..0000000 --- a/src/linked-list/common/random-node.ts +++ /dev/null @@ -1,13 +0,0 @@ -// This class definition comes from the problem itself and we cannot change it, or else our submission will not be -// accepted. -export class _Node { - val: number; - next: _Node | null; - random: _Node | null; - - constructor(val?: number, next?: _Node, random?: _Node) { - this.val = val === undefined ? 0 : val; - this.next = next === undefined ? null : next; - this.random = random === undefined ? null : random; - } -} diff --git a/src/linked-list/copy-list-with-random-pointer.ts b/src/linked-list/copy-list-with-random-pointer.ts deleted file mode 100644 index 9212cd6..0000000 --- a/src/linked-list/copy-list-with-random-pointer.ts +++ /dev/null @@ -1,62 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// A linked list of length n is given such that each node contains an additional random pointer, which could point to -// any node in the list, or null. -// -// Construct a deep copy of the list. The deep copy should consist of exactly n brand new nodes, where each new node has -// its value set to the value of its corresponding original node. Both the next and random pointer of the new nodes -// should point to new nodes in the copied list such that the pointers in the original list and copied list represent -// the same list state. None of the pointers in the new list should point to nodes in the original list. -// -// For example, if there are two nodes X and Y in the original list, where X.random --> Y, then for the corresponding -// two nodes x and y in the copied list, x.random --> y. -// -// Return the head of the copied linked list. -// -// The linked list is represented in the input/output as a list of n nodes. Each node is represented as a pair of -// [val, random_index] where: -// -// - val: an integer representing Node.val -// - random_index: the index of the node (range from 0 to n-1) that the random pointer points to, or null if it does not point to any node. -// -// Your code will only be given the head of the original linked list. -// -// See {@link https://leetcode.com/problems/copy-list-with-random-pointer} -import { _Node } from './common/random-node'; -export { copyRandomList }; - -// SOLUTION: -// -// If there's no random pointer you can iterate through the nodes and create a copy of each node. With the random -// pointer you'll have to maintain a map of original node to copied node so you can assign the random pointers later. -// -// First iterate through the list once to make copies of each node in a map. Then iterate again and assign the next -// and random pointers. -function copyRandomList(head: _Node | null): _Node | null { - if (head === null) { - return null; - } - - type Original = _Node; - type Copy = _Node; - const map = new Map(); - - // First create a map of each node to its copy. - let node: _Node | null = head; - while (node !== null) { - map.set(node, new _Node(node.val)); - node = node.next; - } - - // Now assign the random pointers. - node = head; - while (node !== null) { - const copy = map.get(node)!; - copy.next = node.next !== null ? map.get(node.next)! : null; - copy.random = node.random !== null ? map.get(node.random)! : null; - node = node.next; - } - - // Return the head of the list. - return map.get(head)!; -} diff --git a/src/linked-list/lru-cache.ts b/src/linked-list/lru-cache.ts deleted file mode 100644 index c858d40..0000000 --- a/src/linked-list/lru-cache.ts +++ /dev/null @@ -1,117 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Design a data structure that follows the constraints of a Least Recently Used (LRU) cache. -// -// Implement the LRUCache class: -// -// LRUCache(int capacity) -// Initialize the LRU cache with positive size capacity. -// int get(int key) -// Return the value of the key if the key exists, otherwise return -1. -// void put(int key, int value) -// Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of -// keys exceeds the capacity from this operation, evict the least recently used key. -// -// The functions get and put must each run in O(1) average time complexity. -// -// See {@link https://leetcode.com/problems/lru-cache/} -export { LRUCache }; - -// SOLUTION: -// -// This class definition is provided by LeetCode and must be implemented by you. An LRU cache can be implemented -// using a hashmap and a linked list. -class LRUCache { - private map: Map; - private capacity: number; - private head: Node; - private tail: Node; - - constructor(capacity: number) { - this.map = new Map(); - this.capacity = capacity; - - // These are sentinel nodes that will never be directly accessed, but help us avoid certain undefined checks. - this.head = new Node(-1, -1); - this.tail = new Node(-1, -1); - this.head.next = this.tail; - this.tail.previous = this.head; - } - - get(key: number): number { - if (!this.map.has(key)) { - return -1; - } - - // Here, we can be assured that the key exists because we also store the key on the node. If we want to avoid - // storing the key on the node, we can just delete the last element from the list and then check for an undefined - // value here in case we deleted the key from being at capacity. - const node = this.map.get(key)!; - this.moveToHead(node); - return node.value; - } - - put(key: number, value: number): void { - // If we already have this value in the map, just update it and don't bother mucking with the capacity. - if (this.map.has(key)) { - const node = this.map.get(key)!; - this.moveToHead(node); - node.value = value; - return; - } - - const node = new Node(key, value); - this.map.set(key, node); - this.addToHead(node); - - // If we've exceed capacity, we have to remove the least used element, aka the tail. At this point, there is - // guaranteed to be at least one other element in the list because we just added one. Also, unless the capacity - // is 0, we will always at least have another. - // - // So we can safely access this.tail.previous! - if (this.map.size > this.capacity) { - const tail = this.tail.previous!; - this.removeNode(tail); - this.map.delete(tail.key); - } - } - - private addToHead(node: Node): void { - const a = this.head; - const b = node; - const c = this.head.next; - - // Updates the pointers for the node itself. - b.next = c; - b.previous = a; - - // Inserts the node at the front of the list. - a.next = b; - c!.previous = b; - } - - private moveToHead(node: Node): void { - this.removeNode(node); - this.addToHead(node); - } - - private removeNode(node: Node): void { - const b = node; - const a = b.previous!; - const c = b.next!; - - a.next = c; - c.previous = a; - } -} - -class Node { - public next?: Node; - - public previous?: Node; - - constructor( - public readonly key: number, - public value: number - ) {} -} diff --git a/src/linked-list/merge-two-sorted-lists.ts b/src/linked-list/merge-two-sorted-lists.ts deleted file mode 100644 index 648f5b5..0000000 --- a/src/linked-list/merge-two-sorted-lists.ts +++ /dev/null @@ -1,55 +0,0 @@ -// DIFFICULTY: EASY -// -// You are given the heads of two sorted linked lists list1 and list2. -// -// Merge the two lists into one sorted list. The list should be made by splicing together the nodes of the first two lists. -// -// Return the head of the merged linked list. -// -// See {@link https://leetcode.com/problems/merge-two-sorted-lists/} -import { ListNode } from './common/list-node'; -export { mergeTwoLists }; - -// SOLUTION: -// -// A straightforward solution works. -function mergeTwoLists(list1: ListNode | null, list2: ListNode | null): ListNode | null { - let head: ListNode | null = null; - let tail: ListNode | null = null; - let left = list1; - let right = list2; - - while (left !== null || right !== null) { - let node = null; - - if (left === null && right !== null) { - node = right; - right = right.next; - } - - if (left !== null && right === null) { - node = left; - left = left.next; - } - - if (left !== null && right !== null) { - if (left.val <= right.val) { - node = left; - left = left.next; - } else { - node = right; - right = right.next; - } - } - - if (tail === null) { - tail = node; - head = node; - } else { - tail.next = node; - tail = tail.next; - } - } - - return head; -} diff --git a/src/linked-list/remove-nth-node-from-end-of-list.ts b/src/linked-list/remove-nth-node-from-end-of-list.ts deleted file mode 100644 index 3e364f0..0000000 --- a/src/linked-list/remove-nth-node-from-end-of-list.ts +++ /dev/null @@ -1,58 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given the head of a linked list, remove the nth node from the end of the list and return its head. -// -// @see {@link https://leetcode.com/problems/remove-nth-node-from-end-of-list/} -import { ListNode } from './common/list-node'; -export { removeNthFromEnd }; - -// SOLUTION: -// -// To do this in one iteration, throw all the nodes onto a list. Then we can easily find the nth node from the end of -// the list. After that, we just have to point the (k - 1)th node to the (k + 1)th node. Assuming both exist. -// -// You can also do this using two pointers, but this solution is a more straightforward. -// -// COMPLEXITY: -// -// Runs in O(n) time because we are iterating through the list once. Runs in O(n) space because we are storing the list -// in an array. -function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null { - // Just convert the linked list to an array so we can easily find the nth node from the end. - const list: ListNode[] = []; - let node: ListNode | null = head; - while (node !== null) { - list.push(node); - node = node.next; - } - - // Now make sure this operation can even be done. If n is 0 or greater than the length of the list, then we can't - // then we can't remove anything! - if (head === null || n <= 0 || n > list.length) { - return head; - } - - // Create sentinel nodes to make the logic easier, and so we can return the head of the list later. - const sentinel = new ListNode(); - sentinel.next = head; - - // Now that we know n is within bounds, let's find the node we want to remove. For example, if we want to remove the - // 2nd node from the end of the list with 3 elements, then the index is 1 (3 - 2 = 1). - // - // After this we just have point the (k - 1)th node to the (k + 1)th node. Assuming both exist. - const k = list.length - n; - - // The previous node to remove is the a = (k - 1)th node. If it turns out the (k - 1)th node doesn't exist, then node - // a is the sentinel node we created. - const a = list[k - 1] ?? sentinel; - - // The next node to link it to is the c = (k + 1)th node. If it turns out that the (k + 1)th node doesn't exist, then - // node c is null. - const c = list[k + 1] ?? null; - - // Unlink the node b in position list[k] by setting the a node's next pointer to c. - a.next = c; - - // Return the list minus the sentinel. - return sentinel.next; -} diff --git a/src/math/number-of-one-bits.ts b/src/math/number-of-one-bits.ts deleted file mode 100644 index f791db4..0000000 --- a/src/math/number-of-one-bits.ts +++ /dev/null @@ -1,31 +0,0 @@ -// DIFFICULTY: EASY -// -// Write a function that takes the binary representation of a positive integer and returns the number of -// set bits it has (also known as the Hamming weight). -// -// See {@link https://leetcode.com/problems/number-of-1-bits/} -export { hammingWeight }; - -// SOLUTION: -// -// Note that the problem here says it "takes the binary representation" of a positive integer. However, the signature -// of the solution takes a number argument, not a string. -// -// This solution takes a number, converts it to a string, then does the calculation. But it's likely the interviewer -// will be asking for bit manipulation. -function hammingWeight(n: number) { - let count = 0; - - while (n !== 0) { - // This will be true if the least significant bit (the one all the way on the right) is 1. - if (n & 1) { - count++; - } - - // Do an unsigned shift to the right (so that the most sigificant bit becomes 0), and so that the bit we just - // counted (the least significant bit) gets shifted off. - n >>>= 1; - } - - return count; -} diff --git a/src/math/pow-x-n.ts b/src/math/pow-x-n.ts deleted file mode 100644 index 72b5865..0000000 --- a/src/math/pow-x-n.ts +++ /dev/null @@ -1,69 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Implement pow(x, n), which calculates x raised to the power n (i.e., xn). -// -// See {@link https://leetcode.com/problems/powx-n/} -export { myPow }; - -// SOLUTION: -// -// The faster solution is based on the concept of exponentiation by squaring. Basically, we can reduce the number of -// multiplications if we notice that: -// -// x^n = (x^2)^(n/2) if x n is even -// x^n = x * (x^(n-1)/2) if n is odd -// -// Exponentiation by squaring is faster because the naive method requires n multiplications, but squaring each -// time only requires log(n) multiplications. To implement this: -// -// 1. Check if n is negative. If it is, then we can just invert x and make n positive. -// 2. Check if n === 0. If it is, then we can just return 1. -// 3. Check if n is even. If it is, then we can return myPow(x * x, n / 2). That is, square the number and half the -// exponent. -// 4. Check if n is odd. If it is, we can do the same thing, except just return x * myPow(x * x, (n - 1) / 2). That -// is, we half the exponent minus 1 to make it even (so we can do our squaring), then multiply the result by x. -// -// This can be done even faster if we use bit shift operations and convert the recursive solution to an iterative one. -// -// COMPLEXITY: -// -// Time complexity is O(log n) because we are halving the exponent each time. -// -// Space complexity is O(1) because we are not using any extra space. -function myPow(x: number, n: number) { - if (n === 0) { - return 1; - } - - let xi = x; - let ni = n; - - // Check if n is negative. If it is, then we can just invert x and make n positive, then continue as normal. - if (ni < 0) { - xi = 1 / x; - ni = -n; - } - - let result = 1; - while (ni > 0) { - // Rather than use ni % 2 to check if a number is even, use bitwise and. If the result is 1, then the least - // significant bit is a 1. Otherwise, the bit is 0. - const isEven = (ni & 1) === 0; - if (isEven) { - xi *= xi; - // Rather than divide by 2, we can use the right bit shift operator to do a faster division. The `>>>` - // operation is an unsigned bit shift to the right. This means the leftmost bits are filled with 0's regardless - // of the sign of the original number. - ni >>>= 1; - } else { - result *= xi; - xi *= xi; - // Rather than divide by 2, we can use the right bit shift operator to do a faster division. The `>>>` - // operation is an unsigned bit shift to the right. This means the leftmost bits are filled with 0's regardless - // of the sign of the original number. - ni >>>= 1; - } - } - - return result; -} diff --git a/src/math/reverse-integer.ts b/src/math/reverse-integer.ts deleted file mode 100644 index 5536611..0000000 --- a/src/math/reverse-integer.ts +++ /dev/null @@ -1,47 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a signed 32-bit integer x, return x with its digits reversed. If reversing x causes the value to go outside the -// signed 32-bit integer range [-231, 231 - 1], then return 0. -// -// Assume the environment does not allow you to store 64-bit integers (signed or unsigned). -// -// See {@link https://leetcode.com/problems/reverse-integer/} -export { reverse }; - -// SOLUTION: -// -// You can do this by reversing a string, but you can also just do this with arithmetic. -function reverse(x: number) { - // There is no builtin const for this, but you can calculate the min 32 bit value like this (31 bits due to sign). - const min = -1 * 2 ** 31; - // There is no builtin const for this, but you can calculate the min 32 bit value like this (31 bits due to sign). - // Minus 1 to account for 0. - const max = 2 ** 31 - 1; - const sign = x > 0 ? 1 : -1; - - let n = Math.abs(x); - let r = 0; - while (n > 0) { - // Get the least significant digit of N; this will be the most significant digit of R in the first iteration, - // the second most in the next iteration, and so on. - const lastDigit = n % 10; - - if (r === 0) { - // When r === 0, just make last digit the most significant digit. - r = lastDigit; - } else { - // Otherwise shift R to the left by 10, then add the last digit we got from N. - r = r * 10 + lastDigit; - } - - // Shift N to the right by 10 so we drop the last digit, then go to the next iteration. - n = Math.floor(n / 10); - } - - const value = sign * r; - if (value < min || value > max) { - return 0; - } - - return sign * r; -} diff --git a/src/math/rotate-image.ts b/src/math/rotate-image.ts deleted file mode 100644 index d69462f..0000000 --- a/src/math/rotate-image.ts +++ /dev/null @@ -1,69 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an n x n 2D matrix representing an image, rotate the image by 90 degrees (clockwise). -// -// You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate -// another 2D matrix and do the rotation. -// -// See {@link https://leetcode.com/problems/rotate-image/} -export { rotate }; - -// SOLUTION: -// -// To rotate an image by 90 degrees, we can transpose the matrix (exchange [i, j] with [j, i]), then reverse each -// row. -// -// Generally you can accomplish any number of different rotations by transposition and reversing. -// -// 90 degrees clockwise: transpose, then reverse rows. -// 90 degrees counter-clockwise: transpose, then reverse columns. -// 180 degrees: reverse rows, reverse columns. -// 270 degrees clockwise: same as 90 degrees counter-clockwise. -// 270 degrees counter-clockwise: same as 90 degrees clockwise. -// -// The above approach requires a transposition then reversing each element. It is O(n^2) for the transpose operation -// but then O(n^2) again for reversals. A more complicated algorithm can achieve the transposition in a single pass -// through the matrix. -// -// For a 4 element matrix: -// -// - The element at (0, 0) moves to (0, 3). -// - The element at (0, 3) moves to (3, 3). -// - The element at (3, 3) moves to (3, 0). -// - The element at (3, 0) moves to (0, 0). -// -// This pattern applies for the outer layer of the square matrix, then the next layer below that, and so on. Here -// a "layer" means an square (so the outer square), then the next inner square and so on. -function rotate(matrix: number[][]): void { - // We'll have to apply our algorithm to each "layer" of our matrix. For a matrix of size N, there are N/2 layers. - const n = matrix.length; - const layers = Math.floor(n / 2); - - // Operate our algorithm for each layer; again, an NxN matrix will have N/2 layers. - for (let i = 0; i < layers; i++) { - // By setting j = i, we will descend into an inner layer and process them one by one. That is, the first square - // starts at (0, 0), then the next square starts at (1, 1), and so on. - for (let j = i; j < n - i - 1; j++) { - // Lets define our 4 points so we can get our cells correctly. - const left = i; - const right = n - i - 1; - const top = j; - const bottom = n - j - 1; - - // Temporarily save the top left cell so we can swap it later. - const t = matrix[left][top]; - - // The (left row, top column) cell should get the (bottom row, left column) value. - matrix[left][top] = matrix[bottom][left]; - - // The (bottom row, left column) cell should get the (right row, bottom column) value. - matrix[bottom][left] = matrix[right][bottom]; - - // The (right row, bottom column) cell should get the (top row, right column) value. - matrix[right][bottom] = matrix[top][right]; - - // The (top row, right column) cell should get the (top row, left column) value (which we saved). - matrix[top][right] = t; - } - } -} diff --git a/src/math/sequential-digits.ts b/src/math/sequential-digits.ts deleted file mode 100644 index d0c3073..0000000 --- a/src/math/sequential-digits.ts +++ /dev/null @@ -1,48 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// An integer has sequential digits if and only if each digit in the number is one more than the previous digit. -// -// Return a sorted list of all the integers in the range [low, high] inclusive that have sequential digits. -// -// See {@link https://leetcode.com/problems/sequential-digits/} -export { sequentialDigits }; - -// SOLUTION: -// -// Rather than take a list from low to high and filtering out the numbers that match our criteria, it will be much -// faster to generate a list of numbers with this quality with the correct number of digits, then filter out by low -// and high ranges. -function sequentialDigits(low: number, high: number) { - const results: number[] = []; - - // Sequential digits end at 9, so the largest sequential number you can have is 123456789. Start generating them - // at i = 1. We'll handle 0 in a special case. - for (let i = 1; i < 9; i++) { - // Set the value to be xxxi, and the next digit following xxxi to be i + 1. We will append i + 1 to the end of - // this number. - let value = i; - let next = i + 1; - - while (next <= 9 && value <= high) { - // Shift the current number's digits over to the left, leaving a 0 in the one's place. - value *= 10; - - // Add the next digit to the one's place. - value += next; - next++; - - // If it's within bounds, add it to the array. - if (value >= low && value <= high) { - results.push(value); - } - } - } - - // The above logic doesn't handle the sequential digit number 0; so add it if necessary. - if (low === 0) { - results.push(0); - } - - results.sort((a, b) => a - b); - return results; -} diff --git a/src/matrix/robot-bounded-in-circle.ts b/src/matrix/robot-bounded-in-circle.ts deleted file mode 100644 index ab0599c..0000000 --- a/src/matrix/robot-bounded-in-circle.ts +++ /dev/null @@ -1,104 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// On an infinite plane, a robot initially stands at (0, 0) and faces north. Note that: -// -// - The north direction is the positive direction of the y-axis. -// - The south direction is the negative direction of the y-axis. -// - The east direction is the positive direction of the x-axis. -// - The west direction is the negative direction of the x-axis. -// -// The robot can receive one of three instructions: -// -// - "G": go straight 1 unit. -// - "L": turn 90 degrees to the left (i.e., anti-clockwise direction). -// - "R": turn 90 degrees to the right (i.e., clockwise direction). -// - The robot performs the instructions given in order, and repeats them forever. -// -// Return true if and only if there exists a circle in the plane such that the robot never leaves the circle. -// -// See {@link https://leetcode.com/problems/robot-bounded-in-circle/} -export { isRobotBounded }; - -// SOLUTION: -// -// The problem is worded a little confusingly, but the gist is that if you can draw any circle around the robot's path -// then return true. Otherwise, return false. -// -// Okay, so there are a few possibilities that can be put into two categories: -// -// 1. It follows the path and returns to the starting position. -// 2. It follows the path and does not return to the starting position. -// -// In the first case, regardless of what direction the robot is facing, it will follow the same path and return to the -// starting position again and again, so we can bound it with a circle. -// -// In the second case, it is *possible* that even if it's not in the starting position, it can follow a path that is -// bounded by a circle. Let's explore the possibilities. -// -// - The direction the robot is facing is the same as the initial direction. In this case, the robot will continue to -// move in that same direction forever, making the path unbounded. -// - The direction the robot is facing is different from the initial direction. In this case, the robot will follow -// along a path that can be bounded by a circle. -// -// The second condition is true because the new direction is the new "north" for the robot, and it will eventually -// return to the origin following that path for 4 iterations. -function isRobotBounded(instructions: string): boolean { - const directions = new Map([ - ['north', { x: 0, y: 1 }], - ['east', { x: 1, y: 0 }], - ['west', { x: -1, y: 0 }], - ['south', { x: 0, y: -1 }] - ]); - - // Simulate the robot's path. - let [x, y] = [0, 0]; - let facing = 'north'; - for (const instruction of instructions) { - if (instruction === 'G') { - x += directions.get(facing)!.x; - y += directions.get(facing)!.y; - } else if (instruction === 'L') { - facing = turnLeft(facing); - } else if (instruction === 'R') { - facing = turnRight(facing); - } - } - - // If the robot returned to the origin, then we are good to go. - if (x === 0 && y === 0) { - return true; - } - - // If it didn't return to the origin, but we aren't facing the same direction as we started, then we are good to go. - return facing !== 'north'; -} - -function turnLeft(facing: string) { - switch (facing) { - case 'north': - return 'west'; - case 'east': - return 'north'; - case 'west': - return 'south'; - case 'south': - return 'east'; - default: - throw new Error(); - } -} - -function turnRight(facing: string) { - switch (facing) { - case 'north': - return 'east'; - case 'east': - return 'south'; - case 'west': - return 'north'; - case 'south': - return 'west'; - default: - throw new Error(); - } -} diff --git a/src/matrix/valid-word-square.ts b/src/matrix/valid-word-square.ts deleted file mode 100644 index 190455a..0000000 --- a/src/matrix/valid-word-square.ts +++ /dev/null @@ -1,24 +0,0 @@ -// Given an array of strings words, return true if it forms a valid word square. -// -// A sequence of strings forms a valid word square if the kth row and column read the same string, where -// 0 <= k < max(numRows, numColumns). -// -// See {@link https://leetcode.com/problems/valid-word-square/} -export { validWordSquare }; - -// SOLUTION: -function validWordSquare(words: string[]): boolean { - for (let i = 0; i < words.length; i++) { - for (let j = 0; j < words[i].length; j++) { - try { - if (words[i][j] !== words[j][i]) { - return false; - } - } catch { - return false; - } - } - } - - return true; -} diff --git a/src/prefix-sum/README.md b/src/prefix-sum/README.md deleted file mode 100644 index f87c647..0000000 --- a/src/prefix-sum/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Prefix Sum - -## Common Phrases - -These phrases indicate a prefix sum might be useful: - -- 'range sum query' -- 'sum from index to index' -- 'difference between sums' -- 'rolling sum' -- 'running total' diff --git a/src/prefix-sum/continuous-subarray-sum.ts b/src/prefix-sum/continuous-subarray-sum.ts deleted file mode 100644 index e5916a0..0000000 --- a/src/prefix-sum/continuous-subarray-sum.ts +++ /dev/null @@ -1,95 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums and an integer k, return true if nums has a good subarray or false otherwise. -// -// A good subarray is a subarray where: -// -// - its length is at least two, and -// - the sum of the elements of the subarray is a multiple of k. -// -// Note that: -// -// - A subarray is a contiguous part of the array. -// - An integer x is a multiple of k if there exists an integer n such that x = n * k. 0 is always a multiple of k. -// -// See {@link https://leetcode.com/problems/continuous-subarray-sum/} -export { checkSubarraySum }; - -// SOLUTION: -// -// The brute force solution is to calculate the sum of all subarrays of length 2 or more and check if it's a multiple of -// of k. To do so, compute the prefix sum of the array and use it to calculate the sum of the subarray from [i, j]. -// -// A subarray sum from [i, j] can be calculated as prefixSum[j] - prefixSum[i] + nums[i]. We can check each subarray -// sum to see if it's a multiple of k and return true if we find one. It is; however, O(n^2) to calculate subarray -// sums in this way. Unfortunately, this will exceed the runtime limit for large arrays in LeetCode. -// -// To get a better solution we have to note that: -// -// sum[i, j] = prefixSum[j] - prefixSum[i] + nums[i] -// sum[i, j] = prefixSum[j] - prefixSum[i - 1] -// -// This is because the sum[i, j] is inclusive, and subtracting prefixSum[i] subtracts out nums[i]. That's why we either -// have to add it back in or use i - 1 as the index instead. Afterwards, we can look at the sum modulo k: -// -// sum[i, j] (mod k) = (prefixSum[j] - prefixSum[i - 1]) (mod k) -// -// If we want sum[i, j] % k === 0, then we should write: -// -// (prefixSum[j] - prefixSum[i - 1]) (mod k) = 0 -// prefixSum[j] (mod k) = prefixSum[i - 1] (mod k) -// -// In other words, if prefixSums at positions (i - 1) and j have the same remainder modulo k, then the subarray sum from -// [i, j] has remainder 0 modulo k. -// -// COMPLEXITY: -// -// Time complexity is O(n^2) because we are doing an outer and inner loop on the prefix sum array. -// -// Space complexity is O(n) because we are storing the prefix sum array. -function checkSubarraySum(nums: number[], k: number): boolean { - // Use a map of remainders to positions to store the prefix sum remainders, so we can check later if we have seen any - // remainders that are the same modulo k. - type Remainder = number; - type Index = number; - const remainders = new Map(); - - // Maintain a running prefix sum as we loop through the array. - let prefixSum = 0; - for (let i = 0; i < nums.length; i++) { - prefixSum += nums[i]; - let remainder = prefixSum % k; - - // It might be the case that the remainder is negative, just as a quirk of how JavaScript handles the modulo - // operator. - // - // For example, -5 % 3 = -2. But this is wrong because -2 is not congruent to -5 modulo 3. Instead: - // - // -5 (mod 3) = -5 + 3 (mod 3) = -2 (mod 3) = -2 + 3 (mod 3) = 1 (mod 3) - // - // That is, we want -5 % 3 = 1 instead. To fix this, we half to add the modulus to the remainder if it's negative. - if (remainder < 0) { - remainder += k; - } - - // It's possible that the prefix sum already is a multiple of k. And it's possible that the remainder 0 isn't in - // the remainders map yet. In this case, just check if we have a subarray of length 2 or more. - if (remainder === 0 && i >= 1) { - return true; - } - - // If this is a previously seen remainder, we can check if we have a "good" subarray. - if (remainders.has(remainder)) { - const j = remainders.get(remainder)!; - if (Math.abs(i - j) >= 2) { - return true; - } - } - // If we're seeing this remainder for the first time, store it in the map. - else { - remainders.set(remainder, i); - } - } - - return false; -} diff --git a/src/prefix-sum/max-sum-obtained-of-any-permutation.ts b/src/prefix-sum/max-sum-obtained-of-any-permutation.ts deleted file mode 100644 index 5ffc575..0000000 --- a/src/prefix-sum/max-sum-obtained-of-any-permutation.ts +++ /dev/null @@ -1,71 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// We have an array of integers, nums, and an array of requests where requests[i] = [starti, endi]. The ith request asks -// for the sum of nums[starti] + nums[starti + 1] + ... + nums[endi - 1] + nums[endi]. Both starti and endi are -// 0-indexed. -// -// Return the maximum total sum of all requests among all permutations of nums. -// -// Since the answer may be too large, return it modulo 10^9 + 7. -// -// See {@link https://leetcode.com/problems/maximum-sum-obtained-of-any-permutation/} -export { maxSumRangeQuery }; - -// SOLUTION: -// -// This question is asking for a very weird thing. -// -// Here, the request ranges don't change; they want to sum the values of the range. However, the nums array can be -// in any order you want. The ask is to find the order of nums that gives you the maximum sum across ALL requests, -// and then return that maximum sum (not the specific order). -// -// This will happen if the most frequently asked for numbers are matched up with the largest numbers. So basically, -// sort the nums in descending order by frequency. -function maxSumRangeQuery(nums: number[], requests: number[][]): number { - // First, calculate for each index i, how many times that index is requested. - const freq: number[] = new Array(nums.length).fill(0); - for (const request of requests) { - const [start, end] = request; - - // This is the difference array technique, usable when we want to efficiently apply multiple operations to a - // single array. Here, the naive way to compute frequency would be to do an inner loop and and increment each - // element between start and end by x (here x = 1). - // - // Instead, use the difference array and prefix sums to compute the actual frequencies. A prefix sum will carry - // the value of x (here x = 1) from start through to the end of the array. However, if we set freq[end+1] = -x, - // then the propagated sum effectively gets "turned off" after adding x to freq[end]. - freq[start]++; - if (end + 1 < nums.length) { - freq[end + 1]--; - } - } - - // Next, we can calculate the actual frequency of each element by taking the prefix sum of the array. This - // propagates sums of values at i through to the end of the array (except for spots where we have strategically - // "turned off" the propagation to make it only apply to a range). - for (let i = 1; i < freq.length; i++) { - freq[i] += freq[i - 1]; - } - - // Now we want to sort the frequencies AND nums in descending order, so we can match up the highest frequency asks - // with the largest number. - // - // Note that this makes us lose track of the indices, but that's okay. Every single element in freq represents the - // number of times some value is asked for. Every request is honored, so every value will be asked for. - // - // On top of that, the question asks for the maximum sum across ALL queries, so it's not even necessary to keep - // track of any individual query. In the end we'll sum everything together anyways. - nums.sort((a, b) => b - a); - freq.sort((a, b) => b - a); - - // Now it's just a matter of summing every single request value by matching it up with the largest number. Remember - // that the totality of freq's request values will cover all range queries. Since we don't care about the max - // for any individual query, we can just add them ALL up. - const MODULUS = 1e9 + 7; - let result = 0; - for (let i = 0; i < nums.length; i++) { - result = (result + nums[i] * freq[i]) % MODULUS; - } - - return result; -} diff --git a/src/prefix-sum/product-of-array-except-self.ts b/src/prefix-sum/product-of-array-except-self.ts deleted file mode 100644 index 1b76e6f..0000000 --- a/src/prefix-sum/product-of-array-except-self.ts +++ /dev/null @@ -1,56 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums, return an array answer such that answer[i] is equal to the product of all the elements -// of nums except nums[i]. -// -// The product of any prefix or suffix of nums is guaranteed to fit in a 32-bit integer. -// -// You must write an algorithm that runs in O(n) time and without using the division operation. -// -// See {@link https://leetcode.com/problems/product-of-array-except-self/} -export { productExceptSelf }; - -// SOLUTION: -// -// If we could use division, we could just compute the product of the array (excluding zeroes), then we can divide the -// non-zero product by the current element if there were no zeroes, return the non-zero product if there were zeroes, -// or just return zero if there were multiple zeroes. -// -// Without using division, we'll have to use more space to compute the product-except-self in each array index. To -// do this, we have to construct two new arrays: -// -// - One array to hold the partial product of all elements before, but not including element i, called `prefix`. -// This array is a "prefix product", analogous to the "prefix sum" computational technique used preprocess an array -// for quick range queries. -// - One array to hold the partial product of all elements after, but not including element i, called `suffix`. -// This array is a "suffix product", analogous to the "suffix sum" computational technique used to preprocess an -// array for quick range queries. -// -// To compute the product-except-self, the ith value will be `befores[i] * afters[i]`. -// -// This isn't exactly a prefix sum problem, but well, close enough. -function productExceptSelf(xs: number[]): number[] { - if (xs.length === 0) { - return []; - } - - // Compute the product up to the ith index. - const prefix = Array(xs.length).fill(1); - for (let i = 1; i < xs.length; i++) { - prefix[i] = prefix[i - 1] * xs[i - 1]; - } - - // Compute the product after the ith index. - const suffix = Array(xs.length).fill(1); - for (let i = xs.length - 2; i >= 0; i--) { - suffix[i] = suffix[i + 1] * xs[i + 1]; - } - - // Compute the product except for the element at the ith index. - const products = Array(xs.length).fill(0); - for (let i = 0; i < xs.length; i++) { - products[i] = prefix[i] * suffix[i]; - } - - return products; -} diff --git a/src/prefix-sum/random-pick-with-weight.ts b/src/prefix-sum/random-pick-with-weight.ts deleted file mode 100644 index 453950d..0000000 --- a/src/prefix-sum/random-pick-with-weight.ts +++ /dev/null @@ -1,85 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given a 0-indexed array of positive integers w where w[i] describes the weight of the ith index. -// -// You need to implement the function pickIndex(), which randomly picks an index in the range [0, w.length - 1] -// (inclusive) and returns it. The probability of picking an index i is w[i] / sum(w). -// -// For example, if w = [1, 3], the probability of picking index 0 is 1 / (1 + 3) = 0.25 (i.e., 25%), and the -// probability of picking index 1 is 3 / (1 + 3) = 0.75 (i.e., 75%). -// -// See {@link https://leetcode.com/problems/random-pick-with-weight/} -export { Solution }; - -// SOLUTION: -// -// The naive way to do this is to take the weights and create a new array that's the size of the sum of the weights. -// For example, if you have the input [1, 3], create an array [0, 1, 1, 1]. Then, generate a random number using -// floor(random() * 4) to get a number between 0 and 3 inclusive. That's the index you should pick. -// -// This will work, but once you have a large number of weights, you'll be creating quite large arrays. Probably not a -// good idea. -// -// Instead, we can compute a prefix sum to avoid expanding an array. In the previous example, you can turn the input -// array [1, 3] into the prefix sum array [1, 4]. The cumulative weights divide the range in this way: -// -// [0, 1) -> Index 0 -// [1, 4) -> Index 1 -// -// So if you generate a random number between [0, 4), you can do a linear scan of the prefix sum array to find the index -// that you should pick. This works because each range's weight increases as you move to the right. -// -// Likewise, if you had the input array [1, 4, 2], the prefix sum array would be [1, 5, 7]. The ranges would be: -// -// [0, 1) -> Index 0 -// [1, 5) -> Index 1 -// [5, 7) -> Index 2 -// -// And again, picking a random number between [0, 7) would allow you to do a linear scan to find the index to pick. -// -// But wait! Instead of doing a linear scan, it's much more efficient to do a binary search, which is what this -// solution does. -// -// COMPLEXITY: -// -// It's O(n) to create the prefix sum array. Then O(n) to perform the linear scan or O(log n) to perform the binary -// search. -// -// The space complexity is O(n) to store the prefix sum array. -class Solution { - private sums: number[]; - private total: number; - - constructor(w: number[]) { - this.sums = []; - this.total = 0; - - for (let i = 0; i < w.length; i++) { - this.total += w[i]; - this.sums[i] = this.total; - } - } - - pickIndex(): number { - // Pick a random number between [0, total). Doesn't have to be an integer at all; it's just a number between 0 and - // the total. - const target = Math.random() * this.total; - - // Use insertion point binary search instead of exact match. We aren't looking for an exact match of the element in - // the array. In fact, it's unlikely to even be in the array. Instead we'll use insertion point binary search - // which is more conceptually appropriate. - let left = 0; - let right = this.sums.length; - while (left < right) { - const mid = Math.floor((left + right) / 2); - if (this.sums[mid] < target) { - left = mid + 1; - } else { - right = mid; - } - } - - // The "insertion point" is the index that corresponds to the random number. - return left; - } -} diff --git a/src/prefix-sum/range-sum-query-immutable.ts b/src/prefix-sum/range-sum-query-immutable.ts deleted file mode 100644 index ab548d0..0000000 --- a/src/prefix-sum/range-sum-query-immutable.ts +++ /dev/null @@ -1,44 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an integer array nums, handle multiple queries of the following type: -// -// Calculate the sum of the elements of nums between indices left and right inclusive where left <= right. -// Implement the NumArray class: -// -// - NumArray(int[] nums) Initializes the object with the integer array nums. -// - int sumRange(int left, int right) Returns the sum of the elements of nums between indices left and right inclusive -// (i.e. nums[left] + nums[left + 1] + ... + nums[right]). -export { NumArray }; - -// SOLUTION: -// -// Implement a prefix sum array for efficient range queries. -class NumArray { - private readonly prefixSum: number[]; - - constructor(nums: number[]) { - this.prefixSum = []; - - for (let i = 0; i < nums.length; i++) { - if (i === 0) { - this.prefixSum[0] = nums[0]; - continue; - } - - this.prefixSum[i] = this.prefixSum[i - 1] + nums[i]; - } - } - - sumRange(left: number, right: number): number { - // This is the sum of the elements from 0 to right, inclusive. - const rvalue = this.prefixSum[right]; - - // We'll want to subtract out the sum of elements from 0 to left, excluding the left element. This means we should - // calculate the sum from 0 to left - 1, giving us prefixSum[left - 1]. - // - // If the left index is 0, then we don't need to subtract anything, so the value is 0. - const lvalue = left > 0 ? this.prefixSum[left - 1] : 0; - - return rvalue - lvalue; - } -} diff --git a/src/prefix-sum/running-sum-of-1d-array.ts b/src/prefix-sum/running-sum-of-1d-array.ts deleted file mode 100644 index 0de63ce..0000000 --- a/src/prefix-sum/running-sum-of-1d-array.ts +++ /dev/null @@ -1,25 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an array nums. We define a running sum of an array as runningSum[i] = sum(nums[0]…nums[i]). -// -// Return the running sum of nums. -// -// See {@link https://leetcode.com/problems/running-sum-of-1d-array/description/} -export { runningSum }; - -// SOLUTION: -// -// This is known as a prefix sum. -// -// See {@link https://en.wikipedia.org/wiki/Prefix_sum} -function runningSum(nums: number[]): number[] { - if (nums.length <= 1) { - return nums; - } - - const result = [nums[0]]; - for (let i = 1; i < nums.length; i++) { - result[i] = result[i - 1] + nums[i]; - } - return result; -} diff --git a/src/prefix-sum/subarray-sum-equals-k.ts b/src/prefix-sum/subarray-sum-equals-k.ts deleted file mode 100644 index 8badf01..0000000 --- a/src/prefix-sum/subarray-sum-equals-k.ts +++ /dev/null @@ -1,61 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of integers nums and an integer k, return the total number of subarrays whose sum equals to k. -// -// A subarray is a contiguous non-empty sequence of elements within an array. -// -// See {@link https://leetcode.com/problems/subarray-sum-equals-k/} -export { subarraySum }; - -// SOLUTION: -// -// Many fixed size subarray problems can be solved using the sliding window technique. Here, if we ONLY had positive -// numbers, we could use the sliding window technique to find subarrays that sum to k. However, there could be negative -// numbers and zeroes, so we can't do it. -// -// The brute force approach is to generate all subarrays and check if the sum equals k. This is O(n^2) time complexity. -// Instead, we'll use a prefix sum and hashmap to reduce the time complexity to O(n). -// -// - Use a prefix sum to track sums up to each index. -// - Use a hashmap to track the frequency of each sum. -// - Check if a prefix sum difference equals k to find valid subarrays. -// -// That is, if prefix[j] - prefix[i] = k, then we know that a subarray from (i, j] has a sum of k. Rearranging, we get -// then prefix[j] = prefix[i] - k. If prefix[i] exists in our map, we know at least one subarray exists that sums to k. -function subarraySum(nums: number[], k: number): number { - // This is the number of subarrays that sum to k. - let result = 0; - - // This is prefix sum; it's not actually necessary to create an array to store the prefix sum. We can just keep a - // running sum. - let pj = 0; - - // This is our map of prefix sums to their frequency. - // - // Note that we do need to maintain a map here, instead of just a set of prefix sums. This is because the array could - // have negative numbers, repeated values, and zeroes. This would cause prefix sums to repeat. - const map = new Map(); - - // This is a critical step; don't forget to seed the map with a prefix sum of 0 with frequency 1. Without this seed, - // we'd miss subarrays that start at the beginning of the array. - map.set(0, 1); - - for (const num of nums) { - // This is the our running sum. - pj += num; - - // If prefix[j] - prefix[i] = k, then we know for sure that the subarray from (i, j] has a sum of k. We can compute - // prefix[i] = prefix[j] - k. - const pi = pj - k; - - // If prefix[j] appears in the frequency map, we have at LEAST one subarray that sums to k. Add the total frequency - // to the result. - result += map.get(pi) ?? 0; - - // Now update the frequency map to account for this new prefix sum up to j. - const freq = map.get(pj) ?? 0; - map.set(pj, freq + 1); - } - - return result; -} diff --git a/src/recursion/generate-parentheses.ts b/src/recursion/generate-parentheses.ts deleted file mode 100644 index 689300d..0000000 --- a/src/recursion/generate-parentheses.ts +++ /dev/null @@ -1,54 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. -// -// See {@link https://leetcode.com/problems/generate-parentheses/} -export { generateParenthesis }; - -// SOLUTION: -// -// A naive way to do this would be to do something like generate all the parentheses for n-1, then add more -// parentheses to each of the elements generated. For example, by transforming each element of the n-1 solution to -// `${e}()`, `()${e}`, and `($e)`. -// -// This doesn't work for values like n=4, because you will miss solutions like `(())(())`. Instead, we'll construct -// the string as we go along, keeping track of how many open and close parentheses we have used. In total, the -// resulting strings will each have length n*2. -// -// This is because well formed strings have additional constraints; when generating a power set using the naive -// method, for example, you don't need to worry about balancing the elements within the sub set. In contrast to -// generating a power set, you would have far fewer resulting elements. -function generateParenthesis(n: number): string[] { - if (n === 0) { - return []; - } - - const result: string[] = []; - const max = n * 2; - - function generate(text: string, opens: number, closes: number) { - // The max length of the string is n * 2 because each open paren requires a close paren. We can't get fewer than - // n * 2 characters in a string either, because we have to generate all possible combinations. - if (text.length === max) { - result.push(text); - return; - } - - // The total string length is n * 2, but we can only open a max of n parens; the rest have to be closed. If we - // still have opens remaining, we can open more, or we can close. - if (opens < n) { - generate(`${text}(`, opens + 1, closes); - // Additionally, we can open to close some more parens if there are any open. - if (opens > closes) { - generate(`${text})`, opens, closes + 1); - } - } - // Here we have exceeded our budget of opening parens, so we have to only close. - else { - generate(`${text})`, opens, closes + 1); - } - } - - generate('', 0, 0); - return result; -} diff --git a/src/recursion/maximum-number-of-operations-with-the-same-score.ts b/src/recursion/maximum-number-of-operations-with-the-same-score.ts deleted file mode 100644 index 897a669..0000000 --- a/src/recursion/maximum-number-of-operations-with-the-same-score.ts +++ /dev/null @@ -1,92 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of integers called nums, you can perform any of the following operation while nums contains at least -// 2 elements: -// -// - Choose the first two elements of nums and delete them. -// - Choose the last two elements of nums and delete them. -// - Choose the first and the last elements of nums and delete them. -// -// The score of the operation is the sum of the deleted elements. -// -// Your task is to find the maximum number of operations that can be performed, such that all operations have the same -// score. -// -// Return the maximum number of operations possible that satisfy the condition mentioned above. -// -// See {@link https://leetcode.com/problems/maximum-number-of-operations-with-the-same-score-ii/} -export { maxOperations }; - -// SOLUTION: -// -// The range of possible scores is determined by applying the operation once. Subsequent operations must produce the -// same score, so there are only three possible operations. For each operation, we should apply them, then follow -// paths of operations that result in the same score until the array is exhausted. -// -// We COULD use the sliding window technique by applying each operation and then only following the operation paths -// that have the same score as the one we are considering. However, this would only work if at each choice of three -// operations, the scores are unique. If there are two or more choices with the same score, then we need to explore -// multiple operation paths at once. -// -// To explore multiple operation paths at once, we'll need to use a backtracking approach. -function maxOperations(nums: number[]): number { - if (nums.length < 2) { - return 0; - } - - // These are the range of possible scores we can have as we truncate the array. - const scores = new Set(); - scores.add(nums[0] + nums[1]); - scores.add(nums[nums.length - 1] + nums[nums.length - 2]); - scores.add(nums[0] + nums[nums.length - 1]); - - // There is an opportunity for optimization when using backtracking. We can maintain a memoization table to keep - // track of previously computed values. - const memo = new Map(); - - // Adapt the sliding window approach so we can explore multiple paths at once by backtracking. - function backtrack(score: number, left: number, right: number, paths: number): number { - if (left >= right) { - return paths; - } - - // Check the memoization table to see if we've previously computed this value. - const key = `${score},${left},${right}`; - if (memo.has(key)) { - return memo.get(key)!; - } - - // This is the local max count, after exploring all possible paths. - let local = paths; - - // Check if the first 2 elements are equal to the score, and if the left and right indices are in range. - // - // To be in range, the array must at least look like this: [left, left + 1/right]. - if (left + 1 <= right && nums[left] + nums[left + 1] === score) { - local = Math.max(local, backtrack(score, left + 2, right, paths + 1)); - } - - // Check if the last 2 elements are equal to the score, and if the left and right indices are in range. - // - // To be in range, the array must at least look like this: [left/right - 1, right] - if (left <= right - 1 && nums[right] + nums[right - 1] === score) { - local = Math.max(local, backtrack(score, left, right - 2, paths + 1)); - } - - // Check if the first and last elements are equal to the score, and if the left and right indices are in range. - // - // To be in range, the array must at least look like this: [left, right] - if (left < right && nums[left] + nums[right] === score) { - local = Math.max(local, backtrack(score, left + 1, right - 1, paths + 1)); - } - - // Cache the result for a future computation. - memo.set(key, local); - return local; - } - - // For each score, check how many times we can keep applying operations. To perform a map, we' - const list = [...scores]; - const maxes = list.map(score => backtrack(score, 0 /* left */, nums.length - 1 /* right */, 0 /* paths */)); - return maxes.reduce((a, b) => Math.max(a, b)); -} diff --git a/src/recursion/optimal-account-balancing.ts b/src/recursion/optimal-account-balancing.ts deleted file mode 100644 index 9223c03..0000000 --- a/src/recursion/optimal-account-balancing.ts +++ /dev/null @@ -1,95 +0,0 @@ -// DIFFICULTY: HARD -// -// You are given an array of transactions transactions where transactions[i] = [fromi, toi, amounti] indicates that the -// person with ID = fromi gave amounti $ to the person with ID = toi. -// -// Return the minimum number of transactions required to settle the debt. -// -// See {@link https://leetcode.com/problems/optimal-account-balancing/} -export { minTransfers }; - -// SOLUTION: -function minTransfers(transactions: number[][]): number { - type Person = number; - type Amount = number; - const map = new Map(); - - // First, figure out the balance that each person holds. - for (const transaction of transactions) { - const [from, to, amount] = transaction; - map.set(from, (map.get(from) ?? 0) - amount); - map.set(to, (map.get(to) ?? 0) + amount); - } - - // Second, figure out all non-zero balances. If we needed to know who is paying whom to achieve the minimum number - // of transactions, we will need to have an array indexed by person. However, we don't need to know that. - // - // We just need to know the number of transactions, so we can just get a list of non-zero balances and try to zero - // them out. - const balances: number[] = []; - for (const value of map.values()) { - if (value !== 0) { - balances.push(value); - } - } - - // To settle debts efficiently, we should should match opposite balances together, and have them settle with each - // other. For example, if somebody has +5 balance, we should match then up with a -5 balance. Matching two - // balances with the same sign together will just result in extra transactions. - // - // Therefore, we should examine sequences of pairs that work towards an all zero balance sheet. However, different - // sequences of pairs could result in different numbers of transactions. So we will need to examine *all* possible - // pairs and keep track of the minimum tx's across all sequences. - // - // To do this effectively we need to use a recursive backtracking technique by trying to settle the ith balance - // with i+1, i+2, i+3, etc and seeing how many tx's we incur at each step. - function settle(left: number) { - // Skip over the already settled balances; if we've skipped over all balances, the minimum number of tx's is 0. - while (left < balances.length && balances[left] === 0) { - left++; - } - - if (left === balances.length) { - return 0; - } - - // Try to settle the ith balance with the first opposite signed balance. - let min = Infinity; - for (let right = left + 1; right < balances.length; right++) { - // These balances were either both positive, or both negative, so do not bother executing a transaction with - // them. - if (balances[left] * balances[right] > 0) { - continue; - } - - // Attempt to settle (i, j) by adding the ith balance to the jth balance. Technically we don't have to - // decrement balance[i] at all, since the next call to settle will start at j (and i will be skipped). But it - // is included here for clarity. - // - // Note that we also do not have to ensure that we only add the minimum amount to balance. For example, if - // balances[i] = 10 and balances[j] = -5, we don't need to only add 5 to balances[j]. As we recursively move - // down the list, balances[j] will eventually zero out (and a more efficient path may be discovered). - // - // Also, we are guaranteed that the balances *do* eventually zero out because there isn't any extra money in - // the system. - const value = balances[left]; - balances[right] += value; - balances[left] = 0; - - // The minimum number of transactions, if we've settled the ith balance, is 1 + the minimum after settling the - // i+1th balance. - // - // As a further optimization we can use dynamic programming to avoid redundant calculations. To do so we'll - // need to use a bit mask to keep track of balance state. - min = Math.min(min, 1 + settle(left + 1)); - - // Backtrack by restoring the balances. - balances[right] -= value; - balances[left] = value; - } - - return min; - } - - return settle(0); -} diff --git a/src/recursion/permutations.ts b/src/recursion/permutations.ts deleted file mode 100644 index 511c729..0000000 --- a/src/recursion/permutations.ts +++ /dev/null @@ -1,43 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order. -// -// See {@link https://leetcode.com/problems/permutations/} -export { permute }; - -// SOLUTION: -// -// To do this, recursively generate all permutations of elements without the head. Then take all permutations created -// and add x to the front of the list. This should result in n! number of arrays. -// -// The general strategy is to split the list into a head and a tail. Generate all the permutations of the list -// without the head element, then put the head in front of each of the generated permutations. Do this recursively -// for each element in the list will give all permutations. -function permute(xs: number[]): number[][] { - if (xs.length === 0) { - return [[]]; - } - - if (xs.length === 1) { - return [xs]; - } - - const result = []; - - for (let i = 0; i < xs.length; i += 1) { - // Get the ith element; we will remove this element from the array, and generate permutations without this - // element. - const x = xs[i]; - - // Generates all permutations without the ith element. - const without = [...xs.slice(0, i), ...xs.slice(i + 1, xs.length)]; - const ps = permute(without); - - // For each of the permutations, put the ith element in front of the permutations, to get all possible - // permutations. - ps.forEach(p => p.unshift(x)); - result.push(...ps); - } - - return result; -} diff --git a/src/recursion/subsets.ts b/src/recursion/subsets.ts deleted file mode 100644 index 46af7f3..0000000 --- a/src/recursion/subsets.ts +++ /dev/null @@ -1,48 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums of unique elements, return all possible subsets (the power set). -// -// The solution set must not contain duplicate subsets. Return the solution in any order. -// -// See {@link https://leetcode.com/problems/subsets/} -export { subsets }; - -// SOLUTION: -// -// To do this, recursively generate all combinations of elements of the set `xs - x`, then take all the subsets -// created and add x to them. This should result in 2^n number of subsets. -function subsets(nums: number[]) { - function run(xs: Set): Set> { - if (xs.size === 0) { - // Note that new Set(new Set()) gives you a set with zero elements, because the constructor takes all elements of - // the first argument and adds them to the set. So you actually want to construct a new iterable based on the - // empty set. - return new Set([new Set()]); - } - - // Find the first element of xs and call it x; remove it from the list. - const x = xs.values().next().value!; - - // Generate all subsets without x. - const rest = new Set(xs); - rest.delete(x); - - const excluded: Set> = run(rest); - - // Add x to all of the subsets from above. - const included = new Set>(); - excluded.forEach(ex => { - const set = new Set(ex); - set.add(x); - - included.add(set); - }); - - // The subsets with x and the subsets without x constitute the power set. - return new Set([...excluded, ...included]); - } - - // LeetCode expects the result to be an array of arrays. - const result = run(new Set(nums)); - return [...result].map(xs => [...xs]); -} diff --git a/src/recursion/word-break-ii.ts b/src/recursion/word-break-ii.ts deleted file mode 100644 index d233de2..0000000 --- a/src/recursion/word-break-ii.ts +++ /dev/null @@ -1,70 +0,0 @@ -// DIFFICULTY: HARD -// -// Given a string s and a dictionary of strings wordDict, add spaces in s to construct a sentence where each word is a -// valid dictionary word. Return all such possible sentences in any order. -// -// Note that the same word in the dictionary may be reused multiple times in the segmentation. -// -// See {@link https://leetcode.com/problems/word-break-ii/} -export { wordBreak }; - -// SOLUTION: -function wordBreak(s: string, wordDict: string[]): string[] { - const set = new Set(wordDict); - - // Since values can be re-calculated, let's memoize the results. - const memo = new Map(); - - // This generates all possible sentences using words that start at index i (if any exist) recursively. - // - // Since the only words we have available to us are the ones in the wordDict, we'll just have to loop through from - // i to the end of the string to see if any of those substrings form a 'word'. - function generate(i: number): string[] { - // Because we are slicing from [i, j), and slice is exclusive on the second index, we will be starting at i + 1. - // Therefore, do the bounds check for i === s.length. - if (i === s.length) { - return []; - } - - if (memo.has(i)) { - return memo.get(i)!; - } - - const sentences: string[] = []; - - // Attempt to generate sentences beginning with the 'word' from i to j. Note that we want to slice from i to j, - // so we actually want j to start at i + 1, and we also want to include j === s.length, because the slice function - // is inclusive for the first index, and exclusive for the second index. - for (let j = i + 1; j <= s.length; j++) { - const word = s.slice(i, j); - if (!set.has(word)) { - continue; - } - - // Now generate all possible sentences using words that start at index j (if any exist), and put the word from - // index i in front. - const rest = generate(j); - - // If the problem allowed us to generate sentences using a partial selection of characters (e.g. if we couldn't - // make more words at this point), we could just add the current word to the result, even if there were unused - // characters remaining. - // - // However, the problem does not allow us to do this, so we only add the current word if there are no unused - // characters remaining. - if (rest.length === 0 && j === s.length) { - sentences.push(word); - continue; - } - - // If we could generate words starting at j, prepend the word starting at index i. - for (const sentence of rest) { - sentences.push(`${word} ${sentence}`); - } - } - - memo.set(i, sentences); - return sentences; - } - - return generate(0); -} diff --git a/src/sliding-window/best-time-to-buy-and-sell-stock-i.ts b/src/sliding-window/best-time-to-buy-and-sell-stock-i.ts deleted file mode 100644 index 40c0703..0000000 --- a/src/sliding-window/best-time-to-buy-and-sell-stock-i.ts +++ /dev/null @@ -1,46 +0,0 @@ -// DIFFICULTY: EASY -// -// You are given an array prices where prices[i] is the price of a given stock on the ith day. -// -// You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future -// to sell that stock. -// -// Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0. -// -// See {@link https://leetcode.com/problems/best-time-to-buy-and-sell-stock/} -export { maxProfit }; - -// SOLUTION: -// -// The question is a bit contrived, as in reality this would never happen. Here, we are assuming we can go backwards -// in time to be able to buy at the low point and sell at the high point. Just keep that in mind: we have a time -// machine. -// -// This type of problem is easily solved with a modified version of the sliding window technique. We will maintain -// a minimum price and update that value whenever we see a lower value, from there we can calculate proposed profit -// on any given day. -// -// By iterating through the array we'll find the maximum profit at the end. -function maxProfit(prices: number[]): number { - // We want to buy at the lowest point, and then sell at the highest point. To do this, keep track of the minimum - // stock price at any point, and use that minimum stock price to determine our maximum profit. - let minPrice = Infinity; - let maxProfit = 0; - - for (let i = 0; i < prices.length; i++) { - const price = prices[i]; - - // If the price ends up being lower than what we've ever seen, buy it and see if we can profit. - if (price < minPrice) { - minPrice = price; - continue; - } - - // Unfortunately, it can be the case that we bought at a low point but the stock continues or fall or trades flat. - // If that is the case, we'll keep our previous value of max profit. - const profit = price - minPrice; - maxProfit = Math.max(maxProfit, profit); - } - - return maxProfit; -} diff --git a/src/sliding-window/count-subarrays-where-max-element-appears.ts b/src/sliding-window/count-subarrays-where-max-element-appears.ts deleted file mode 100644 index 3b6839d..0000000 --- a/src/sliding-window/count-subarrays-where-max-element-appears.ts +++ /dev/null @@ -1,56 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer array nums and a positive integer k. -// -// Return the number of subarrays where the maximum element of nums appears at least k times in that subarray. -// -// A subarray is a contiguous sequence of elements within an array. -// -// See {@link https://leetcode.com/problems/count-subarrays-where-max-element-appears-at-least-k-times/} -export { countSubarrays }; - -// SOLUTION: -// -// Use the sliding window technique to count the number of subarrays with at least k elements. -function countSubarrays(nums: number[], k: number): number { - const max = Math.max(...nums); - - // We could use a set to store the number of unique windows that match our criterion of having at least k max - // elements. To do so, we'd need to store all combination of indices (i, j) where i <= left and j >= right. We - // have to do it this way because storing the subarrays themselves would result in 'duplicates' not being counted. - // - // For example, consider [1,9,9,9,9,9,1] and k=2 where the window [9,9] can appear multiple times. Using a set of - // subarrays would not count the subarrays resulting from 'duplicate' windows. - // - // Even worse, using the formula (a * (b + 1)) to compute additional subarrays where a is the number of elements to - // the left of the window and b is the number of elements to the right of the window does not work. Normally it - // would work, but we are trying to compute subarrays for *each* window, which will result in double counting. - // - // Even though using a set does work, you will exceed LeetCode's time limits, so you'll need a more clever solution. - let total = 0; - - // The number of max elements in our window. - let count = 0; - - for (let left = 0, right = 0; right < nums.length; right++) { - // Count elements that are equal to the max until we get to k elements. - if (nums[right] === max) { - count++; - } - - // Now that we have k elements or more, shrink the window from the left until we have fewer than k elements. - while (count >= k) { - if (nums[left] === max) { - count--; - } - - left++; - } - - // Once left is advanced after there are < k elements, every subarray ending at the right pointer is counted by - // the value at left. - total += left; - } - - return total; -} diff --git a/src/sliding-window/length-of-longest-substring.ts b/src/sliding-window/length-of-longest-substring.ts deleted file mode 100644 index e191902..0000000 --- a/src/sliding-window/length-of-longest-substring.ts +++ /dev/null @@ -1,43 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a string s, find the length of the longest substring without repeating characters. -// -// See {@link https://leetcode.com/problems/longest-substring-without-repeating-characters/} -export { lengthOfLongestSubstring }; - -// SOLUTION: -// -// This can be solved using the sliding window technique by maintaining, always, a window where we have a substring -// without repeating characters. -// -// To ensure we can check this constraint, we'll keep track of the last time we have seen a character, so we can move -// the left pointer to the right of the left seen index and maintain our no-repeat constraint. -// -// Once we move the left pointer just one step to the right of the last seen index, we'll update that seen index to -// the rightmost position we've seen it, so we can leverage the map again next time. -function lengthOfLongestSubstring(text: string): number { - const seen = new Map(); - - // Use a sliding window approach by moving the right pointer along the string's characters, and updating the left - // pointer any time we see a repeated character. - let max = 0; - for (let left = 0, right = 0; right < text.length; ) { - const c = text[right]; - - // If we've already seen this character, update the left pointer and move it just right of the last seen - // occurrence to ensure no repeated characters. - if (seen.has(c)) { - const last = seen.get(c)!; - left = Math.max(left, last + 1); - } - - // Update our asumption about the current max. - max = Math.max(max, right - left + 1); - - // Set the current character to be seen and advance the right pointer. - seen.set(c, right); - right++; - } - - return max; -} diff --git a/src/sliding-window/longest-continuous-subarray.ts b/src/sliding-window/longest-continuous-subarray.ts deleted file mode 100644 index f942ba9..0000000 --- a/src/sliding-window/longest-continuous-subarray.ts +++ /dev/null @@ -1,94 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of integers nums and an integer limit, return the size of the longest non-empty subarray such that the -// absolute difference between any two elements of this subarray is less than or equal to limit. -// -// See {@link https://leetcode.com/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/} -export { longestSubarray }; - -// Use a sliding window approach to find the max length subarray. We'll have to keep track of what the min/max -// elements in the subarray while adjusting our window size. To do so we'll use two deques. -// -// We'll have a right pointer that moves forward through the array as normal. The left pointer will move forward -// through the array only when the subarray constraint (under limit) is violated. As the pointers move, we'll need -// to keep track of the smallest and largest elements; that's what the deques are for. -// -// We can't simply store the max element or min element by itself; as the pointers move, these elements will change. -// Instead, we'll have to use our deques to maintain the max/min elements at every window size. The `minDeque` will -// have elements in increasing order, and the `maxDeque` will have elements in decreasing order. We keep the deques -// organized like this so that the largest element will always be at the front of the `maxDeque`, and the smallest -// element will always be at the front of the `minDeque`. -// -// As we advance the right pointer, we'll have to pop off elements at the back of the deque and then push the right -// pointer onto the end of the deque, while maintaining the ascending/descending order. As we advance the left -// pointer, we'll shift off the element at the front of the deque if it is less than what we just passed. -function longestSubarray(nums: number[], limit: number): number { - type Index = number; - - // Keeps track of indices whose values are in descending order. This will allow us to quickly find the index of the - // largest element as the window shifts. - const maxDeque: Index[] = []; - - // Keeps track of indices whose values are in ascending order. This will allow us to quickly find the index of the - // smallest element as the window shifts. - const minDeque: Index[] = []; - - // The current max length of a continuous subarray. - let result = 0; - - function last(deque: Index[]) { - return deque[deque.length - 1]; - } - - // Expand the right window while updating our deques. Each element we encounter from the right side will be used - // to update our deques. - for (let left = 0, right = 0; right < nums.length; right++) { - const value = nums[right]; - - // Update the max deque by removing all elements at the end of the deque that are smaller, so we can maintain a - // deque in descending order. - while (nums[last(maxDeque)] <= value) { - maxDeque.pop(); - } - maxDeque.push(right); - - // Update the min deque by removing all elements at the end of the deque that are bigger, so we can maintain a - // deque in ascending order. - while (nums[last(minDeque)] >= value) { - minDeque.pop(); - } - minDeque.push(right); - - // Check the conditions of our subarray; if we have exceeded the limit, move the left pointer until we are under - // the limit. - // - // After moving the left pointer, it could be the case that we have to update our min/max deque as it is no longer - // being considered as part of the subarray. - let min = minDeque[0]; - let max = maxDeque[0]; - while (Math.abs(nums[max] - nums[min]) > limit) { - left++; - - // If we've moved past the min index, remove the min index from the front of the deque. Note that we are - // comparing indices here, not actual values. - if (minDeque[0] < left) { - minDeque.shift(); - } - - // Alternatively, if we've moved past the max index, remove the max index from the front of the deque. Note - // that we are comparing indices here, not actual values. - if (maxDeque[0] < left) { - maxDeque.shift(); - } - - min = minDeque[0]; - max = maxDeque[0]; - } - - // Update our assumptions of the max length of the subarray. - const length = right - left + 1; - result = Math.max(result, length); - } - - return result; -} diff --git a/src/sliding-window/max-sum-distinct-subarray-of-size-k.ts b/src/sliding-window/max-sum-distinct-subarray-of-size-k.ts deleted file mode 100644 index 656e0c6..0000000 --- a/src/sliding-window/max-sum-distinct-subarray-of-size-k.ts +++ /dev/null @@ -1,85 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer array nums and an integer k. Find the maximum subarray sum of all the subarrays of nums that -// meet the following conditions: -// -// - The length of the subarray is k, and -// - All the elements of the subarray are distinct. -// -// Return the maximum subarray sum of all the subarrays that meet the conditions. If no subarray meets the conditions, -// return 0. -// -// A subarray is a contiguous non-empty sequence of elements within an array. -// -// See {@link https://leetcode.com/problems/maximum-sum-of-distinct-subarrays-with-length-k} -export { maximumSubarraySum }; - -// SOLUTION: -// -// Without the distinct requirement, we can use the sliding window technique with a window of size k, shifting the -// window as we look for the maximum sum. -// -// With the distinct element requirement, we need to maintain a set and disregard sub arrays that don't have unique -// elements. -function maximumSubarraySum(xs: number[], k: number): number { - let current = 0; - let max = 0; - let left = 0; - let right = 0; - - // Define a set to check if we have a distinct sub array, and define the left pointer of our sliding window. We - // will update the window based on uniqueness and compute a sum when we have k elements. - const uniques = new Set(); - - // Build up our window by moving the right pointer. We'll update the left pointer along the way in case we - // detect a sub array with duplicate elements. - while (right < xs.length) { - const value = xs[right]; - - // If the current element is already in the sub array, advance the left pointer until we no longer have a - // duplicate element. We'll have to decrement the current sum to account for sliding the left pointer forward. - while (uniques.has(value)) { - uniques.delete(xs[left]); - current -= xs[left]; - left++; - } - - // Now that the sub array is free of dupes, we can add the current element to the sum. Let's not update the - // right pointer just yet; if we are over k elements, we want to leave the right pointer where it is for the - // moment. - current += value; - uniques.add(value); - - // If, by adding this element, we've gone over k elements, shrink the window by moving the left pointer, which - // should put us at exactly k elements. - // - // Note that we can check if we've violated the size constraint by checking the size of the `uniques` set, which - // forms our contiguous set. By adding a single element, the most we could've gone over by is one element, so - // just check, and if we've done so, slide the left pointer forward again. - // - // Once within constraints we can fall through to the next block. - if (uniques.size > k) { - uniques.delete(xs[left]); - current -= xs[left]; - left++; - } - - // If the sub array has exactly k elements at this iteration (or after we've shrink the window), update our - // assumptions about the max sum and update the right pointer. - // - // Note that the continue statements are unnecessary but are added for clarity. - if (uniques.size === k) { - max = Math.max(current, max); - right++; - continue; - } - - // If the sub array has fewer than k elements, grow the window by increasing the right pointer. - if (uniques.size < k) { - right++; - continue; - } - } - - return max; -} diff --git a/src/sliding-window/minimum-window-substring.ts b/src/sliding-window/minimum-window-substring.ts deleted file mode 100644 index 295b849..0000000 --- a/src/sliding-window/minimum-window-substring.ts +++ /dev/null @@ -1,112 +0,0 @@ -// DIFFICULTY: HARD -// -// Given two strings s and t of lengths m and n respectively, return the minimum window substring of s such that every -// character in t (including duplicates) is included in the window. If there is no such substring, return the empty -// string "". -// -// The testcases will be generated such that the answer is unique. -// -// @see {@link https://leetcode.com/problems/minimum-window-substring/} -export { minWindow }; - -// SOLUTION: -// -// This can be solved using the sliding window technique with a bunch of extra bookkeeping. The right pointer will be -// expanded until we have a valid window, then the left pointer will be shrunk to the minimum size window that still -// satisfies the requirements. -// -// COMPLEXITY: -// -// Time complexity is O(m + n) because we iterate through both the source and the target strings. -// -// The want map is O(n) space complexity because we store character frequency of the target string. The got map is -// O(m) space complexity because we store character frequency of the source string. Together it's O(m + n). -function minWindow(s: string, t: string): string { - // It is not possible to return a minimum window here; the target substring MUST be shorter than the source string. - if (t.length > s.length) { - return ''; - } - - // First we need to create a frequency map, so we can easily check if a substring has all the characters in t. This - // is required because the characters in t can be duplicated, and we require the same number of duplicates in the - // source substring. - const want = new Map(); - for (const c of t) { - const freq = want.get(c) || 0; - want.set(c, freq + 1); - } - - // Define both pointers to start at the source string, and start by expanding the right pointer. Once a valid window - // is discovered, we contract the left pointer. - let left = 0; - let right = 0; - - // Define a map to keep track of the characters we have seen so far in the window. This will let us know when we have - // a valid window. - // - // We also keep track of which characters we've 'gotten' so far. That is, if we have a requirement to contain X - // number of 'a' characters, and we have seen X characters, then we have 'gotten' that character. When we have - // 'gotten' all characters in the string, we have a valid window. - const got = new Map(); - let gotten = 0; - - // Keep track of our minimum window substring. The left pointer should point to the start, and the size will tell us - // the length of the window, allowing us to call s.substring() later. - // - // If we run through the algorithm and don't get a valid window, because minSize never got updated, then we return ''. - let minLeft = 0; - let minSize = Infinity; - - // Begin by expanding the right pointer until we have a valid window. - while (right < s.length) { - // Update the frequency of the character that we have got. - const c = s[right]; - const freq = got.get(c) || 0; - got.set(c, freq + 1); - - // If it turns out that we got exactly the frequency of character c that we wanted, then the requirement to contain - // X number of character c has been satisfied. - if (want.has(c) && want.get(c) === got.get(c)) { - gotten++; - } - - // If we've gotten all the characters at exactly the right frequency, then we have a valid window. In that case, - // we can begin shrinking the window to see if it stays valid. - while (gotten === want.size) { - const k = s[left]; - - // Calculate the size of the window. Note that left and right are INCLUSIVE indexes, so if we want the window - // size of say [0, 1, 2, 3] with left = 0 and right = 3 (a window size of 4), we should do 3 - 0 + 1 = 4. - // - // So make sure to add 1 here to get the correct size. - const size = right - left + 1; - if (size < minSize) { - minSize = size; - minLeft = left; - } - - // Now try to contract the window by moving the left pointer. When we do this, we have to update the frequency - // of characters we've gotten so far, and update the gotten count. - // - // Update the frequency of the character that we are about to lose. Then if we have got fewer characters than - // wanted, we decrement the 'gotten' count because we no longer have a valid window. - got.set(k, got.get(k)! - 1); - if (want.has(k) && got.get(k)! < want.get(k)!) { - gotten--; - } - - // After performing the bookkeeping, we can now shrink the window by moving the left pointer. - left++; - } - - // Keep expanding the right window until we have a valid window. - right++; - } - - // This means we didn't find any valid window. - if (minSize === Infinity) { - return ''; - } - - return s.substring(minLeft, minLeft + minSize); -} diff --git a/src/sliding-window/moving-average-from-data-stream.ts b/src/sliding-window/moving-average-from-data-stream.ts deleted file mode 100644 index 241c026..0000000 --- a/src/sliding-window/moving-average-from-data-stream.ts +++ /dev/null @@ -1,37 +0,0 @@ -// DIFFICULTY: EASY -// -// Given a stream of integers and a window size, calculate the moving average of all integers in the sliding window. -// -// Implement the MovingAverage class: -// -// - MovingAverage(int size) Initializes the object with the size of the window size. -// - double next(int val) Returns the moving average of the last size values of the stream. -// -// See {@link https://leetcode.com/problems/moving-average-from-data-stream/} -export { MovingAverage }; - -// SOLUTION: -// -// This can be solved with a queue or sliding window. -class MovingAverage { - private readonly size: number; - private readonly window: number[]; - private sum: number; - - constructor(size: number) { - this.size = size; - this.window = []; - this.sum = 0; - } - - next(val: number): number { - if (this.window.length === this.size) { - const out = this.window.shift(); - this.sum -= out!; - } - - this.window.push(val); - this.sum += val; - return this.sum / this.window.length; - } -} diff --git a/src/sliding-window/repeated-dna-sequences.ts b/src/sliding-window/repeated-dna-sequences.ts deleted file mode 100644 index 214527d..0000000 --- a/src/sliding-window/repeated-dna-sequences.ts +++ /dev/null @@ -1,49 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// The DNA sequence is composed of a series of nucleotides abbreviated as 'A', 'C', 'G', and 'T'. -// -// For example, "ACGAATTCCG" is a DNA sequence. -// When studying DNA, it is useful to identify repeated sequences within the DNA. -// -// Given a string s that represents a DNA sequence, return all the 10-letter-long sequences (substrings) that occur -// more than once in a DNA molecule. You may return the answer in any order. -// -// See {@link https://leetcode.com/problems/repeated-dna-sequences/} -export { findRepeatedDnaSequences }; - -// SOLUTION: -// -// Use the sliding window technique to keep track of sequences. -function findRepeatedDnaSequences(s: string): string[] { - if (s.length < 10) { - return []; - } - - const seen = new Set(); - const repeated = new Set(); - - // We can use a sliding window to keep track of 10 character sequences, then if we have seen the sequence, add it - // to the result. - for (let left = 0, right = 9; right < s.length; right++) { - // Two options exist here, because substring takes the leftmost element (inclusive) and rightmost element - // (exclusive). - // - // i) We could calculate right - left + 1 to account for us slicing from [0, 9 + 1). - // ii) We could start right = 10, then set the loop condition to be right <= s.length instead of just <. - // - // The second approach makes it easier to avoid off by 1, but we do this here to show you have to tackle the off - // by 1 head on. - if (right - left + 1 > 10) { - left++; - } - - const seq = s.substring(left, right + 1); - if (seen.has(seq)) { - repeated.add(seq); - } else { - seen.add(seq); - } - } - - return [...repeated]; -} diff --git a/src/stack/README.md b/src/stack/README.md deleted file mode 100644 index 60d47e0..0000000 --- a/src/stack/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Stack - -## Common Phrases - -These phrases indicate a stack might be useful: - -- 'balanced delimiter (like parentheses)', 'valid delimiters (like brackets)' -- 'next greater', 'previous smaller' -- 'undo' diff --git a/src/stack/basic-calculator-ii.ts b/src/stack/basic-calculator-ii.ts deleted file mode 100644 index 22eb62d..0000000 --- a/src/stack/basic-calculator-ii.ts +++ /dev/null @@ -1,136 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a string s which represents an expression, evaluate this expression and return its value. -// -// The integer division should truncate toward zero. -// -// You may assume that the given expression is always valid. All intermediate results will be in the range of -// [-231, 231 - 1]. -// -// Note: You are not allowed to use any built-in function which evaluates strings as mathematical expressions, such as -// eval(). -// -// See {@link https://leetcode.com/problems/basic-calculator-ii} -export { calculate }; - -// SOLUTION: -// -// Assumption is that we only have numbers, +, -, *, and / in the expression. We also assume no parentheses for -// grouping expressions. -// -// To do this we iterate through the string and use a stack to store intermediate results of computation. Because the -// '*' and '/' operators have higher precedence, we'll resolve them immediately. Those intermediate results will be -// stored onto the stack and we'll resolve the '+' and '-' operators at the end. -// -// Take note that the '-' operator can be a unary operator. -// -// Here's an example evaluation. Let's suppose we have '33+2*2'. Whenever we resolve an operator, OR start the -// calculator, we'll assume the first number is 0 (no value) and the last operator is '+'. -// -// - When we see c = '3', we'll store it as the current value as '3'. -// - When we see c = '3' again, we'll set the current value as '3 *10 + 3' or '33'. -// - When we see c = '+', we'll resolve the current value '33' on the stack. In this step, the current operator c is -// '+' and the last operator is also '+'. Based on the last operator being '+', we push onto the stack. Now we set -// the last operator to '+'. -// - When we see c = '2', we'll store it as the current value as '2'. -// - When we see c = '*', we'll resolve the current value '2'. In this step, the current operator 'c' is '*' and the -// last operator is '+'. Based on the last operator being '+', we push onto the stack. Now we set the last operator -// to '*'. -// - When we see c = '2', we'll store it as the current value as '2'. -// - Because it is the last character, we resolve the last operator, which is '*'. So we pop from the stack ('2'), and -// multiply by '2', giving a result '4', which we push to the stack. -// - Finally we sum the stack. -// -// In a situation where the last operator is '-', we would push a negative value onto the stack. For example, if we -// have '3-2', when we see '2', the last operator is '-', so we push '-2' onto the stack. -// -// Similarly, if we just have '-2', we will see the '2' and push '-2' onto the stack, since the last operator is '-'. -// -// COMPLEXITY: -// -// Time and space complexity are both O(n); we do not need to traverse the string more than once, and we also do not -// need to store more than n values (where n is the length of the string) onto the stack. -function calculate(s: string): number { - function isDigit(c: string) { - return c >= '0' && c <= '9'; - } - - // Keep a stack of numbers to add up; the multiply and divide operations will be applied immediately since they have - // higher precedence. - const stack: number[] = []; - - // Keep track of the current value as we iterate through the string. Every time we see a digit, we can multiply the - // number by 10 and add the digit to it. - let value = 0; - - // Note that the action we take when we see an operator depends on the LAST operator we saw, not the current operator - // we see. - // - // Before we've seen any operators, the default behavior is to add (not subtract or something), so set the last - // operator to '+'. - let last = '+'; - - for (let i = 0; i < s.length; i++) { - const c = s[i]; - - // If we see a space, we can safely skip it. UNLESS, it is the last character in a string. In that case, we do - // actually need to resolve the last operation we saw. For example, '3 * 2 ' should resolve '3 * 2'. Here, we'll - // just skip spaces if they are not the last character. - if (c === ' ' && i !== s.length - 1) { - continue; - } - - // If we find a digit, build up the current number by multiplying the current value by 10 and adding the digit. - if (isDigit(c)) { - value = value * 10 + Number.parseInt(c, 10 /* radix */); - - // Normally, we could just continue, but if the digit is the very last character, like '3 * 2', we do actually - // need to fall through to the operation resolution section below. - if (i !== s.length - 1) { - continue; - } - } - - // Now we have a non-digit character. If it's a space, we can ignore it, but ONLY if it's not the last character. - // If it's the last character, we'll have to resolve the current number and operator. - // Okay, now we have either a space or a non-digit character that could potentially be an operation. Either way - // we need to resolve an expression. For example, reading up to the last character in both strings results in some - // evaluation: - // - // - '3 * 2 +' -> Results in resolving '3 * 2' first, then adding the result to the stack. - // - '3 * 2 ' -> Results in resolving '3 * 2' because we are at the last value, but it's a string and not an - // operator! - // - // Remember, the current operator tells us what to do LATER. What we do now depends on the last operator. If the - // last operator was '+', we delay the summation until the end. - if (last === '+') { - stack.push(value); - } - // Similarly if the operator was '-', we delay the summation until the end. - else if (last === '-') { - stack.push(-value); - } - // If the operator was '*', we do want to resolve it immediately with the current value and the value on the stack. - // Imagine we've hit a case like '2 * 2 +' and we've just read the '+'. We'll want to resolve '2 * 2' first. - else if (last === '*') { - const left = stack.pop()!; - const right = value; - stack.push(left * right); - } - // If the last operator was '/', we similarly want to resolve it immediately. Take care to do left / right where - // the left value is on the stack and the right value is what number we just read. - else if (last === '/') { - const left = stack.pop()!; - const right = value; - stack.push(Math.trunc(left / right)); - } - - // Now, after resolving an operation, reset the current value and store the current operator as the last one we have - // seen. - last = c; - value = 0; - } - - const result = stack.reduce((a, b) => a + b, 0); - return result; -} diff --git a/src/stack/basic-calculator.ts b/src/stack/basic-calculator.ts deleted file mode 100644 index 9bb8540..0000000 --- a/src/stack/basic-calculator.ts +++ /dev/null @@ -1,79 +0,0 @@ -// DIFFICULTY: HARD -// -// Given a string s representing a valid expression, implement a basic calculator to evaluate it, and return the result -// of the evaluation. -// -// Note: You are not allowed to use any built-in function which evaluates strings as mathematical expressions, such as -// eval(). -// -// See {@link https://leetcode.com/problems/basic-calculator} -export { calculate }; - -// SOLUTION: -// -// This is way harder than basic calculator ii, even though we don't consider multiplication and division. Here, we -// have to consider parentheses. -function calculate(s: string): number { - const stack = []; - let result = 0; - let sign = 1; - let n = 0; - - function isDigit(c: string) { - // Number(' ') gives you 0, but that's totally not a digit. - if (c === ' ') { - return false; - } - - return !Number.isNaN(Number(c)); - } - - for (let i = 0; i < s.length; i++) { - const c = s[i]; - - if (isDigit(c)) { - n = n * 10 + Number(c); - } else if (c === '+') { - // Whatever the current number is, add it to the result. The sign will account for subtractions. - result += sign * n; - - // Now we reset the sign to be POSITIVE and number to be 0. - sign = 1; - n = 0; - } else if (c === '-') { - // Whatever the current number is, add it to the result. The sign will account for subtractions. - result += sign * n; - - // Now we reset the sign to be NEGATIVE and number to be 0. - sign = -1; - n = 0; - } else if (c === '(') { - // Opening brace means we are evaluating a sub-expression. Whatever the current result and sign are, push it - // onto the stack. - stack.push(result); - stack.push(sign); - - // Now we reset the entire calculator (result, sign, current number) to evaluate the sub-expression. - result = 0; - sign = 1; - n = 0; - } else if (c === ')') { - // Closing brace means we are finished evaluating a sub-expression. Resolve the current operation and add it - // to the sub-expression result. - result += sign * n; - - // Pop off the sign. If we had a NEGATIVE sign when opening the brace, the entire sub-expression result needs - // to be negated. That is, '-(1+2)' would require us to do result *= -1. - result *= stack.pop()!; - - // Pop off the original result and add it to the sub-expression result. - result += stack.pop()!; - - // Now reset the calculator. - sign = 1; - n = 0; - } - } - - return result + sign * n; -} diff --git a/src/stack/key-value-store-nested-transactions.ts b/src/stack/key-value-store-nested-transactions.ts deleted file mode 100644 index 96f4013..0000000 --- a/src/stack/key-value-store-nested-transactions.ts +++ /dev/null @@ -1,103 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Implement a key-value store with the following functionality: -// -// - get(key: string) -// - set(key: string, value: number) -// - delete(key: string) -// - begin() starts a transaction -// - commit() commits a transaction -// - rollback() rolls back a transaction -// -// This structure supports nested transactions, and the topmost transaction should be able to see the values in the -// transactions below it. -// -// NOTE: This is not an official LeetCode question, but it was asked by Cruise and Bloomberg (sometimes with nested -// transactions and sometimes without). -// -// See {@link https://leetcode.com/discuss/interview-question/279913/Bloomberg-or-Onsite-or-Key-Value-Store-with-transactions} -export { KeyValueStore }; - -// SOLUTION: -// -// The key to this problem is to maintain a stack of maps. When you start a transaction, you push a new map onto the -// stack. -// -// To improve runtime performance, we maintain a current transaction context that gets propagated to the top of the -// stack (or thrown away if we rollback). -class KeyValueStore { - private readonly stack: Map[]; - - private readonly map: Map; - - constructor() { - this.stack = []; - this.map = new Map(); - } - - public get(key: string): number | undefined { - if (this.stack.length === 0) { - return this.map.get(key); - } - - let frame = this.stack.length - 1; - while (frame >= 0) { - if (this.stack[frame].has(key)) { - return this.stack[frame].get(key) ?? undefined; - } - - frame--; - } - - return undefined; - } - - public set(key: string, value: number): void { - if (this.stack.length === 0) { - this.map.set(key, value); - return; - } - - this.stack[this.stack.length - 1].set(key, value); - } - - public delete(key: string): void { - if (this.stack.length === 0) { - this.map.delete(key); - return; - } - - this.stack[this.stack.length - 1].set(key, null); - } - - public begin(): void { - this.stack.push(new Map()); - } - - public commit() { - if (this.stack.length === 0) { - return; - } - - const target = this.stack.length === 1 ? this.map : this.stack[this.stack.length - 2]; - const source = this.stack.pop()!; - - for (const [key, value] of source) { - if (value === null) { - target.delete(key); - continue; - } - - target.set(key, value); - } - } - - public rollback() { - if (this.stack.length === 0) { - return; - } - - const map = this.stack.pop()!; - map.clear(); - } -} diff --git a/src/stack/longest-absolute-file-path.ts b/src/stack/longest-absolute-file-path.ts deleted file mode 100644 index ebf0716..0000000 --- a/src/stack/longest-absolute-file-path.ts +++ /dev/null @@ -1,93 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Suppose we have a file system that stores both files and directories. An example of one system is represented in the -// following picture: -// -// Here, we have `dir` as the only directory in the root. `dir` contains two subdirectories, `subdir1` and `subdir2`. -// `subdir1` contains a file `file1.ext` and subdirectory `subsubdir1`. `subdir2` contains a subdirectory `subsubdir2`, -// which contains a file `file2.ext`. -// -// In text form, it looks like this (with ⟶ representing the tab character): -// -// dir -// ⟶ subdir1 -// ⟶ ⟶ file1.ext -// ⟶ ⟶ subsubdir1 -// ⟶ subdir2 -// ⟶ ⟶ subsubdir2 -// ⟶ ⟶ ⟶ file2.ext -// -// If we were to write this representation in code, it will look like this: -// -// - "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext". -// -// Note that the '\n' and '\t' are the new-line and tab characters. -// -// Every file and directory has a unique absolute path in the file system, which is the order of directories that must -// be opened to reach the file/directory itself, all concatenated by '/'s. Using the above example, the absolute path -// to file2.ext is "dir/subdir2/subsubdir2/file2.ext". Each directory name consists of letters, digits, and/or spaces. -// Each file name is of the form name.extension, where name and extension consist of letters, digits, and/or spaces. -// -// Given a string input representing the file system in the explained format, return the length of the longest absolute -// path to a file in the abstracted file system. If there is no file in the system, return 0. -// -// Note that the testcases are generated such that the file system is valid and no file or directory name has length 0. -// -// See {@link https://leetcode.com/problems/longest-absolute-file-path/} -export { lengthLongestPath }; - -// SOLUTION: -// -// A solution can be efficient by using a stack to keep track of directory depth. However, this only works if the -// input does not contain duplicate sub directories, and all of the files appear right after the subdirectory they are -// in. If it turns out the input can vary such that files and subdirectories appear out of order, or the -// subdirectories can contain duplicates, we'll have to actually build out a real filesystem structure using a graph -// of FileNode (with strings mapping to subdirectory FileNodes). -// -// In this solution, we will assume that the input is constrained. -function lengthLongestPath(input: string): number { - // This is a stack that will keep track of where we are in the directory structure. We'll assume that every - // directory has a trailing slash (no leading slash). We never calculate directory lengths, so that will never - // mess up our calculation. - const dirs: string[] = []; - - // The current and max file lengths. - let current = 0; - let max = 0; - - const parts = input.split('\n'); - for (const part of parts) { - // This indicates that the part of the text is in some subdirectory; we should locate the subdirectory by popping - // directories off of the stack. This will put us at the correct subdirectory after all the popping. - // - // The number of tabs tells us how many elements should be in the stack; if there are no tabs, the last index is - // -1. If there is 1 tab, the index is 0, and so the number of levels will be the last index + 1. - const levels = part.lastIndexOf('\t') + 1; - if (levels !== -1) { - // Pop directories off the stack until it matches levels. - while (dirs.length > levels) { - const dir = dirs.pop()!; - current -= dir.length; - } - } - - // To get the name of the file, we replace all the tabs with empty string. We can also slice the string starting - // from the original value of levels. - const name = levels === -1 ? part : part.slice(levels); - - // This indicates that the part of the text is a file, so we should calculate the file length. - if (name.includes('.')) { - max = Math.max(max, current + name.length); - continue; - } - - // If it doesn't start with a tab or contain a dot, that means we are looking at a directory, so we should push - // it onto the stack. - // - // Add 1 to the value to account for the trailing slash (there is no leading slash). - dirs.push(`${name}/`); - current += name.length + 1; - } - - return max; -} diff --git a/src/stack/max-chunks-to-make-sorted-ii.ts b/src/stack/max-chunks-to-make-sorted-ii.ts deleted file mode 100644 index 7ba4dec..0000000 --- a/src/stack/max-chunks-to-make-sorted-ii.ts +++ /dev/null @@ -1,61 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer array arr. -// -// We split arr into some number of chunks (i.e., partitions), and individually sort each chunk. After concatenating -// them, the result should equal the sorted array. -// -// Return the largest number of chunks we can make to sort the array. -// -// See {@link https://leetcode.com/problems/max-chunks-to-make-sorted-ii/} -export { maxChunksToSorted }; - -// SOLUTION: -// -// We can use a stack to keep track of chunks. Rather than store the *entire* chunk on the stack, we just store the -// largest element. That's because if we have chunks [1, 3] and [4, 5, 9], it's enough to know that [? to 3] and -// [? to 9] form chunks. -// -// When we encounter a new number, like 10, we make a new chunk (since we want as many chunks as possible). When we -// encounter a new number, like 7, we include it as part of the chunk from [? to 9]. But because we might have seen -// a 7 or 6 or some other number in between earlier, we'll have to go down the stack and prune out numbers we don't -// need anymore. -function maxChunksToSorted(xs: number[]) { - // The element stack[i] is the max element of chunk i. At the end, the length of the stack is the number of chunks - // we can create. - // - // We keep track of the max element of chunks, because the smallest element of the next chunk needs to be greater - // than the maximum element of the current chunk. - const stack = [xs[0]]; - - // Start iteration at 1 because you already pushed the first element onto the stack. - for (let i = 1; i < xs.length; i++) { - const x = xs[i]; - - // If this element is larger than top of stack, then xs[i] is part of a new chunk. Therefore, push it onto the - // stack and begin the new chunk. - // - // Conceptually, if you have chunks [1], [3], [5] then you see a [9], then this starts a new chunk. - if (x > stack[stack.length - 1]) { - stack.push(x); - continue; - } - - // Otherwise, xs[i] is part of the chunk which is made up of all chunks where stack[j] > xs[i]. Pop these chunks - // off the stack so you can "merge" them. - // - // Conceptually, if you have chunks [1], [3], [5], [9], then you see a [4], then you can't begin a new chunk. - // Instead, you know that [4, 5, 9] constitute a chunk. However, we don't need to store all numbers [4, 5, 9] in - // the stack; we only need to store the largest element in the chunk [9]. - // - // Therefore, go down the stack and pop off elements in the stack we don't want. - const max = stack[stack.length - 1]; - while (x < stack[stack.length - 1]) { - stack.pop(); - } - - stack.push(max); - } - - return stack.length; -} diff --git a/src/stack/min-stack.ts b/src/stack/min-stack.ts deleted file mode 100644 index 45ea0c8..0000000 --- a/src/stack/min-stack.ts +++ /dev/null @@ -1,60 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. -// -// Implement the MinStack class: -// -// MinStack() initializes the stack object. -// void push(int val) pushes the element val onto the stack. -// void pop() removes the element on the top of the stack. -// int top() gets the top element of the stack. -// int getMin() retrieves the minimum element in the stack. -// -// You must implement a solution with O(1) time complexity for each function. -// -// See {@link https://leetcode.com/problems/min-stack/} -export { MinStack }; - -// SOLUTION: -class MinStack { - private readonly stack: number[]; - - private readonly min: number[]; - - constructor() { - this.stack = []; - - // This stack will contain the same number of elements as the underlying stack, but each element pushed onto it is - // the minimum value element at the time. - this.min = []; - } - - push(x: number): void { - this.stack.push(x); - - if (this.min.length === 0) { - this.min.push(x); - return; - } - - const smallest = Math.min(x, this.getMin()); - this.min.push(smallest); - } - - pop(): void { - this.stack.pop(); - this.min.pop(); - } - - top(): number { - return this.stack[this.stack.length - 1]; - } - - getMin(): number { - if (this.min.length === 0) { - return -Infinity; - } - - return this.min[this.min.length - 1]; - } -} diff --git a/src/stack/minimum-add-to-make-valid-parentheses.ts b/src/stack/minimum-add-to-make-valid-parentheses.ts deleted file mode 100644 index 47f21fc..0000000 --- a/src/stack/minimum-add-to-make-valid-parentheses.ts +++ /dev/null @@ -1,69 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// A parentheses string is valid if and only if: -// -// - It is the empty string, -// - It can be written as AB (A concatenated with B), where A and B are valid strings, or -// - It can be written as (A), where A is a valid string. -// - You are given a parentheses string s. In one move, you can insert a parenthesis at any position of the string. -// -// For example, if s = "()))", you can insert an opening parenthesis to be "(()))" or a closing parenthesis to be -// "())))". -// -// Return the minimum number of moves required to make s valid. -// -// @see {@link https://leetcode.com/problems/minimum-add-to-make-parentheses-valid/} -export { minAddToMakeValid }; - -// SOLUTION: -// -// One way to do this is to use a stack and push open braces onto it. Then, when we encounter a closing brace, we can -// pop it off. If we don't have closing braces left, then increment opensRequired. At the end, count up the number of -// elements still left on the stack (the unmatched closing braces), and add that to the number of opening braces tallied -// up. -// -// However! Notice that we don't even need a stack for this. We just have to keep track of any open and close braces, -// which can be done with a simple counter for each variable. -// -// COMPLEXITY: -// -// Runs in O(n) time, where n is the length of the string. Runs in O(1) space because we are only using a few extra -// variables. -function minAddToMakeValid(s: string): number { - // Every time we see '(', increment this counter. Every time we see ')', decrement this counter, making it similar to - // our stack without using one. - // - // Likewise, at the end, if this variable isn't 0, then it's the same as the situation where the stack still has - // elements in it, which indicates some number of unmatched closing braces. - let closesRequired = 0; - - // If we encounter a ')' and we don't have a matching '(' (aka the stack is empty), then we have to insert an opening - // brace later. - let opensRequired = 0; - - for (const c of s) { - // If we encounter an opening brace, we'll need to require that we close it eventually. - if (c === '(') { - closesRequired++; - continue; - } - // If we encounter a closing brace, there are two possibilities: - // - // 1. We already have an opening brace to match it, which means we can decrement the number of closing braces we - // required. - // 2. We don't have an opening brace to match it, which means we have to insert an opening brace later. - else if (c === ')') { - // This is case 1; we encountered '(' earlier, which caused us to increment closesRequired. But we encountered - // a closing brace, so we can decrement the number of closing braces we need to insert. - if (closesRequired > 0) { - closesRequired--; - } - // This is case 2; we don't have an opening brace to match it, so we have to remember to insert one. - else { - opensRequired++; - } - } - } - - return closesRequired + opensRequired; -} diff --git a/src/stack/minimum-remove-to-make-valid-parentheses.ts b/src/stack/minimum-remove-to-make-valid-parentheses.ts deleted file mode 100644 index 9ccc35f..0000000 --- a/src/stack/minimum-remove-to-make-valid-parentheses.ts +++ /dev/null @@ -1,97 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a string s of '(' , ')' and lowercase English characters. -// -// Your task is to remove the minimum number of parentheses ( '(' or ')', in any positions ) so that the resulting -// parentheses string is valid and return any valid string. -// -// Formally, a parentheses string is valid if and only if: -// -// - It is the empty string, contains only lowercase characters, or -// - It can be written as AB (A concatenated with B), where A and B are valid strings, or -// - It can be written as (A), where A is a valid string. -// -// See {@link https://leetcode.com/problems/minimum-remove-to-make-valid-parentheses/} -export { minRemoveToMakeValid }; - -// SOLUTION: -// -// Imagine if we were just asked to validate the parentheses. This can be done with a stack pretty easily. In this -// problem, we can push open parens onto the stack, then pop off close parens when we encounter them. If we ever -// encounter a close parens while the stack is empty, OR if we have remaining open parens on the stack, then we know -// the parentheses are invalid. -// -// Here, though, we are asked to remove invalid parentheses. This means that instead of failing immediately, every time -// we encounter a close without an open we need to mark it for removal. In addition, any open parens left on the stack -// at the end should also be marked for removal. -// -// Once we have indices that are marked for removal, we'll go through the string again, and copy it to a new string, but -// avoid any indices that are marked for removal. -// -// COMPLEXITY: -// -// Time complexity is O(n) for each pass through the string. Two passes means O(n) overall. -// -// Space complexity is O(n) because we are using the stack (which is size of the string) and the removable set, which -// is also size of the string. -function minRemoveToMakeValid(s: string): string { - type Index = number; - - // For the validate parens problem, we'll have a stack of '(' strings. However, notice that in that case we never - // push any ')' onto the stack; as soon as ')' we pop the item off the stack or we fail. That is, in the validation - // problem, we only ever have '(' characters on the stack. - // - // Therefore we can reclaim that space by pushing the index of the '(' character onto the stack. It is not necessary - // to declare both: - // - // const stack: string[] = []; - // const opens = new Set(); - // - // One array data structure is enough to handle both. Note that having a set would cause problems because we'd at - // some point need to pop the last open paren off the stack, and we'd have to search the set for it (or convert the - // set into an array). - const stack: Index[] = []; - - // These are the indexes we are meant to remove at the end. - const removable = new Set(); - - // Do a first pass to check the string for parens that you want to remove. - for (let i = 0; i < s.length; i++) { - const c = s[i]; - - // If we encounter open paren, just push it onto the stack. - if (c === '(') { - stack.push(i); - continue; - } - - // If we encounter a non-close paren, just skip it. It's not relevant for the removal process. - if (c !== ')') { - continue; - } - - // If we have a matched close paren, pop the associated open from the stack and just continue. - if (stack.length !== 0) { - stack.pop(); - continue; - } - - // Uh oh! At this point, we have an unmatched close paren, so mark this index for removal. - removable.add(i); - } - - // It's possible we are left with some unmatched open parens, so mark them for removal as well. - stack.forEach(i => removable.add(i)); - - // Construct the result string by skipping marked indices. - let result = ''; - for (let i = 0; i < s.length; i++) { - if (removable.has(i)) { - continue; - } - - result += s[i]; - } - - return result; -} diff --git a/src/stack/simplify-path.ts b/src/stack/simplify-path.ts deleted file mode 100644 index c34c5c2..0000000 --- a/src/stack/simplify-path.ts +++ /dev/null @@ -1,67 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an absolute path for a Unix-style file system, which always begins with a slash '/'. Your task is to -// transform this absolute path into its simplified canonical path. -// -// The rules of a Unix-style file system are as follows: -// -// - A single period '.' represents the current directory. -// - A double period '..' represents the previous/parent directory. -// - Multiple consecutive slashes such as '//' and '///' are treated as a single slash '/'. -// - Any sequence of periods that does not match the rules above should be treated as a valid directory or file name. -// For example, '...' and '....' are valid directory or file names. -// -// The simplified canonical path should follow these rules: -// -// - The path must start with a single slash '/'. -// - Directories within the path must be separated by exactly one slash '/'. -// - The path must not end with a slash '/', unless it is the root directory. -// - The path must not have any single or double periods ('.' and '..') used to denote current or parent directories. -// -// Return the simplified canonical path. -// -// See {@link https://leetcode.com/problems/simplify-path/description} -export { simplifyPath }; - -// SOLUTION: -// -// Just iterate through the path and use a stack to keep track of directories. The only one we need to be careful with -// is the '..' name; this means we need to pop the last directory from the stack to "go up" one level. -// -// COMPLEXITY: -// -// Splitting the path, traversing segments, and joining the path are all O(n) time complexity. Where n is the length of -// the input string. -// -// The space complexity is O(n) to store the string segments on the stack. Worst case we have to store the length of -// the string. -function simplifyPath(path: string): string { - const stack: string[] = []; - const names = path.split('/'); - - for (const name of names) { - // Presumably these can just get ignored and thrown away. - if (name === '' || name === '.') { - continue; - } - - // If we are going up a level, we need to pop the last directory from the stack. - // - // Wait, what happens if we are already at the root directory and we can't go up a directory anymore? The - // instructions are not clear, but we definitely do get inputs like '/../' and the result should be '/', indicating - // that the desired behavior is to do nothing. - if (name === '..') { - // Technically we don't actually need this check; stack.pop() does nothing if the array is empty. - if (stack.length > 0) { - stack.pop(); - } - - continue; - } - - // Otherwise we should just push the directory onto the stack as normal. - stack.push(name); - } - - return '/' + stack.join('/'); -} diff --git a/src/stack/valid-number.ts b/src/stack/valid-number.ts deleted file mode 100644 index 81102dc..0000000 --- a/src/stack/valid-number.ts +++ /dev/null @@ -1,133 +0,0 @@ -// DIFFICULTY: HARD -// -// A valid number can be split up into these components (in order): -// -// A decimal number or an integer. -// (Optional) An 'e' or 'E', followed by an integer. -// A decimal number can be split up into these components (in order): -// -// (Optional) A sign character (either '+' or '-'). -// One of the following formats: -// One or more digits, followed by a dot '.'. -// One or more digits, followed by a dot '.', followed by one or more digits. -// A dot '.', followed by one or more digits. -// An integer can be split up into these components (in order): -// -// (Optional) A sign character (either '+' or '-'). -// One or more digits. -// For example, all the following are valid numbers: ["2", "0089", "-0.1", "+3.14", "4.", "-.9", "2e10", "-90E3", "3e+7", "+6e-1", "53.5e93", "-123.456e789"], while the following are not valid numbers: ["abc", "1a", "1e", "e3", "99e2.5", "--6", "-+3", "95a54e53"]. -// -// Given a string s, return true if s is a valid number. -// -// See {@link https://leetcode.com/problems/valid-number/} -export { isNumber }; - -// SOLUTION: -// -// I don't know that this is a particularly hard problem, but it is a bit tricky to get all the edge cases right. It -// doesn't seem like a particularly fair problem to ask in a 45m interview though. -function isNumber(text: string): boolean { - // Defining this set will probably be faster than testing a regex over and over. - const set = new Set(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']); - const state = { - signed: false, - digits: false, - decimal: false, - exp: false - }; - - for (let i = 0; i < text.length; i++) { - const c = text[i]; - const p = i === 0 ? '' : text[i - 1].toLowerCase(); - - switch (c) { - case '+': - case '-': - if (state.exp) { - // After seeing the E symbol, signs are only valid immediately after the E symbol. - if (p !== 'e') { - return false; - } - - // However, signs are not valid if they are the last symbol. - if (i === text.length - 1) { - return false; - } - - state.signed = true; - break; - } - - // Before seeing the E symbol, signs are not valid if they aren't in the first position. - if (i !== 0) { - return false; - } - - // Signs are invalid after we have encountered a decimal. - if (state.decimal) { - return false; - } - - // Signs are invalid if they are the last symbol, and no digits have been seen. For example, 9. is valid, but - // . by itself is not. - if (i === text.length - 1 && !state.digits) { - return false; - } - - state.signed = true; - break; - case 'e': - case 'E': - // If the E symbol is encountered again after already seeing one, the number is automatically invalid. - if (state.exp) { - return false; - } - - // The E symbol may not appear as the first symbol or the last symbol. - if (i === 0 || i === text.length - 1) { - return false; - } - - // The E symbol may not appear just after the sign symbol. For example, 46+e3 is invalid. - if (p === '+' || p === '-') { - return false; - } - - // The E symbol may not appear after a decimal symbol, if no digits have been seen. For example, 9.e3 is - // valid, but .e3 is not valid. - if (p === '.' && !state.digits) { - return false; - } - - state.exp = true; - break; - case '.': - if (state.exp) { - // The decimal symbol may not appear after the E symbol has appeared. - return false; - } - - // The decimal symbol may not appear after the decimal symbol has appeared. - if (state.decimal) { - return false; - } - - // The decimal symbol cannot be the last symbol, unless digits have already been seen. - if (i === text.length - 1 && !state.digits) { - return false; - } - - state.decimal = true; - break; - default: - // Any other character that is not a digit does not constitute a number. - if (!set.has(c)) { - return false; - } - - state.digits = true; - } - } - - return true; -} diff --git a/src/stack/valid-parentheses.ts b/src/stack/valid-parentheses.ts deleted file mode 100644 index 7a5d7b7..0000000 --- a/src/stack/valid-parentheses.ts +++ /dev/null @@ -1,44 +0,0 @@ -// DIFFICULTY: EASY -// -// Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. -// -// An input string is valid if: -// -// Open brackets must be closed by the same type of brackets. -// Open brackets must be closed in the correct order. -// Every close bracket has a corresponding open bracket of the same type. -// -// See {@link https://leetcode.com/problems/valid-parentheses/} -export { isValid }; - -// SOLUTION: -function isValid(text: string) { - const map = new Map(); - map.set('(', ')'); - map.set('{', '}'); - map.set('[', ']'); - - const stack = new Array(); - for (let i = 0; i < text.length; i++) { - const c = text[i]; - - if (c === '(' || c === '{' || c === '[') { - stack.push(c); - continue; - } - - if (c === ')' || c === '}' || c === ']') { - if (stack.length === 0) { - return false; - } - - const open = stack.pop(); - const close = map.get(open || ''); - if (close !== c) { - return false; - } - } - } - - return stack.length === 0; -} diff --git a/src/string/find-the-closest-palindrome.ts b/src/string/find-the-closest-palindrome.ts deleted file mode 100644 index 2eff520..0000000 --- a/src/string/find-the-closest-palindrome.ts +++ /dev/null @@ -1,159 +0,0 @@ -// DIFFICULTY: HARD -// -// Given a string n representing an integer, return the closest integer (not including itself), which is a palindrome. -// If there is a tie, return the smaller one. -// -// The closest is defined as the absolute difference minimized between two integers. -// -// Constraints: 1 <= n.length <= 18 -// -// See {@link https://leetcode.com/problems/find-the-closest-palindrome/} -export { nearestPalindromic }; - -// SOLUTION: -// -// To devise a strategy, first consider a few examples: -// -// "123" => "121" -// "1234" => "1221" -// "1000" => "1001" -// "1" => "0" <-- It's not "1" because we can't return the number itself. -// "999" => "1001" <-- It's not "888" because "1001" is "closer" to "999" by 2. -// "1221" => "1111" <-- It's not "1221" because we can't return the number itself. -// -// Generally, we can either mirror the left side of the number (since that should result in the "closer" number) and -// use that, or we may have to do some incrementing/decrementing to get a part of the number we can mirror. We also -// need to handle edge cases like "1" or "0". -function nearestPalindromic(text: string): string { - // Because the problem states that we could have 18 digit numbers, we may lose precision if we do not use BigInt. - const n = BigInt(text); - - // If it's just a single digit, return that digit minus one (since we can't return the original digit). - if (text.length === 1) { - return String(n - BigInt(1)); - } - - // Split up the string so we have the left prefix (and middle) in case we need it. - const { left, mid } = findPrefix(text); - - // Find possible candidates for the closest palindrome. - const candidates = findCandidates(text, left, mid); - - // Find the actual closest. - const closest = findClosest(candidates, n); - - return closest; -} - -function findPrefix(text: string) { - // Find the left half of the number, which we may need to mirror. If the number has an odd number of digits, we - // don't take the midpoint because we won't want to mirror it anyways. - const left = text.slice(0, text.length / 2); - const mid = text.length % 2 === 0 ? '' : text.charAt(text.length / 2); - return { left, mid }; -} - -function findCandidates(text: string, left: string, mid: string) { - // Generate candidate palindromes; if the original number is already a palindrome, this won't work and we'll have - // to increment or decrement the left side to find the nearest palindrome. - // - // In some cases, we may also have to increment or decrement the middle digit. - const set = new Set(); - [-1, 0, 1].forEach(i => { - const u = BigInt(left); - const v = u + BigInt(i); - const prefix = v.toString(); - const suffix = prefix.split('').reverse().join(''); - set.add(prefix + mid + suffix); - - // There are cases where the middle digit needs to be incremented or decremented as well. For example, take the - // following cases: - // - // "11911" => "11811" - // "10001" => "11111" - // - // To handle these cases we'll vary the middle digit as well. - [-1, 0, 1].forEach(j => { - const updated = BigInt(mid) + BigInt(j); - - // Only add this middle digit if we have a positive value, so we don't end up with "10-11" or something. - if (updated < 0) { - return; - } - - set.add(prefix + updated + suffix); - }); - }); - - // These candidates will work for the vast majority of numbers, but sometimes we'll get edge cases where simply - // incrementing or decrementing won't work. For example: - // - // "101" => "99" - // "99" => "101" - // - // In these situations, just special case them by building special numbers like all 9's or 100...001. - [text.length - 1, text.length, text.length + 1].forEach(size => { - const array = Array(size); - - // Add all 9s. - set.add(array.fill(9).join('')); - - // Add 100..001. - array.fill(0); - array[0] = 1; - array[array.length - 1] = 1; - set.add(array.join('')); - }); - - // Finally prune the candidate list of numbers that don't make sense. - return [...set].filter(candidate => { - if (candidate.startsWith('0')) { - return false; - } - - if (candidate === text) { - return false; - } - - return true; - }); -} - -function findClosest(candidates: string[], n: bigint) { - // Find the closest elements to the original number. Since we need to find the smallest to break a tie, just map - // deltas to their candidates. Note that BigInt is the constructor and bigint is the type. Don't set the type to - // be BigInt or else you won't be able to store stuff in your map. - type Delta = bigint; - type Candidate = bigint; - const map = new Map(); - - // Note that BigInt(Infinity) doesn't work; we'll just set the max value to the number itself. - let lowest = n; - for (let i = 0; i < candidates.length; i++) { - const candidate = BigInt(candidates[i]); - - // Math.abs() is not available for BigInt; we'll have to implement it ourselves. - let delta = n - candidate; - delta = delta > 0 ? delta : -delta; - if (delta < lowest) { - lowest = delta; - } - - const list = map.get(delta) ?? []; - list.push(candidate); - map.set(delta, list); - } - - // If there are multiple closests, then return the smallest. - const list = map.get(lowest) ?? []; - - // BigInt sorting seems to be broken; using .sort() puts 11n in front of 9n. Just do this explicitly instead. - list.sort((a, b) => { - if (a === b) { - return 0; - } - - return a < b ? -1 : 1; - }); - return list[0].toString(); -} diff --git a/src/string/int-to-roman.ts b/src/string/int-to-roman.ts deleted file mode 100644 index a33c5f7..0000000 --- a/src/string/int-to-roman.ts +++ /dev/null @@ -1,93 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. -// -// Symbol Value -// I 1 -// V 5 -// X 10 -// L 50 -// C 100 -// D 500 -// M 1000 -// For example, 2 is written as II in Roman numeral, just two one's added together. 12 is written as XII, which is -// simply X + II. The number 27 is written as XXVII, which is XX + V + II. -// -// Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. -// Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same -// principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: -// -// I can be placed before V (5) and X (10) to make 4 and 9. -// X can be placed before L (50) and C (100) to make 40 and 90. -// C can be placed before D (500) and M (1000) to make 400 and 900. -// Given an integer, convert it to a roman numeral. -// -// See {@link https://leetcode.com/problems/integer-to-roman/} -export { intToRoman }; - -// SOLUTION: -function intToRoman(n: number): string { - function convert(digit: number, ones: string, fives: string, tens: string) { - switch (digit) { - case 1: - case 2: - case 3: - return ones.repeat(digit); - case 4: - return `${ones}${fives}`; - case 5: - return fives; - case 6: - case 7: - case 8: - return `${fives}${ones.repeat(digit - 5)}`; - case 9: - return `${ones}${tens}`; - case 0: - return ''; - default: - throw new Error('not a digit'); - } - } - - let cs = n.toString(); - // If we have a number like 1, we don't know how many digits remain. We could run two pointers through the array, - // converting the least significant digits first, or we could just pad the number so that the hundreds and thousands - // logic will skip over the few digits with zeroes. Choosing the latter here. - if (cs.length < 4) { - cs = '0'.repeat(4 - cs.length) + cs; - } - - const result = []; - for (let i = 0; i < cs.length; i++) { - const digit = Number.parseInt(cs[i], 10 /* radix */); - if (i === 0) { - // We can't have any numbers over 3999, so we don't need to specify any numerals for fives or tens. - const roman = convert(digit, 'M', '', ''); - result.push(roman); - continue; - } - - if (i === 1) { - const roman = convert(digit, 'C', 'D', 'M'); - result.push(roman); - continue; - } - - if (i === 2) { - const roman = convert(digit, 'X', 'L', 'C'); - result.push(roman); - continue; - } - - if (i === 3) { - const roman = convert(digit, 'I', 'V', 'X'); - result.push(roman); - continue; - } - - throw new Error('too many digits'); - } - - return result.join(''); -} diff --git a/src/string/integer-to-english-words.ts b/src/string/integer-to-english-words.ts deleted file mode 100644 index 6db349c..0000000 --- a/src/string/integer-to-english-words.ts +++ /dev/null @@ -1,152 +0,0 @@ -// DIFFICULTY: HARD -// -// Convert a non-negative integer num to its English words representation. -// -// See {@link https://leetcode.com/problems/integer-to-english-words/} -export { numberToWords }; - -// SOLUTION: -// -// To get an idea of how to approach this problem, first start with a few examples, as there are going to be a lot of -// edges cases: -// -// 1 => One -// 10 => Ten -// 11 => Eleven -// 19 => Nineteen -// 100 => One Hundred -// 101 => One Hundred One -// 1_000 => One Thousand -// 1_001 => One Thousand One -// 100_000 => One Hundred Thousand -// 1_000_000 => One Million -// 1_000_000_000 => One Billion -// -// A few notes: -// -// - 2^32 is the limit, and it is approximately 4 billion, so we don't need to describe numbers over 4 billion. -// - The word "And" isn't required between words, so "One Hundred One", not "One Hundred And One". -// - The numbers 1-20 need to be handled in a special way due to how English works. -// - It's easier to deal with numbers 3 digits at a time. -// -// When looking at a number XYZ_123_UWV, the 123 part will always be translated into "One Hundred Twenty Three", and -// after that, we will append "Thousand", "Million", or "Billion". For this reason, it's best to split the number -// into segments of 3, translate that part directly, and then append the correct word afterwards. -function numberToWords(num: number): string { - if (num === 0) { - return 'Zero'; - } - - // Each index represents how to say a number in English, if it applies. - const under20 = [ - '', // Zero missing as a special case; we will never say it in a multi word number phrase. - 'One', - 'Two', - 'Three', - 'Four', - 'Five', - 'Six', - 'Seven', - 'Eight', - 'Nine', - 'Ten', - 'Eleven', - 'Twelve', - 'Thirteen', - 'Fourteen', - 'Fifteen', - 'Sixteen', - 'Seventeen', - 'Eighteen', - 'Nineteen' - ]; - - // Each index represents how we'd say a tens digit in English. - const over20 = [ - '', // We don't say Zero, - '', // We don't say Ten One; we say Eleven. This is covered by the under20 array. - 'Twenty', - 'Thirty', - 'Forty', - 'Fifty', - 'Sixty', - 'Seventy', - 'Eighty', - 'Ninety' - ]; - - // Each index represents how we'd say increasingly larger 3 segment chunks of numbers. For example: - // - // 111 => One Hundred Eleven - // 111_000 => One Hundred Eleven Thousand - // 111_000_000 => One Hundred Eleven Million - // 111_000_000_000 => One Hundred Eleven Billion - const thousands = [ - '', // For numbers under 1000, we don't say anything. - 'Thousand', - 'Million', - 'Billion' - ]; - - // A few aliases for convenience. - const ones = under20; - const tens = over20; - - // Converts a 3 digit number into a phrase. For example, 123 => One Hundred Twenty Three. We can then append the - // word Million, Billion, or Thousand afterwards. - function toWordInternal(n: number): string { - // If we have a number like 100_000_001, then the middle segment of 000 does not get translated to "Zero", but - // instead remains blank. - if (n === 0) { - return ''; - } - - // The numbers under twenty need to be handled in a special way due to how English works. - if (n < 20) { - return under20[n]; - } - - // For a number like 11, we take the tens digit and find out how to say it, then append - if (n < 100) { - const firstDigit = Math.floor(n / 10); - const secondDigit = n % 10; - const phrase = `${tens[firstDigit]} ${ones[secondDigit]}`; - return phrase.trim(); - } - - // Finally, for a three digit number, take the first digit and append "Hundred", then figure out how to say the - // 2 digit number. - const firstDigit = Math.floor(n / 100); - const last2Digits = n % 100; - const firstPart = `${ones[firstDigit]} Hundred`; - const secondPart = toWordInternal(last2Digits); - const phrase = `${firstPart} ${secondPart}`; - return phrase.trim(); - } - - let result = ''; - - // This will keep track of how many 3 digit segments we've seen so we can add Thousands, Millions, or Billions. - let i = 0; - - // Handle the number 3 digits at a time; each 3 digit segment represents an increase to Thousands, Millions, or - // Billions. - while (num > 0) { - // Get the last 3 digits and figure out how to say them. Once we do that, figure out if we should append nothing, - // Thousand, Million, or Billion. - if (num % 1000 !== 0) { - const last3 = num % 1000; - const phrase = `${toWordInternal(last3)} ${thousands[i]}`; - - // Add the created phrase to the front of the result, since we are dealing with the last 3 (least significant) - // digits each time. - result = `${phrase} ${result}`; - } - - // Now truncate the number by 3 digits and repeat. - num = Math.floor(num / 1000); - i++; - } - - return result.trim(); -} diff --git a/src/string/letter-combinations-phone-number.ts b/src/string/letter-combinations-phone-number.ts deleted file mode 100644 index 713156a..0000000 --- a/src/string/letter-combinations-phone-number.ts +++ /dev/null @@ -1,59 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could -// represent. Return the answer in any order. -// -// A mapping of digits to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any -// letters. -// -// See {@link https://leetcode.com/problems/letter-combinations-of-a-phone-number/} -export { letterCombinations }; - -// SOLUTION: -function letterCombinations(digits: string): string[] { - const map = new Map(); - map.set('1', []); - map.set('2', ['a', 'b', 'c']); - map.set('3', ['d', 'e', 'f']); - map.set('4', ['g', 'h', 'i']); - map.set('5', ['j', 'k', 'l']); - map.set('6', ['m', 'n', 'o']); - map.set('7', ['p', 'q', 'r', 's']); - map.set('8', ['t', 'u', 'v']); - map.set('9', ['w', 'x', 'y', 'z']); - map.set('0', [' ']); - - // This can be done recursively; first generate all combinations for string of digits minus the head digit. Then add - // letters to each combination of the rest. - function generate(text: string): string[] { - if (text.length === 0) { - return []; - } - - if (text.length === 1) { - return map.get(text)!; - } - - // Separate the first digit and the rest of the digits. - const first = text[0]; - const rest = text.slice(1); - - // Find all combinations of words possible just from the rest of the digits. - const combinations = generate(rest); - - // Now insert the letters possible from the first digit in front of each combination. - const letters = map.get(first)!; - const result = []; - for (let i = 0; i < combinations.length; i++) { - const combination = combinations[i]; - for (let j = 0; j < letters.length; j++) { - const letter = letters[j]; - result.push(letter + combination); - } - } - - return result; - } - - return generate(digits); -} diff --git a/src/string/longest-palindromic-substring.ts b/src/string/longest-palindromic-substring.ts deleted file mode 100644 index 82924fe..0000000 --- a/src/string/longest-palindromic-substring.ts +++ /dev/null @@ -1,81 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a string s, return the longest palindromic substring in s. -// -// See {@link https://leetcode.com/problems/longest-palindromic-substring/} -export { longestPalindrome }; - -// SOLUTION: -// -// The naive solution is to iterate through each character, then treat that character as the center of a palindrome, -// expanding outwards to check if the characters match. -// -// Note that for each character, we have to do two checks: -// -// 1. Check if there's a palindrome centered at i, for odd length palindromes. -// 2. Check if there's a palindrome centered at i and i + 1, for even length palindromes. -// -// There is a dynamic programming solution that can solve this problem in O(n^2) time and O(n^2) space. -// -// There is a more sophisticated algorithm called Manacher's algorithm that can solve this problem in O(n) time, but -// it's pretty complicated. -// -// COMPLEXITY: -// -// Each checkPalindrome() call is O(n) where n is the length of the string. We call this function twice for each -// character in the string, so the total time complexity is O(n^2). -// -// The space complexity is O(1) because we only use a constant amount of space. -function longestPalindrome(s: string): string { - let start = 0; - let maxLength = 0; - - function checkPalindrome(left: number, right: number) { - let matched = false; - - // Expand the left and right pointers outwards from the center. - while (left >= 0 && right < s.length) { - // If the characters match, expand the pointers. - if (s[left] === s[right]) { - left--; - right++; - matched = true; - } - // Otherwise, break out of the loop. - else { - break; - } - } - - // If we didn't match any characters, then there's no need to update the length of the longest palindrome. - if (!matched) { - return; - } - - // When the while loop ends, s[left] !== s[right], which means they don't match. Adjust them back to a valid - // position. If they didn't match, then there's no need to - left++; - right--; - - // The length of the palindrome we have is bounded by [left, right] inclusive. To get the length of this range, - // we need to do right - left + 1. - const length = right - left + 1; - if (right - left + 1 > maxLength) { - start = left; - maxLength = length; - } - } - - if (s.length === 0) { - return ''; - } - - for (let i = 0; i < s.length; i++) { - // Check if there's a palindrome centered at i. - checkPalindrome(i, i); - // Check if there's a palindrome centered at i and i + 1. - checkPalindrome(i, i + 1); // Even-length palindrome - } - - return s.substring(start, start + maxLength); -} diff --git a/src/string/palindrome-number.ts b/src/string/palindrome-number.ts deleted file mode 100644 index c60635f..0000000 --- a/src/string/palindrome-number.ts +++ /dev/null @@ -1,13 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an integer x, return true if x is a palindrome, and false otherwise. -// -// See {@link https://leetcode.com/problems/palindrome-number/} -export { isPalindrome }; - -// SOLUTION: -function isPalindrome(x: number): boolean { - const s = x.toString(); - const r = s.split('').reverse().join(''); - return s === r; -} diff --git a/src/string/remove-all-adjacent-duplicates-i.ts b/src/string/remove-all-adjacent-duplicates-i.ts deleted file mode 100644 index a5d4832..0000000 --- a/src/string/remove-all-adjacent-duplicates-i.ts +++ /dev/null @@ -1,30 +0,0 @@ -// DIFFICULTY: EASY -// -// You are given a string s consisting of lowercase English letters. A duplicate removal consists of choosing two -// adjacent and equal letters and removing them. -// -// We repeatedly make duplicate removals on s until we no longer can. -// -// Return the final string after all such duplicate removals have been made. It can be proven that the answer is unique. -// -// See {@link https://leetcode.com/problems/remove-all-adjacent-duplicates-in-string/} -export { removeDuplicates }; - -// SOLUTION: -function removeDuplicates(s: string) { - const stack: string[] = []; - - for (const c of s) { - // If the top of the stack has a dupe, pop it off and ignore this character. - if (stack.length !== 0 && c === stack[stack.length - 1]) { - stack.pop(); - continue; - } - - // Otherwise push the character on the stack. - stack.push(c); - } - - // We'll end up with all the non-dupes. - return stack.join(''); -} diff --git a/src/string/remove-all-adjacent-duplicates-ii.ts b/src/string/remove-all-adjacent-duplicates-ii.ts deleted file mode 100644 index 60769df..0000000 --- a/src/string/remove-all-adjacent-duplicates-ii.ts +++ /dev/null @@ -1,63 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given a string s and an integer k, a k duplicate removal consists of choosing k adjacent and equal letters -// from s and removing them, causing the left and the right side of the deleted substring to concatenate together. -// -// We repeatedly make k duplicate removals on s until we no longer can. -// -// Return the final string after all such duplicate removals have been made. It is guaranteed that the answer is unique. -// -// See {@link https://leetcode.com/problems/remove-all-adjacent-duplicates-in-string-ii/} -export { removeDuplicates }; - -// SOLUTION: -// -// In contrast with remove duplicates i, we need to keep track of not just the top element of the stack, but rather if -// the top k elements constitute dupes. Once we remove k elements, it's possible the new stack now has k more -// elements to remove. -// -// To account for this, we cannot simply count elements and remove when we see k, resetting out counter to 1. Instead -// we will have to store, at each stack frame, the elements *and* what the counter was. -function removeDuplicates(s: string, k: number) { - interface Character { - value: string; - count: number; - } - - const stack: Character[] = []; - let count = 1; - - for (const c of s) { - // If the top of the stack and the current character match, increase our count. If they don't match, reset our - // count so that it is 1 (since we always at least see 1 match; the character itself). - if (stack.length !== 0 && c === stack[stack.length - 1].value) { - count++; - } else { - count = 1; - } - - stack.push({ - value: c, - count - }); - - // Only if we've seen k dupes, pop them all. Here, we cannot reset our counter back to 1; we have to reset the - // counter back to what it was when we first pushed the top element onto the stack. - if (count === k) { - for (let i = 0; i < k; i++) { - stack.pop(); - } - - // Reset our count. If we have no more stack elements, we can reset to 1. However, if we have stack elements, - // we have to reset to what it was previously. - if (stack.length === 0) { - count = 1; - } else { - count = stack[stack.length - 1].count; - } - } - } - - // We'll end up with all the non-dupes. - return stack.map(c => c.value).join(''); -} diff --git a/src/string/reverse-words-in-a-string.ts b/src/string/reverse-words-in-a-string.ts deleted file mode 100644 index 66aa6d2..0000000 --- a/src/string/reverse-words-in-a-string.ts +++ /dev/null @@ -1,61 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an input string s, reverse the order of the words. -// -// A word is defined as a sequence of non-space characters. The words in s will be separated by at least one space. -// -// Return a string of the words in reverse order concatenated by a single space. -// -// Note that s may contain leading or trailing spaces or multiple spaces between two words. The returned string should -// only have a single space separating the words. Do not include any extra spaces. -// -// See {@link https://leetcode.com/problems/reverse-words-in-a-string/} -export { reverseWords }; - -// SOLUTION: -function reverseWords(s: string): string { - let reversed = ''; - let word = ''; - let i = s.length - 1; - - while (i >= 0) { - // If we see a non space character, we are going to continue forming our word. - if (s[i] !== ' ') { - // Don't add it to the end; we are looping backwards and getting 'eulb' instead of 'blue'. Instead build the - // string in reverse. - word = s[i] + word; - i--; - continue; - } - - // If we see a space character, we will add our word to the reversed result, but only if we actually have a word - // to form. - if (word.length !== 0) { - // If we've already added words to the reversed sentence, separate this one with a space. - if (reversed.length > 0) { - reversed += ' '; - } - - // Otherwise just add our word. - reversed += word; - - // Reset our word and continue; - word = ''; - i--; - continue; - } - - // Otherwise if there's a space and we have no word, just continue. - i--; - } - - // Now add the last word to the list, if there is one. - if (word.length !== 0) { - if (reversed.length > 0) { - reversed += ' '; - } - reversed += word; - } - - return reversed; -} diff --git a/src/string/roman-to-int.ts b/src/string/roman-to-int.ts deleted file mode 100644 index 0170e77..0000000 --- a/src/string/roman-to-int.ts +++ /dev/null @@ -1,59 +0,0 @@ -// DIFFICULTY: EASY -// -// Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. -// -// Symbol Value -// I 1 -// V 5 -// X 10 -// L 50 -// C 100 -// D 500 -// M 1000 -// -// For example, 2 is written as II in Roman numeral, just two ones added together. 12 is written as XII, which is -// simply X + II. The number 27 is written as XXVII, which is XX + V + II. -// -// Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. -// Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same -// principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: -// -// - I can be placed before V (5) and X (10) to make 4 and 9. -// - X can be placed before L (50) and C (100) to make 40 and 90. -// - C can be placed before D (500) and M (1000) to make 400 and 900. -// -// Given a roman numeral, convert it to an integer. -// -// See {@link https://leetcode.com/problems/roman-to-integer/} -export { romanToInt }; - -// SOLUTION: -function romanToInt(s: string): number { - const map = new Map([ - ['I', 1], - ['V', 5], - ['X', 10], - ['L', 50], - ['C', 100], - ['D', 500], - ['M', 1000] - ]); - - let total = 0; - for (let i = 0; i < s.length; i++) { - const current = map.get(s[i])!; - const next = i + 1 < s.length ? map.get(s[i + 1]) : undefined; - - // In this case, we have a situation like IV (4) or IX (9), where the current number is less than the next number. - // That means we want to do a subtraction. - if (next !== undefined && current < next) { - total -= current; - } - // Other cases like VI (6) or XI (11), we just want to add. - else { - total += current; - } - } - - return total; -} diff --git a/src/string/time-needed-to-rearrange-binary-string.ts b/src/string/time-needed-to-rearrange-binary-string.ts deleted file mode 100644 index deaec15..0000000 --- a/src/string/time-needed-to-rearrange-binary-string.ts +++ /dev/null @@ -1,68 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given a binary string s. In one second, all occurrences of "01" are simultaneously replaced with "10". This -// process repeats until no occurrences of "01" exist. -// -// Return the number of seconds needed to complete this process. -// -// See {@link https://leetcode.com/problems/time-needed-to-rearrange-a-binary-string/} -export { secondsToRemoveOccurrences }; - -// SOLUTION: -// -// Rather than simulate the replacement process, we can think about how the replacement affects a string. In a simple -// case, "01" -> "10". However, a newly created "10" can create more replacements. Here's an example: -// -// "0001" -> "0010" -> "0100" -> "1000" (three 0's and three seconds to flip) -// -// Here' the "10" ripples to the left. The number of times it will "ripple" is determined by the number of 0's to -// the left of the 1. Here there are three 0's before the 1, so it will take 3 seconds to complete the replacement. -// -// Here's a more complicated example: -// -// "001001" -> "010010" -> "100100" -> "101000" -> "110000" (four 0's and four seconds to flip) -// -// Notice that the first "001" only takes two seconds to transform into "100", but we are not done yet. The second -// "001" took 4 seconds to replace, which is the two 0's in front of "001", but also the two 0's that were in front -// of the first "001". In essence, the first "001" -> "100" transferred the 0's to the right, which the second "001" -// has to deal with. -// -// This means the number of seconds it takes to replace the rightmost "01" is equal to the number of 0's that appeared -// to the left of it, in most cases. However, this won't work for a case like this: -// -// "011" -> "101" -> "110" (one 0 and two seconds to flip) -// -// Here, there is one 0 but two seconds needed to elapse. This is because the second 1 cannot flip until the first -// "01" has flipped. We don't have this issue if we space out 0's and 1's: -// -// "101" -> "110" (one 0 and one second to flip) -// "0101" -> "1010" -> "1100" (two 0's and two seconds to flip) -// -// This means that each 1 we see requires at LEAST one second to flip. -function secondsToRemoveOccurrences(s: string): number { - let count = 0; - let seconds = 0; - - for (let i = 0; i < s.length; i++) { - // This counts how many 0's we have seen so far. - if (s[i] === '0') { - count++; - continue; - } - - // If we see a 1 and we haven't seen any 0's before this, we don't need to update the number of seconds. That is, - // "1111..." will not result in increasing the number of seconds. - if (count === 0) { - continue; - } - - // Here we have seen a 1, and there are some number of zeroes before it. Keep in mind that in most cases, we can - // calculate that count of zeroes = count of seconds. However, each 1 requires at LEAST one second to flip. - // - // To handle a case of "011" -> "101" -> "110" which has one 0, but requires TWO seconds to flip, we should - // compare: seconds + 1 (at LEAST one second to flip) versus the count of zeroes. - seconds = Math.max(seconds + 1, count); - } - - return seconds; -} diff --git a/src/string/valid-word-abbreviation.ts b/src/string/valid-word-abbreviation.ts deleted file mode 100644 index ef80968..0000000 --- a/src/string/valid-word-abbreviation.ts +++ /dev/null @@ -1,78 +0,0 @@ -// DIFFICULTY: EASY -// -// A string can be abbreviated by replacing any number of non-adjacent, non-empty substrings with their lengths. The -// lengths should not have leading zeros. -// -// For example, a string such as "substitution" could be abbreviated as (but not limited to): -// -// "s10n" ("s ubstitutio n") -// "sub4u4" ("sub stit u tion") -// "12" ("substitution") -// "su3i1u2on" ("su bst i t u ti on") -// "substitution" (no substrings replaced) -// The following are not valid abbreviations: -// -// "s55n" ("s ubsti tutio n", the replaced substrings are adjacent) -// "s010n" (has leading zeros) -// "s0ubstitution" (replaces an empty substring) -// Given a string word and an abbreviation abbr, return whether the string matches the given abbreviation. -// -// A substring is a contiguous non-empty sequence of characters within a string. -// -// See {@link https://leetcode.com/problems/valid-word-abbreviation/} -export { validWordAbbreviation }; - -// SOLUTION: -// -// To do this, we'll use two pointers, but not to iterate through a single string. Instead, each pointer iterates -// through its own string. If we see an numerical abbreviation, we'll advance through the other string a the -// corresponding number of characters. Because of that, this problem isn't categorized as a two-pointer problem. -// -// At each stage of advancement we'll check if the characters match. -function validWordAbbreviation(word: string, abbr: string): boolean { - function isDigit(c: string) { - // Number(x) will stop on invalid characters anywhere in the string, but Number.parseInt(x) will keep going until - // it finds a non-digit. We expect the entire input to be a number. - return !Number.isNaN(Number(c)); - } - - let i = 0; - let j = 0; - while (i < word.length && j < abbr.length) { - // Substitutions with leading zeroes (or replace an empty substring) are not valid. The word string can only - // contain non-numeric characters, so this can never match. - if (abbr[j] === '0') { - return false; - } - - // If it is a digit, perform the substitution by advancing the word pointer by the number of digits we read. - if (isDigit(abbr[j])) { - let num = 0; - - // We should collect digits into a number, and then do the advancement. - // - // To append to a number, we multiply by 10 and add the new digit. We also could've kept it as a string and then - // converted it afterwards. - while (j < abbr.length && isDigit(abbr[j])) { - num = num * 10 + Number(abbr[j]); - j++; - } - - i += num; - continue; - } - - // If it's not a digit, we check if the characters match. If they don't, it's not a valid substitution. - if (word[i] !== abbr[j]) { - return false; - } - - i++; - j++; - } - - // We need to have advanced PAST the last character in each string for it to be valid. The last character is - // checked on line 55, and the pointers advance one more time afterwards. Hence we should not subtract 1 from - // word.length or abbr.length. - return i === word.length && j === abbr.length; -} diff --git a/src/string/vowel-spellchecker.ts b/src/string/vowel-spellchecker.ts deleted file mode 100644 index 6befa50..0000000 --- a/src/string/vowel-spellchecker.ts +++ /dev/null @@ -1,80 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a wordlist, we want to implement a spellchecker that converts a query word into a correct word. -// -// For a given query word, the spell checker handles two categories of spelling mistakes: -// -// Capitalization: If the query matches a word in the wordlist (case-insensitive), then the query word is returned with -// the same case as the case in the wordlist. -// -// - Example: wordlist = ["yellow"], query = "YellOw": correct = "yellow" -// - Example: wordlist = ["Yellow"], query = "yellow": correct = "Yellow" -// - Example: wordlist = ["yellow"], query = "yellow": correct = "yellow" -// - Vowel Errors: If after replacing the vowels ('a', 'e', 'i', 'o', 'u') of the query word with any vowel -// individually, it matches a word in the wordlist (case-insensitive), then the query word is returned with the same -// case as the match in the wordlist. -// - Example: wordlist = ["YellOw"], query = "yollow": correct = "YellOw" -// - Example: wordlist = ["YellOw"], query = "yeellow": correct = "" (no match) -// - Example: wordlist = ["YellOw"], query = "yllw": correct = "" (no match) -// -// In addition, the spell checker operates under the following precedence rules: -// -// - When the query exactly matches a word in the wordlist (case-sensitive), you should return the same word back. -// - When the query matches a word up to capitlization, you should return the first such match in the wordlist. -// - When the query matches a word up to vowel errors, you should return the first such match in the wordlist. -// - If the query has no matches in the wordlist, you should return the empty string. -// - Given some queries, return a list of words answer, where answer[i] is the correct word for query = queries[i]. -// -// See {@link https://leetcode.com/problems/vowel-spellchecker/} -export { spellchecker }; - -// SOLUTION: -function spellchecker(wordlist: string[], queries: string[]): string[] { - const set = new Set(wordlist); - const caps = new Map(); - const vowels = new Map(); - - // We can normalize all inputs and words to a canonical form, then check if the inputs match the canonical form. - for (const word of wordlist) { - const lowered = word.toLowerCase(); - - // Problem says to only use the first mapping; multiple may be given. So if we have a mapping, ignore the rest. - if (!caps.has(lowered)) { - caps.set(lowered, word); - } - - // Problem is not 100% clear, but it seems we are allowed to change any number of vowels so that the input matches - // the word (not just a single vowel). That means we can just normalize all the vowel inputs to a canonical form - // with vowels replaced. - const voweled = lowered.replace(/[aeiou]/g, '#'); - if (!vowels.has(voweled)) { - vowels.set(voweled, word); - } - } - - const result: string[] = []; - - // Now just canonicalize all the words and check if they match a rule. - for (const query of queries) { - if (set.has(query)) { - result.push(query); - continue; - } - - const lowered = query.toLowerCase(); - if (caps.has(lowered)) { - result.push(caps.get(lowered)!); - continue; - } - - const voweled = lowered.replace(/[aeiou]/g, '#'); - if (vowels.has(voweled)) { - result.push(vowels.get(voweled)!); - continue; - } - - result.push(''); - } - - return result; -} diff --git a/src/tree/binary-tree-level-order-traversal.ts b/src/tree/binary-tree-level-order-traversal.ts deleted file mode 100644 index 5cbdcab..0000000 --- a/src/tree/binary-tree-level-order-traversal.ts +++ /dev/null @@ -1,55 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given the root of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, -// level by level). -// -// See {@link https://leetcode.com/problems/binary-tree-level-order-traversal/} -import { TreeNode } from './common/tree-node'; -export { levelOrder }; - -// SOLUTION: -// -// This is essentially a BFS algorithm from the root node. -// -// COMPLEXITY: -// -// Each node is visited once, so the time complexity is O(n). -function levelOrder(root: TreeNode | null): number[][] { - if (root === null) { - return []; - } - - const result: number[][] = []; - const queue: TreeNode[] = [root]; - - // Consume the nodes in the current level while noting the frontier nodes. The nodes at the current level will be - // recorded, then the frontier nodes are added to the queue. - while (queue.length > 0) { - const level: number[] = []; - const frontier: TreeNode[] = []; - - // Consume all the nodes from the current level and record them. Don't use the queue.length in the for loop as - // we are going to be modifying the queue during the loop. - const size = queue.length; - for (let i = 0; i < size; i++) { - const node = queue.shift()!; - level.push(node.val); - - if (node.left !== null) { - frontier.push(node.left); - } - - if (node.right !== null) { - frontier.push(node.right); - } - } - - // Add the recorded nodes at this level to the result. - result.push(level); - - // Continue processing frontier nodes. - queue.push(...frontier); - } - - return result; -} diff --git a/src/tree/binary-tree-right-side-view.ts b/src/tree/binary-tree-right-side-view.ts deleted file mode 100644 index 340852f..0000000 --- a/src/tree/binary-tree-right-side-view.ts +++ /dev/null @@ -1,38 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given the root of a binary tree, imagine yourself standing on the right side of it, return the values of the nodes -// you can see ordered from top to bottom. -// - -import { TreeNode } from './common/tree-node'; - -// See {@link https://leetcode.com/problems/binary-tree-right-side-view} -export { rightSideView }; - -// SOLUTION: -// -// We can use a simple DFS to traverse the tree, and prioritize the right child over the left child. At each depth, -// we can add the first rightmost node to the result array, since that's the node we'll see when viewing it from the -// right. -function rightSideView(root: TreeNode | null): number[] { - const depths: number[] = []; - - function dfs(node: TreeNode | null, depth: number) { - if (node === null) { - return; - } - - // If the current depth is the lowest depth we've seen, then it's the rightmost node at this depth, because we will - // always prioritize going down the right node. - if (depths.length === depth) { - depths.push(node.val); - } - - // Prioritize going down the right subtree first! - dfs(node.right, depth + 1); - dfs(node.left, depth + 1); - } - - dfs(root, 0); - return depths; -} diff --git a/src/tree/binary-tree-vertical-order-traversal.ts b/src/tree/binary-tree-vertical-order-traversal.ts deleted file mode 100644 index eb57d3e..0000000 --- a/src/tree/binary-tree-vertical-order-traversal.ts +++ /dev/null @@ -1,88 +0,0 @@ -// DIFFICULTY: MEDIUM -// Given the root of a binary tree, return the vertical order traversal of its nodes' values. (i.e., from top to bottom, -// column by column). -// -// If two nodes are in the same row and column, the order should be from left to right. -// -// See {@link https://leetcode.com/problems/binary-tree-vertical-order-traversal/}. -import { TreeNode } from './common/tree-node'; -export { verticalOrder }; - -// SOLUTION: -// -// There is no straightforward vertical order traversal. However, a standard level order traversal can be used to get -// part of the way there. If we assume that the root is at row 0 and column 0, we can do some bookkeeping while we -// traverse the nodes to assign a coordinate to every single node. -// -// While doing the traversal, we can keep track of a Map where each column is mapped to an array of -// nodes in the order they were encountered. By doing a level order traversal (or BFS), we can ensure that nodes in -// the same "row" are encountered from left to right. -// -// COMPLEXITY: -// -// The BFS will run in O(n) time where n is the number of nodes. The result computation at the end will run in O(k) -// time where k is the number of columns. However, you're always guaranteed to have more nodes than columns (or the -// same) so overall time complexity is still O(n). -// -// Space complexity is O(n) because we are storing k columns mapping to lists of nodes, but the overall number of nodes -// stored in the map is at most n. We are also using a queue, which has at most n nodes in it. -function verticalOrder(root: TreeNode | null): number[][] { - type Column = number; - - if (root === null) { - return []; - } - - // Keep a map of column to nodes. Note that we don't actually need to store their row values, because if we visit - // the nodes in the right order, the array will already be sorted by row. The key is to push the left child before - // the right child. - const map = new Map(); - - // We DO need to store the column values in our BFS queue though. That's because as we shift items off the queue, we - // will need to know which column it's in, so we can add it to the TreeNode array in the map above. - type ExtendedNode = [TreeNode, Column]; - const queue: ExtendedNode[] = [[root, 0]]; - - // Finally, we have to keep track of the min and max columns so we know where to start with the vertical order list. - let min = 0; - let max = 0; - - // The rest of the algorithm is a standard BFS. We don't need to maintain a visited set because the nodes are - // guaranteed to be organized as a tree, so we can't visit the same node twice. - while (queue.length > 0) { - const [node, column] = queue.shift()!; - - // Add the node to this map for bookkeeping. - if (!map.has(column)) { - map.set(column, []); - } - map.get(column)!.push(node); - - // Update min/max columns. - min = Math.min(min, column); - max = Math.max(max, column); - - // Enqueue the children as usual in BFS, but make sure to enqueue the LEFT child first to keep our proper row order. - if (node.left !== null) { - queue.push([node.left, column - 1]); - } - - // Enqueue the children as usual in BFS, but make sure to enqueue the RIGHT child last. - if (node.right !== null) { - queue.push([node.right, column + 1]); - } - } - - // Now we have a map of Column -> TreeNode[], and for each column, we just print out the values in the row. - const result: number[][] = []; - for (let column = min; column <= max; column++) { - // These nodes are already in row order because we visited the left child before the right. - const rows = map.get(column)!; - - // Now map each extended node to its value for the final result. - const values = rows.map(node => node.val); - result.push(values); - } - - return result; -} diff --git a/src/tree/common/parent-node.ts b/src/tree/common/parent-node.ts deleted file mode 100644 index a264c9b..0000000 --- a/src/tree/common/parent-node.ts +++ /dev/null @@ -1,62 +0,0 @@ -// This class definition comes from the problem itself and we cannot change it, or else our submission will not be -// accepted. -export class _Node { - val: number; - left: _Node | null; - right: _Node | null; - parent: _Node | null; - - constructor(v: number) { - this.val = v; - this.left = null; - this.right = null; - this.parent = null; - } -} - -// The LeetCode test input is given as an array, but the exact code to construct the tree is not given. This is likely -// how they've implemented it. -export function array2tree(array: (number | null)[]): _Node | null { - if (array.length === 0) { - return null; - } - - const root = new _Node(array[0]!); - const queue = [root]; - let i = 1; - - while (queue.length > 0 && i < array.length) { - const node = queue.shift()!; - if (node === null) { - continue; - } - - // Add the left node. - if (i < array.length) { - const val = array[i]; - if (val !== null) { - node.left = new _Node(val); - node.left.parent = node; - queue.push(node.left); - } else { - node.left = null; - } - i++; - } - - // Add the right node. - if (i < array.length) { - const val = array[i]; - if (val !== null) { - node.right = new _Node(val); - node.right.parent = node; - queue.push(node.right); - } else { - node.right = null; - } - i++; - } - } - - return root; -} diff --git a/src/tree/common/quad-tree.ts b/src/tree/common/quad-tree.ts deleted file mode 100644 index 0015b7d..0000000 --- a/src/tree/common/quad-tree.ts +++ /dev/null @@ -1,26 +0,0 @@ -// This class definition comes from the problem itself and we cannot change it, or else our submission will not be -// accepted. -export class _Node { - val: boolean; - isLeaf: boolean; - topLeft: _Node | null; - topRight: _Node | null; - bottomLeft: _Node | null; - bottomRight: _Node | null; - - constructor( - val?: boolean, - isLeaf?: boolean, - topLeft?: _Node, - topRight?: _Node, - bottomLeft?: _Node, - bottomRight?: _Node - ) { - this.val = val === undefined ? false : val; - this.isLeaf = isLeaf === undefined ? false : isLeaf; - this.topLeft = topLeft === undefined ? null : topLeft; - this.topRight = topRight === undefined ? null : topRight; - this.bottomLeft = bottomLeft === undefined ? null : bottomLeft; - this.bottomRight = bottomRight === undefined ? null : bottomRight; - } -} diff --git a/src/tree/common/tree-node.ts b/src/tree/common/tree-node.ts deleted file mode 100644 index 9d9625a..0000000 --- a/src/tree/common/tree-node.ts +++ /dev/null @@ -1,58 +0,0 @@ -// This class definition comes from the problem itself and we cannot change it, or else our submission will not be -// accepted. -export class TreeNode { - val: number; - left: TreeNode | null; - right: TreeNode | null; - - constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { - this.val = val === undefined ? 0 : val; - this.left = left === undefined ? null : left; - this.right = right === undefined ? null : right; - } -} - -// The LeetCode test input is given as an array, but the exact code to construct the tree is not given. This is likely -// how they've implemented it. -export function array2tree(array: (number | null)[]): TreeNode | null { - if (array.length === 0) { - return null; - } - - const root = new TreeNode(array[0]!); - const queue = [root]; - let i = 1; - - while (queue.length > 0 && i < array.length) { - const node = queue.shift()!; - if (node === null) { - continue; - } - - // Add the left node. - if (i < array.length) { - const val = array[i]; - if (val !== null) { - node.left = new TreeNode(val); - queue.push(node.left); - } else { - node.left = null; - } - i++; - } - - // Add the right node. - if (i < array.length) { - const val = array[i]; - if (val !== null) { - node.right = new TreeNode(val); - queue.push(node.right); - } else { - node.right = null; - } - i++; - } - } - - return root; -} diff --git a/src/tree/construct-quad-tree.ts b/src/tree/construct-quad-tree.ts deleted file mode 100644 index 3bfcb9f..0000000 --- a/src/tree/construct-quad-tree.ts +++ /dev/null @@ -1,59 +0,0 @@ -// DIFFICULTY: HARD -// -// Given a n * n matrix grid of 0's and 1's only. We want to represent grid with a Quad-Tree. -// -// Return the root of the Quad-Tree representing grid. -// -// See {@link https://en.wikipedia.org/wiki/Quadtree} -// See {@link https://leetcode.com/problems/construct-quad-tree/} -import { _Node } from './common/quad-tree'; -export { construct }; - -// SOLUTION: -// -// To make a quad tree, recursively divide the matrix into quadrants until each quadrant is all 1's or all 0's. -function construct(_grid: number[][]): _Node | null { - if (_grid.length === 0 || _grid[0].length === 0) { - return null; - } - - return constructInternal(_grid, 0, 0, _grid.length); -} - -function constructInternal(grid: number[][], row: number, column: number, size: number): _Node { - const node = new _Node(); - - // If the grid segment is uniform, we can create a single quad tree node and set the value to all 1's or 0's. - if (isUniform(grid, row, column, size)) { - node.val = grid[row][column] === 1; - node.isLeaf = true; - return node; - } - - // If the grid segment here isn't uniform, we'll need to create 4 quadrants and construct nodes out of all 4 of - // them. - // - // Setting the `node.val` of this node is irrelevant as it's not a leaf. We can just use the default value. - const half = size / 2; - node.isLeaf = false; - node.topLeft = constructInternal(grid, row, column, half); - node.topRight = constructInternal(grid, row, column + half, half); - node.bottomLeft = constructInternal(grid, row + half, column, half); - node.bottomRight = constructInternal(grid, row + half, column + half, half); - - return node; -} - -// Checks if a quadrant of the grid starting at [row, column] is uniformly all 1's or all 0's. -function isUniform(grid: number[][], row: number, column: number, size: number) { - const value = grid[row][column]; - for (let i = row; i < row + size; i++) { - for (let j = column; j < column + size; j++) { - if (grid[i][j] !== value) { - return false; - } - } - } - - return true; -} diff --git a/src/tree/design-in-memory-file-system.ts b/src/tree/design-in-memory-file-system.ts deleted file mode 100644 index bbe15de..0000000 --- a/src/tree/design-in-memory-file-system.ts +++ /dev/null @@ -1,118 +0,0 @@ -// DIFFICULTY: HARD -// -// Design a data structure that simulates an in-memory file system. -// -// Implement the FileSystem class: -// -// - FileSystem() -// Initializes the object of the system. -// - List ls(String path) -// If path is a file path, returns a list that only contains this file's name. -// If path is a directory path, returns the list of file and directory names in this directory. -// The answer should in lexicographic order. -// - void mkdir(String path) -// Makes a new directory according to the given path. The given directory path does not exist. -// If the middle directories in the path do not exist, you should create them as well. -// - void addContentToFile(String filePath, String content) -// If filePath does not exist, creates that file containing given content. -// If filePath already exists, appends the given content to original content. -// - String readContentFromFile(String filePath) -// Returns the content in the file at filePath. -// -// See {@link https://leetcode.com/problems/design-in-memory-file-system/} -export { FileSystem }; - -// SOLUTION: -// -// The problem states that we can assume all inputs are valid, so we don't need to check if we try to mkdir on path -// parts that contain files, or otherwise list children of files. We can also assume that all listed directories -// and files exist. -// -// Also, note that FileSystem shadows a built-in, so we'll have to suppress warnings about it. -class FileNode { - public isFile = false; - - public content: string | undefined = undefined; - - public children = new Map(); -} - -class FileSystem { - private readonly root = new FileNode(); - - ls(path: string): string[] { - let node = this.root; - - const parts = this.split(path); - for (const part of parts) { - if (!node.children.has(part)) { - return []; - } - - node = node.children.get(part)!; - - // Note that the ls function only wants to know the name of the file, not the absolute path, so don't bother - // prefixing the parent path. - if (node.isFile) { - return [part]; - } - } - - const children = [...node.children.keys()]; - children.sort((a, b) => a.localeCompare(b)); - return children; - } - - mkdir(path: string): void { - let node = this.root; - - const parts = this.split(path); - for (const part of parts) { - if (!node.children.has(part)) { - node.children.set(part, new FileNode()); - } - - node = node.children.get(part)!; - } - } - - addContentToFile(filePath: string, content: string): void { - let node = this.root; - - const parts = this.split(filePath); - for (let i = 0; i < parts.length; i++) { - const part = parts[i]; - - // The problem isn't exactly clear here, but the assumption is that we also mkdir the paths if they do not - // exist. - if (!node.children.has(part)) { - node.children.set(part, new FileNode()); - } - - node = node.children.get(part)!; - - // The very last part is the filename, so append the content. - if (i === parts.length - 1) { - const value = node.content === undefined ? '' : node.content; - node.content = value + content; - node.isFile = true; - } - } - } - - readContentFromFile(filePath: string): string { - let node = this.root; - - const parts = this.split(filePath); - for (const part of parts) { - node = node.children.get(part)!; - } - - return node.content ?? ''; - } - - private split(path: string) { - const normalized = path.endsWith('/') ? path.slice(0, path.length - 1) : path; - return normalized.split('/'); - } -} diff --git a/src/tree/diameter-of-binary-tree.ts b/src/tree/diameter-of-binary-tree.ts deleted file mode 100644 index 88491e7..0000000 --- a/src/tree/diameter-of-binary-tree.ts +++ /dev/null @@ -1,46 +0,0 @@ -// DIFFICULTY: EASY -// -// Given the root of a binary tree, return the length of the diameter of the tree. -// -// The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or -// may not pass through the root. -// -// The length of a path between two nodes is represented by the number of edges between them. -// -// See {@link https://leetcode.com/problems/diameter-of-binary-tree} -import { TreeNode } from './common/tree-node'; -export { diameterOfBinaryTree }; - -// SOLUTION: -// -// For any node, the longest path might: -// -// - Pass through the node, in which case the longest path is the longest path of the left and right subtrees, plus one. -// - Not pass through the node, in which case the longest path is the max of the longest path of the left and right. -// -// We can use DFS to traverse the tree and keep track of the longest path we've seen so far. -function diameterOfBinaryTree(root: TreeNode | null): number { - let max = 0; - - function dfs(node: TreeNode | null): number { - if (node === null) { - return 0; - } - - // Figure out the max depth of the left and right subtrees. - const left = dfs(node.left); - const right = dfs(node.right); - - // The left and right depths represent the current diameter of the tree rooted at this node. We'll compare it - // with the global max diameter. - const current = left + right; - max = Math.max(current, max); - - // Return the max depth starting from the current node. Whichever path (left or right) is longer will be the max - // depth, but we'll add one to account for the current node. - return Math.max(left, right) + 1; - } - - dfs(root); - return max; -} diff --git a/src/tree/lowest-common-ancestor-of-a-binary-tree-iii.ts b/src/tree/lowest-common-ancestor-of-a-binary-tree-iii.ts deleted file mode 100644 index 829a7eb..0000000 --- a/src/tree/lowest-common-ancestor-of-a-binary-tree-iii.ts +++ /dev/null @@ -1,58 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given two nodes of a binary tree p and q, return their lowest common ancestor (LCA). -// -// According to the definition of LCA on Wikipedia: "The lowest common ancestor of two nodes p and q in a tree T is the -// lowest node that has both p and q as descendants (where we allow a node to be a descendant of itself)." -// -// See {@link https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree-iii} -import { _Node } from './common/parent-node'; -export { lowestCommonAncestor }; - -// SOLUTION: -// -// Note that this problem is quite simple since we are given parent nodes. All we have to do is traverse up to the root -// for one node and collect ancestors. Then we traverse up to the root for the other node and check if any parent is in -// the ancestor set. If it is, then we have found the lowest common ancestor. -// -// However, that solution involves the use of a set. We can optimize this by using the two pointer technique in a -// clever way. Let's consider paths from p and q to the LCA. -// -// If path[p] === path[q], then all we have to do is advance both pointers one step at a time and they will converge at -// the LCA eventually. -// -// If path[p] !== path[q], then one of the pointers will reach past the root and become null first. -// -// The goal, then is to make BOTH pointers reach the LCA at the same time. So let's say path[p].length === 5, and that -// path[q].length === 10. If we could craft an extended path for both p and q so that they both travel the same -// distance, and end up at the LCA, then that would work. -// -// To create this extended path, envision an extended path2[p] that goes from p to the root then to q then to the root -// (yes, imagine there's a direct path from root -> q). Likewise, envision an extended path2[q] that goes from q to the -// root then from p to the root. -// -// Now if you make both the p and q pointers follow this path, they will both advance PAST the root, but then eventually -// converge at the LCA before they hit the root again. -// -// COMPLEXITY: -// -// For the solution involving a set, the time complexity is O(h + h) where h is the depth of the deeper node. The -// space complexity is O(h) to store the ancestors. -// -// For the solution not involving a set, the time complexity is O(h + h) where h is the depth of the deeper node. -function lowestCommonAncestor(p: _Node | null, q: _Node | null): _Node | null { - // Create new pointers to advance; we need the original starting points p and q. - let [a, b] = [p, q]; - - // Eventually a and b will converge at the LCA. - while (a !== b) { - // Advance a = p -> root -> q -> root. Pointers converge before hitting the root again. - a = a !== null ? a.parent : q; - - // Advance b = q -> root -> p -> root. Pointers converge before hitting the root again. - b = b !== null ? b.parent : p; - } - - // Return either pointer; they are both at the LCA. - return a; -} diff --git a/src/tree/lowest-common-ancestor-of-a-binary-tree.ts b/src/tree/lowest-common-ancestor-of-a-binary-tree.ts deleted file mode 100644 index eef3189..0000000 --- a/src/tree/lowest-common-ancestor-of-a-binary-tree.ts +++ /dev/null @@ -1,80 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given two nodes of a binary tree p and q, return their lowest common ancestor (LCA). -// -// According to the definition of LCA on Wikipedia: "The lowest common ancestor of two nodes p and q in a tree T is the -// lowest node that has both p and q as descendants (where we allow a node to be a descendant of itself)." -// -// See {@link https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree-iii} -import { TreeNode } from './common/tree-node'; -export { lowestCommonAncestor }; - -// SOLUTION: -// -// In this problem, we are not given the parent nodes. So we have to solve this problem using a recursive divide and -// conquer approach. -// -// The idea is to recursively check the left and right subtrees for nodes p and q. There are three cases: -// -// 1. We find p and q in DIFFERENT subtrees. -// 2. We find p and q BOTH in the left subtree. -// 3. We find p and q BOTH in the right subtree. -// -// Well, in case 1, if we began this process from the root, that's it! The current node has to be the LCA. If instead -// we discover that p and q are BOTH in the left or right subtree, make that subtree the new root and continue the -// search. -// -// Note that this solution requires that both p and q exist in the tree. If one of them doesn't exist we could return -// a false LCA. To guard against this we'd have to check the tree beforehand to ensure that both p and q exist. -// -// COMPLEXITY: -// -// The recursive calls visit every node once, so the time complexity is O(n) in number of nodes. We don't use extra -// space but the call stack uses O(h) space where h is the height of the tree. -function lowestCommonAncestor(node: TreeNode | null, p: TreeNode | null, q: TreeNode | null): TreeNode | null { - // Recursively find p or q in the tree rooted at node. - function findNode(node: TreeNode | null, p: TreeNode | null, q: TreeNode | null): TreeNode | null { - // We DID NOT find p or q in either of the subtrees. - if (node === null) { - return null; - } - - // We DID find p or q in either of the subtrees, so return it. - if (node === p || node === q) { - return node; - } - - // Otherwise, continue by checking for p and q in the left and right subtrees. - // - // If left is non-null, then we found one of p or q in the left subtree. - // If right is non-null, then we found one of p or q in the right subtree. - // If both were non-null, then we found p and q in different subtrees. - const left = findNode(node.left, p, q); - const right = findNode(node.right, p, q); - - // If BOTH left and right are non-null, then we found p and q in different subtrees. If that is the case, the - // current node must be the LCA. - if (left !== null && right !== null) { - return node; - } - - // We found ONE of nodes p or q in the LEFT subtree. However, we didn't find the other node in the right subtree - // so we assume that they were both in the left subtree. Note that we ASSUME that both p and q exist in the tree, - // or else we could return a false LCA. - if (left !== null) { - return left; - } - - // We found ONE of nodes p or q in the RIGHT subtree. However, we didn't find the other node in the left subtree - // so we assume that they were both in the right subtree. Note that we ASSUME that both p and q exist in the tree, - // or else we could return a false LCA. - return right; - } - - // Technically, lowestCommonAncestor has the same signature as findNode, so we could've just implemented the LCA - // logic directly. - // - // However, lowestCommonAncestor will return to you the LCA always. In findNode, sometimes the intermediate result - // is not the LCA (e.g. when we are just looking for the node). - return findNode(node, p, q); -} diff --git a/src/tree/range-sum-of-bst.ts b/src/tree/range-sum-of-bst.ts deleted file mode 100644 index 3ae0f18..0000000 --- a/src/tree/range-sum-of-bst.ts +++ /dev/null @@ -1,35 +0,0 @@ -// DIFFICULTY: EASY -// -// Given the root node of a binary search tree and two integers low and high, return the sum of values of all nodes -// with a value in the inclusive range [low, high]. -// -// See {@link https://leetcode.com/problems/range-sum-of-bst/} -import { TreeNode } from './common/tree-node'; -export { rangeSumBST }; - -// SOLUTION: -// -// A simple recursive solution will work. -// -// COMPLEXITY: -// -// The time complexity is O(n) where n is the number of nodes in the tree. -function rangeSumBST(root: TreeNode | null, low: number, high: number): number { - let sum = 0; - - function traverse(node: TreeNode | null) { - if (node === null) { - return; - } - - if (node.val >= low && node.val <= high) { - sum += node.val; - } - - traverse(node.left); - traverse(node.right); - } - - traverse(root); - return sum; -} diff --git a/src/tree/same-tree.ts b/src/tree/same-tree.ts deleted file mode 100644 index 8a60848..0000000 --- a/src/tree/same-tree.ts +++ /dev/null @@ -1,32 +0,0 @@ -// DIFFICULTY: EASY -// -// Given the roots of two binary trees p and q, write a function to check if they are the same or not. -// -// Two binary trees are considered the same if they are structurally identical, and the nodes have the same value. -// -// See {@link https://leetcode.com/problems/same-tree/} -import { TreeNode } from './common/tree-node'; -export { isSameTree }; - -// SOLUTION: -// -// A simple recursive solution will work. -// -// COMPLEXITY: -// -// The time complexity is O(n) where n is the number of nodes in the tree. -function isSameTree(p: TreeNode | null, q: TreeNode | null): boolean { - if (p === null) { - return q === null; - } - - if (q === null) { - return p === null; - } - - if (p.val !== q.val) { - return false; - } - - return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); -} diff --git a/src/tree/sum-root-to-leaf-numbers.ts b/src/tree/sum-root-to-leaf-numbers.ts deleted file mode 100644 index 7808097..0000000 --- a/src/tree/sum-root-to-leaf-numbers.ts +++ /dev/null @@ -1,45 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given the root of a binary tree containing digits from 0 to 9 only. -// -// Each root-to-leaf path in the tree represents a number. -// -// For example, the root-to-leaf path 1 -> 2 -> 3 represents the number 123. -// -// Return the total sum of all root-to-leaf numbers. Test cases are generated so that the answer will fit in a 32-bit integer. -// -// A leaf node is a node with no children. -// -// See {@link https://leetcode.com/problems/sum-root-to-leaf-numbers} -import { TreeNode } from './common/tree-node'; -export { sumNumbers }; - -// SOLUTION: -// -// This is a simple DFS problem. Just keep track of the current number as you traverse the tree. If you reach a leaf, -// add it to the running tally and sum up all the numbers at the end. -function sumNumbers(root: TreeNode | null): number { - const xs: number[] = []; - if (root === null) { - return 0; - } - - function dfs(node: TreeNode | null, sum: number) { - if (node === null) { - return; - } - - // Only add to the running list if we're at a leaf. - const x = sum * 10 + node.val; - if (node.left === null && node.right === null) { - xs.push(x); - return; - } - - dfs(node.left, x); - dfs(node.right, x); - } - - dfs(root, 0); - return xs.reduce((a, b) => a + b, 0); -} diff --git a/src/two-pointer/bag-of-tokens.ts b/src/two-pointer/bag-of-tokens.ts deleted file mode 100644 index 6fdca75..0000000 --- a/src/two-pointer/bag-of-tokens.ts +++ /dev/null @@ -1,76 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You start with an initial power of `power`, an initial score of 0, and a bag of tokens given as an integer array -// tokens, where each tokens[i] denotes the value of tokeni. -// -// Your goal is to maximize the total score by strategically playing these tokens. In one move, you can play an unplayed -// token in one of the two ways (but not both for the same token): -// -// Face-up: If your current power is at least tokens[i], you may play tokeni, losing tokens[i] power and gaining 1 -// score. -// Face-down: If your current score is at least 1, you may play tokeni, gaining tokens[i] power and losing 1 score. -// -// Return the maximum possible score you can achieve after playing any number of tokens. -// -// See {@link https://leetcode.com/problems/bag-of-tokens/} -export { bagOfTokensScore }; - -// SOLUTION: -// -// You basically are allowed to gain power at the cost of score, or gain score at the cost of power. You want to -// maximize the score. The problem does not tell you this, but: -// -// 1. The tokens are given to you as an array, but you may play them in any order. -// 2. You may opt to not play any tokens at all. -// -// Because score is always gained one point at a time, but power can be unlimited, we'll want to spend the least -// amount of power for score (that is make the smallest power tokens face up), while also sacrificing score for the -// highest power tokens. -// -// To start with, we'll try to play the highest power tokens face down first, then use that power to score as much as -// we can with the lower power tokens. This problem is suited for the two pointer technique since we are going to -// keep track of the high/low power tokens simultaneously. -// -// COMPLEXITY: -// -// Runs in O(n log n) time due to the sort. -function bagOfTokensScore(tokens: number[], power: number): number { - // First sort the tokens, so we can consume tokens from the ends to maximize score. - tokens.sort((a, b) => a - b); - - // Use the two pointers technique to figure out if we should consume tokens from the left to build power, or - // consume tokens from the right to build score. - let left = 0; - let right = tokens.length - 1; - let score = 0; - let max = 0; - - // Use left <= right here because you must process each token. - while (left <= right) { - // Since we are attempting to maximize score, let's try to eat the smaller tokens first to build score. - if (power >= tokens[left]) { - power -= tokens[left]; - score++; - left++; - - // Because we are running through every possible combination of token placing, we may have achieved max score - // already, but continue to play tokens. The problem asks for the max possible score, so record it here. - max = Math.max(score, max); - continue; - } - - // Otherwise, let's try to eat a big token to build power if we can. - if (score > 0) { - power += tokens[right]; - score--; - right--; - continue; - } - - // If we've reached this point, we don't have the score to build power. We also can't eat any tokens to build - // score, so we have to stop. - break; - } - - return max; -} diff --git a/src/two-pointer/container-with-most-water.ts b/src/two-pointer/container-with-most-water.ts deleted file mode 100644 index 165c4c7..0000000 --- a/src/two-pointer/container-with-most-water.ts +++ /dev/null @@ -1,54 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// You are given an integer array height of length n. There are n vertical lines drawn such that the two endpoints of -// the ith line are (i, 0) and (i, height[i]). -// -// Find two lines that together with the x-axis form a container, such that the container contains the most water. -// -// Return the maximum amount of water a container can store. -// -// Notice that you may not slant the container. -// -// See {@link https://leetcode.com/problems/container-with-most-water/} -export { maxArea }; - -// SOLUTION: -// -// Use the two pointers technique to maximize the area. We'll initialize at the left and right sides of the array, -// then advance the pointers inward and update our knowledge of the maximum area. -// -// We'll want to go from outside to inside, because if we have the highest columns on the outside, it's trivial to -// calculate the max area container. The problem is if we have shorter columns over a long width, but we can adjust -// the pointers and max area as we move them inwards. -function maxArea(xs: number[]): number { - let max = 0; - let left = 0; - let right = xs.length - 1; - - // Use left < right because there's no need to process left === right (that's a container with no area). - while (left < right) { - // Set the width to be the difference between left and right pointers. The height should be the smaller of the - // two heights referenced by the pointers. - const width = right - left; - const height = Math.min(xs[left], xs[right]); - - // Update the max area. - max = Math.max(max, width * height); - - // Now advance the pointers. No matter which pointer we choose to advance, the width will decrease. - // - // To attempt to maximize the area, we should advance the pointer that points to the lowest height, in some hopes - // that we will reach a higher height area and get a bigger container. - // - // Note that if we advance a pointer and the height ends up lower, we'll end up with an area calculation that does - // not correspond to an actual container. However, that "phantom container" will have a smaller max area than - // what was previously calculated, and it won't affect the actual result. - if (xs[left] < xs[right]) { - left++; - } else { - right--; - } - } - - return max; -} diff --git a/src/two-pointer/longest-common-prefix.ts b/src/two-pointer/longest-common-prefix.ts deleted file mode 100644 index ecf2d82..0000000 --- a/src/two-pointer/longest-common-prefix.ts +++ /dev/null @@ -1,46 +0,0 @@ -// DIFFICULTY: EASY -// -// Write a function to find the longest common prefix string amongst an array of strings. -// -// If there is no common prefix, return an empty string "". -// -// See {@link https://leetcode.com/problems/longest-common-prefix/} -export { longestCommonPrefix }; - -// SOLUTION: -// -// Note that the longest prefix can only be, at most, the length of the shortest string. We can start with the first -// string as a guess for the longest prefix, then tighten the bounds of that guess as we look the other strings. -function longestCommonPrefix(all: string[]): string { - if (all.length === 0) { - return ''; - } - - // The longest prefix can be at most the length of the shortest string. Therefore, we can set our best guess of - // the longest prefix to the first string. From there, we can tighten the bounds of the prefix and come up with an - // even shorter string as a result. - let longest = all[0]; - for (let i = 1; i < all.length; i++) { - const current = all[i]; - - // Compare the current string, character by character, against the longest prefix we've found so far. The length - // of matching characters will be used to update our assumption about the longest prefix afterwards. - let j = 0; - while ( - // If the characters at the current position DO NOT match, stop. The longest prefix will become where we have - // stopped. - longest[j] === current[j] && - // If the characters at the current position DO match, but we exceeded the length of either the current string - // or the longest prefix, we have to stop. There are no more characters to compare against each other. - j < Math.min(current.length, longest.length) - ) { - j += 1; - } - - // The length j represents how many characters match between the current prefix and the longest string, so update - // that value. - longest = current.substring(0, j); - } - - return longest; -} diff --git a/src/two-pointer/longest-substring-without-repeating-chars.ts b/src/two-pointer/longest-substring-without-repeating-chars.ts deleted file mode 100644 index dfc8215..0000000 --- a/src/two-pointer/longest-substring-without-repeating-chars.ts +++ /dev/null @@ -1,57 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a string s, find the length of the longest substring without repeating characters. -// -// See {@link https://leetcode.com/problems/longest-substring-without-repeating-characters/} -export { lengthOfLongestSubstring }; - -// SOLUTION: -// -// The naive way of doing this problem would be to generate all substrings, then check if each of the substrings has -// unique characters, updating a max length as we go along. -// -// However, we can do better than that using the two pointer technique. -// -// Since we only need to know the length of the substring, and not exactly what it is, we don't need to build up the -// string as we loop through the characters. -// -// Instead, we'll maintain a map of character -> index, where the index is the time where we most recently encountered -// that character. We'll use that index, along with the current index, to update our knowledge of what the max length -// substring is. -function lengthOfLongestSubstring(text: string): number { - // Use start as the index of the current substring under consideration. - let start = 0; - let max = 0; - const map = new Map(); - - for (let i = 0; i < text.length; i++) { - const c = text.charAt(i); - - // If we've seen this character before, we'll have to update our start index. This is because our substring can't - // have repeated characters any repeat character forces us to update start value. - if (map.has(c)) { - const j = map.get(c)!; - // It's possible we saw this character a while ago, and our current start index is more accurate. In that case - // the case, leave start alone. - if (start <= j) { - // In the case that we need to refresh the start index, set it past the index of the last seen instance of - // this character (since we are seeing this character now). - start = j + 1; - } - } - - // Consider the current index as the possible end of a substring. If this substring has a longer length than the - // max, update the max. - const end = i; - // The new max should be the difference between start and end, inclusive. - const len = end - start + 1; - if (len > max) { - max = len; - } - - // Finally mark this character's last seen index in our map. - map.set(c, i); - } - - return max; -} diff --git a/src/two-pointer/remove-duplicates-from-sorted-array.ts b/src/two-pointer/remove-duplicates-from-sorted-array.ts deleted file mode 100644 index 4ad62f5..0000000 --- a/src/two-pointer/remove-duplicates-from-sorted-array.ts +++ /dev/null @@ -1,51 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an integer array nums sorted in non-decreasing order, remove the duplicates in-place such that each unique -// element appears only once. The relative order of the elements should be kept the same. Then return the number of -// unique elements in nums. -// -// Consider the number of unique elements of nums to be k, to get accepted, you need to do the following things: -// -// Change the array nums such that the first k elements of nums contain the unique elements in the order they were -// present in nums initially. The remaining elements of nums are not important as well as the size of nums. -// -// Return k. -// -// See {@link https://leetcode.com/problems/remove-duplicates-from-sorted-array/} -export { removeDuplicates }; - -// SOLUTION: -// -// Use the two pointer technique to iterate through the array at different speeds. The first pointer moves through the -// array as number. The second pointer, j, will point to the last unique element. -function removeDuplicates(nums: number[]): number { - if (nums.length === 0) { - return 0; - } - - // Initialize the first pointer that moves through the array at normal speed. The first element is always unique. - let i = 1; - - // Initialize the second pointer to move only if we found a unique element. - let j = 0; - - while (i < nums.length) { - const current = nums[i]; - const lastUnique = nums[j]; - - // If the current element is different from the last unique element, then we have a new unique element. So advance - // the unique pointer, then copy the current element to the unique pointer. - if (current !== lastUnique) { - j++; - nums[j] = current; - } - - // Now advance the other pointer to continue through the array. - i++; - } - - // The index j tracks the last unique element, so the number of unique elements is j + 1. Contrast this to the remove - // element problem. - const k = j + 1; - return k; -} diff --git a/src/two-pointer/remove-element.ts b/src/two-pointer/remove-element.ts deleted file mode 100644 index 1d4f11c..0000000 --- a/src/two-pointer/remove-element.ts +++ /dev/null @@ -1,44 +0,0 @@ -// DIFFICULTY: EASY -// -// Given an integer array nums and an integer val, remove all occurrences of val in nums in-place. The order of the -// elements may be changed. Then return the number of elements in nums which are not equal to val. -// -// Consider the number of elements in nums which are not equal to val be k, to get accepted, you need to do the -// following things: -// -// - Change the array nums such that the first k elements of nums contain the elements which are not equal to val. -// The remaining elements of nums are not important as well as the size of nums. -// - Return k. -// -// See {@link https://leetcode.com/problems/remove-element} -export { removeElement }; - -// SOLUTION: -// -// The solution is very similar to the remove duplicates from sorted array problem. We just need to maintain two -// pointers; one that moves through the array at normal speed and another that moves only when we find an element that -// is not equal to val. -function removeElement(nums: number[], val: number): number { - // Initialize the first pointer that moves through the array at normal speed. The first element is always unique. - let i = 0; - - // Initialize the second pointer to move only if we found an element not equal to val. - let j = 0; - - while (i < nums.length) { - const current = nums[i]; - - // If we found an element that's not equal to val, we should keep it. So copy it to the j pointer. - if (current !== val) { - nums[j] = current; - j++; - } - - // Now advance the other pointer to continue through the array. - i++; - } - - // The index j represents the number of elements that are not equal to val. That means j === k, so we can return it - // directly. Contrast this with remove duplicates from sorted array. - return j; -} diff --git a/src/two-pointer/string-compression.ts b/src/two-pointer/string-compression.ts deleted file mode 100644 index e8c4c3f..0000000 --- a/src/two-pointer/string-compression.ts +++ /dev/null @@ -1,54 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an array of characters chars, compress it using the following algorithm: -// -// Begin with an empty string s. For each group of consecutive repeating characters in chars: -// -// - If the group's length is 1, append the character to s. -// - Otherwise, append the character followed by the group's length. -// -// The compressed string s should not be returned separately, but instead, be stored in the input character array chars. -// Note that group lengths that are 10 or longer will be split into multiple characters in chars. -// -// After you are done modifying the input array, return the new length of the array. -// -// You must write an algorithm that uses only constant extra space. -// -// See {@link https://leetcode.com/problems/string-compression/} -export { compress }; - -// SOLUTION: -// -// Because we cannot use extra space, we will need to read and write to the array at the same time. We can use the -// two pointer approach for doing so. -function compress(cs: string[]): number { - let read = 0; - let write = 0; - - while (read < cs.length) { - // Keep track of the current character and the number of times it has appeared. - const c = cs[read]; - let n = 0; - - // Consume the characters until we reach a point where the character differs. - while (read < cs.length && cs[read] === c) { - read++; - n++; - } - - // Write the current character and the number of times it appears. We'll always have enough room because we'll - // only need to write more than one digit if we have 10 or more characters. - cs[write] = c; - write++; - - // Only write digits if the count > 1; otherwise just leave the character as is. - if (n > 1) { - for (const digit of n.toString()) { - cs[write] = digit; - write++; - } - } - } - - return write; -} diff --git a/src/two-pointer/three-sum-closest.ts b/src/two-pointer/three-sum-closest.ts deleted file mode 100644 index 8d3fb58..0000000 --- a/src/two-pointer/three-sum-closest.ts +++ /dev/null @@ -1,59 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums of length n and an integer target, find three integers in nums such that the sum is -// closest to target. -// -// Return the sum of the three integers. -// -// You may assume that each input would have exactly one solution. -// -// See {@link https://leetcode.com/problems/3sum-closest/} -export { threeSumClosest }; - -// SOLUTION: -// -// We'll use the two pointer technique to zero in on the target sum while iterating through the array. To make -// calculations easier, we have to sort the array. -function threeSumClosest(xs: number[], target: number): number { - xs.sort((a, b) => a - b); - - let closestSum = Infinity; - let closestValues: number[] = []; - - for (let i = 0; i < xs.length - 2; i++) { - // Use the two pointer technique to find the closest triple. We will move the left pointer forward, or move the - // right pointer backwards as we attempt to get close to our target sum. - // - // Initialize the left pointer to the right of i, and initialize the right pointer at the end of the array. We - // will begin moving the left and right pointers closer to each other as we attempt to compute the triple. - let left = i + 1; - let right = xs.length - 1; - - while (left < right) { - // If the current sum is closer than what we have, update the closest sum and the triple. - const currentSum = xs[i] + xs[left] + xs[right]; - const currentDistance = Math.abs(currentSum - target); - const closestDistance = Math.abs(closestSum - target); - if (currentDistance < closestDistance) { - closestSum = currentSum; - closestValues = [xs[i], xs[left], xs[right]]; - } - - // If we've matched the target sum, just return right away. - if (closestSum === target) { - return closestValues.reduce((a, b) => a + b); - } - - // Update the left and right pointers, and try again. Because we've sorted the array, we can move the pointers - // accordingly. If the sum is too small, move the left pointer forwards. If the sum is too big, move the - // the right pointer backwards. - if (currentSum < target) { - left++; - } else { - right--; - } - } - } - - return closestValues.reduce((a, b) => a + b); -} diff --git a/src/two-pointer/three-sum.ts b/src/two-pointer/three-sum.ts deleted file mode 100644 index de87e95..0000000 --- a/src/two-pointer/three-sum.ts +++ /dev/null @@ -1,92 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that -// i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0. -// -// Notice that the solution set must not contain duplicate triplets. -// -// See {@link https://leetcode.com/problems/3sum/} -export { threeSum }; - -// SOLUTION: -// -// The two sum problem can be solved with a two pointer approach. This can be solved with a similar approach, but -// you'll need to apply the two pointer approach to every element in the array. -function threeSum(xs: number[]) { - const result: number[][] = []; - - // Sorting will help us more efficiently use the sliding window approach to find the triplets that sum to 0. - // - // Note that xs.sort() will sort by string value. So if you have negative numbers, something like [-1, -2, 0] is - // considered "sorted". To fix this, make sure to explicitly provide a compare function. - xs.sort((a, b) => a - b); - - // For each of the elements we will use a sliding window approach to find the other two elements that will sum up - // to 0. - // - // There are two ways we can think of this: we can think of our current element as element b, then set the left - // point to 0 and the right pointer to the length of the array - 1. However, if we do this, we will reconsider - // elements we've already seen as the pointer advances. - // - // Instead, we'll consider the current element as a, then b starts at left = i + 1, and c starts at - // right = length - 1. - for (let i = 0; i < xs.length - 2; i++) { - // Because we can't have duplicate triples in the result, we should just skip over any duplicates. - if (i > 0 && xs[i] === xs[i - 1]) { - continue; - } - - // For each element a, use the two pointers technique to find (b, c) such that a + b + c = 0. - const a = xs[i]; - - // We cannot have duplicate triples in the result. We can do this by setting the left pointer to 0, and the right - // pointer to the last element, tightening the bounds as we consider sums. However, this will reconsider - // duplicate triples as i advances. We would need a set to dedupe the triples. - // - // Starting the left pointer at i + 1 avoids this problem. - let left = i + 1; - let right = xs.length - 1; - while (left < right) { - const b = xs[left]; - const c = xs[right]; - const sum = a + b + c; - - // If the sum is less than 0, that means we need to increase the sum, so advance the left pointer. - if (sum < 0) { - left++; - continue; - } - - // If the sum is greater than 0, that means we need to decrease the sum, so advance the right pointer. - if (sum > 0) { - right--; - continue; - } - - // If the sum is the target of 0, add the triple to the result array. However, there may yet still be more - // triplets we haven't found with our pointers! - // - // To find them, we should keep advancing the pointers past any dupes (as long as they are in range). - if (sum === 0) { - result.push([a, b, c]); - - // Advance the left pointer to skip any dupes of b. - while (xs[left + 1] === xs[left] && left < right) { - left++; - } - - // Advance the right pointer to skip any dupes of c. - while (xs[right - 1] === xs[right] && left < right) { - right--; - } - - // Advance both pointers to consider the next potential sum. We need to advance here and not before doing - // the while loops because advancing could have put us into a dupe! - left++; - right--; - } - } - } - - return result; -} diff --git a/src/two-pointer/trapping-rain-water.ts b/src/two-pointer/trapping-rain-water.ts deleted file mode 100644 index a24fcf6..0000000 --- a/src/two-pointer/trapping-rain-water.ts +++ /dev/null @@ -1,63 +0,0 @@ -// DIFFICULTY: HARD -// -// Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water -// it can trap after raining. -// -// See {@link https://leetcode.com/problems/trapping-rain-water/} -export { trap }; - -// SOLUTION: -// -// Note that for this question, the leftmost element of the elevation map doesn't trap any water, because there is no -// land/bar to the left of the leftmost element. -// -// Similarly, for the rightmost element, any depression of the land/bar doesn't trap any water because there isn't -// any elevation to the right of the rightmost element. -// -// To solve this we can use the two pointers technique to compute trapped rainwater on a pointer by pointer basis, -// advancing the pointers -function trap(elevationMap: number[]): number { - // The difference between the lowest height and the smaller of the max height from the left and right sides - // contributes to that many units of trapped rainwater AT THE CURRENT INDEX. - // - // For example, a current elevation of zero with a max elevation of 4 and 6 on the left and right sides respectively - // will hold 4 units of trapped rainwater. We'll use this information to compute rainwater at each index. - let trapped = 0; - - // Track the maximum height on the left side and right side, based on where our pointers land. The smaller of these - // two values will be the height we will use to compute trapped rainwater (since any rainwater over the smaller - // value will spill out). - let lmax = 0; - let rmax = 0; - let max = 0; - - // Use the two pointers technique to compute the trapped rainwater. - let left = 0; - let right = elevationMap.length - 1; - while (left < right) { - // Update the left and right max values, then compute the smaller of these two values to figure out the amount - // of rainwater we can actually trap. - lmax = Math.max(lmax, elevationMap[left]); - rmax = Math.max(rmax, elevationMap[right]); - - // This is the max amount we can trap with an elevation of zero. We compute the elevation next to figure out how - // much is really trapped. - max = Math.min(lmax, rmax); - - // Compute the trapped rainwater from one of the pointers, then advance it. - // - // Choose to advance the pointer associated with the smaller height inward, so that we have the opportunity - // to move to a higher elevation and trap more water. - if (elevationMap[left] < elevationMap[right]) { - const elevation = elevationMap[left]; - trapped += max - elevation; - left++; - } else { - const elevation = elevationMap[right]; - trapped += max - elevation; - right--; - } - } - - return trapped; -} diff --git a/src/two-pointer/two-sum-input-array-sorted.ts b/src/two-pointer/two-sum-input-array-sorted.ts deleted file mode 100644 index f3b6284..0000000 --- a/src/two-pointer/two-sum-input-array-sorted.ts +++ /dev/null @@ -1,40 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given a 1-indexed array of integers numbers that is already sorted in non-decreasing order, find two numbers such -// that they add up to a specific target number. Let these two numbers be numbers[index1] and numbers[index2] where -// 1 <= index1 < index2 <= numbers.length. -// -// Return the indices of the two numbers, index1 and index2, added by one as an integer array [index1, index2] of -// length 2. -// -// The tests are generated such that there is exactly one solution. You may not use the same element twice. -// -// Your solution must use only constant extra space. -// -// See {@link https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/} -export { twoSum }; - -// SOLUTION: -// -// Use the two pointer approach to narrow in on the target sum from both the left and the right. -function twoSum(xs: number[], target: number) { - let left = 0; - let right = xs.length - 1; - - while (left < right) { - const sum = xs[left] + xs[right]; - if (sum === target) { - return [left + 1, right + 1]; - } - - if (sum < target) { - left++; - } - - if (sum > target) { - right--; - } - } - - return []; -} diff --git a/src/two-pointer/valid-palindrome-ii.ts b/src/two-pointer/valid-palindrome-ii.ts deleted file mode 100644 index 640ef72..0000000 --- a/src/two-pointer/valid-palindrome-ii.ts +++ /dev/null @@ -1,63 +0,0 @@ -// DIFFICULTY: EASY -// -// Given a string s, return true if the s can be palindrome after deleting at most one character from it. -// -// See {@link https://leetcode.com/problems/valid-palindrome-ii/} -export { validPalindrome }; - -// SOLUTION: -// -// Use the two pointer technique to check if the character from the left matches the character on the right. If they -// do, just proceed as normal checking characters. -// -// If they do not match, we have one chance to check if deleting either the left character, or the right character, -// results in a palindrome (we can only delete one character). To do this check, we only need to check a subset of -// the characters. -// -// COMPLEXITY: -// -// The outer loop runs in O(n) time; if the word is a palindrome, the inner loop will never be called, resulting in -// O(n) runtime. -// -// The inner loop from isPalindrome() will also run in O(n) time, and they will be called at most twice. -// -// This results in time complexity of O(n + 2 * n) = O(n). -function validPalindrome(s: string): boolean { - // Check if a string is a palindrome starting using the supplied left/right pointers. - function isPalindrome(left: number, right: number) { - while (left < right) { - if (s[left] !== s[right]) { - return false; - } - - left++; - right--; - } - - return true; - } - - // Use left < right here because it's not necessary to check if the middle character matches itself. - let left = 0; - let right = s.length - 1; - while (left < right) { - // If characters match, proceed as normal. - if (s[left] === s[right]) { - left++; - right--; - continue; - } - - // Characters didn't match; check if deleting/skipping the left character results in a palindrome. - const isSkipLeftOk = isPalindrome(left + 1, right); - - // Characters didn't match; check if deleting/skipping the right character results in a palindrome. - const isSkipRightOk = isPalindrome(left, right - 1); - - // If deleting either was okay, we still have a "valid" palindrome. - return isSkipLeftOk || isSkipRightOk; - } - - // We didn't skip any characters, so it is a palindrome. - return true; -} diff --git a/src/two-pointer/valid-palindrome.ts b/src/two-pointer/valid-palindrome.ts deleted file mode 100644 index b5a087f..0000000 --- a/src/two-pointer/valid-palindrome.ts +++ /dev/null @@ -1,36 +0,0 @@ -// DIFFICULTY: EASY -// -// A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all -// non-alphanumeric characters, it reads the same forward and backward. Alphanumeric characters include letters and -// numbers. -// -// Given a string s, return true if it is a palindrome, or false otherwise. -// -// See {@link https://leetcode.com/problems/valid-palindrome/} -export { isPalindrome }; - -// SOLUTION: -// -// Use the two pointer technique to compare elements from the ends of the array to see if you have a match. -// -// COMPLEXITY: -// -// Runs in O(n) time. -function isPalindrome(s: string): boolean { - // Replace all non-alphanumeric characters with an empty string. - const text = s.replace(/[^a-zA-Z0-9]/g, ''); - - // Use left < right here because it's not necessary to check if the middle character matches itself. - let left = 0; - let right = text.length - 1; - while (left < right) { - if (text[left].toUpperCase() !== text[right].toUpperCase()) { - return false; - } - - left++; - right--; - } - - return true; -} diff --git a/src/two-pointer/valid-triangle-number.ts b/src/two-pointer/valid-triangle-number.ts deleted file mode 100644 index a5353c0..0000000 --- a/src/two-pointer/valid-triangle-number.ts +++ /dev/null @@ -1,61 +0,0 @@ -// DIFFICULTY: MEDIUM -// -// Given an integer array nums, return the number of triplets chosen from the array that can make triangles if we take -// them as side lengths of a triangle. -// -// See {@link https://leetcode.com/problems/valid-triangle-number/} -export { triangleNumber }; - -// SOLUTION: -// -// To form a triangle, 3 numbers have to satisfy the inequalities: -// -// a + b > c -// a + c > b -// b + c > a -// -// The most straightforward approach is to sort the numbers and use the two pointer technique to find triples. -function triangleNumber(xs: number[]): number { - if (xs.length <= 2) { - return 0; - } - - // Once all elements are sorted, we know that if i < j < k, then xs[i] <= xs[j] <= xs[k]. Let a = xs[i], b = xs[j], - // and c = xs[k]. We know that a <= b <= c. - // - // If we find that a + b > c, then we have found a triplet. This is because: - // - // a + c > b, since c > b. - // b + c > a, since c > a. - // - // Once we find a triplet, we know that all elements between the left and right pointer are valid. - xs.sort((a, b) => a - b); - - let result = 0; - for (let i = xs.length - 1; i >= 0; i--) { - // Loop backwards to find our c, which we'll check if it is greater than a + b. - const c = xs[i]; - - // Start at opposite ends of the array, [a, ...., b, c], and let a and b close in on each other to find a valid - // triplet. Starting this way lets us adjust a and b in the right directions to account for c. - let left = 0; - let right = i - 1; - while (left < right) { - const a = xs[left]; - const b = xs[right]; - - // We have found a triplet; all elements between left and right can form a triplet. - if (a + b > c) { - result += right - left; - - // Since c is large compared to a + b, advance the right pointer and make c smaller to find more triplets. - right--; - } else { - // Since c is small compared to a + b, advance the left pointer and make c bigger to find more triplets. - left++; - } - } - } - - return result; -} diff --git a/test/array/__snapshots__/group-anagrams.test.ts.snap b/test/array/__snapshots__/group-anagrams.test.ts.snap deleted file mode 100644 index 8ad83de..0000000 --- a/test/array/__snapshots__/group-anagrams.test.ts.snap +++ /dev/null @@ -1,18 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`group anagrams group anagrams - test case 1 1`] = ` -[ - [ - "eat", - "tea", - "ate", - ], - [ - "tan", - "nat", - ], - [ - "bat", - ], -] -`; diff --git a/test/array/best-time-to-buy-and-sell-stock-ii.test.ts b/test/array/best-time-to-buy-and-sell-stock-ii.test.ts deleted file mode 100644 index 07f927c..0000000 --- a/test/array/best-time-to-buy-and-sell-stock-ii.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { maxProfit } from './../../src/array/best-time-to-buy-and-sell-stock-ii'; - -describe('best time to buy and sell stock ii', () => { - test('max profit ii - test case 1', async () => { - expect(maxProfit([7, 1, 5, 3, 6, 4])).toBe(7); - }); - - test('max profit ii - test case 2', async () => { - expect(maxProfit([1, 2, 3, 4, 5])).toBe(4); - }); -}); diff --git a/test/array/buildings-with-an-ocean-view.test.ts b/test/array/buildings-with-an-ocean-view.test.ts deleted file mode 100644 index f89fb22..0000000 --- a/test/array/buildings-with-an-ocean-view.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { findBuildings } from '../../src/array/buildings-with-an-ocean-view'; - -describe('buildings with an ocean view', () => { - test('findBuildings - test case 1', async () => { - expect(findBuildings([4, 2, 3, 1])).toEqual([0, 2, 3]); - }); -}); diff --git a/test/array/design-hit-counter.test.ts b/test/array/design-hit-counter.test.ts deleted file mode 100644 index 610e4ca..0000000 --- a/test/array/design-hit-counter.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { HitCounter } from '../../src/array/design-hit-counter'; - -describe('design hit counter', () => { - test('design hit counter - test case 1', async () => { - const counter = new HitCounter(); - - counter.hit(1); - counter.hit(2); - counter.hit(3); - - expect(counter.getHits(4)).toBe(3); - - counter.hit(300); - - expect(counter.getHits(300)).toBe(4); - expect(counter.getHits(301)).toBe(3); - }); -}); diff --git a/test/array/dot-product-of-two-sparse-vectors.test.ts b/test/array/dot-product-of-two-sparse-vectors.test.ts deleted file mode 100644 index f6e47c7..0000000 --- a/test/array/dot-product-of-two-sparse-vectors.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { SparseVector } from '../../src/array/dot-product-of-two-sparse-vectors'; - -describe('dot product of two sparse vectors', () => { - test('dot product of two sparse vectors', async () => { - const a = new SparseVector([1, 0, 0, 2, 3]); - const b = new SparseVector([0, 3, 0, 4, 0]); - - expect(a.dotProduct(b)).toStrictEqual(8); - }); -}); diff --git a/test/array/group-anagrams.test.ts b/test/array/group-anagrams.test.ts deleted file mode 100644 index d3c5217..0000000 --- a/test/array/group-anagrams.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { groupAnagrams } from '../../src/array/group-anagrams'; - -describe('group anagrams', () => { - test('group anagrams - test case 1', async () => { - expect(groupAnagrams(['eat', 'tea', 'tan', 'ate', 'nat', 'bat'])).toMatchSnapshot(); - }); -}); diff --git a/test/array/longest-consecutive-sequence.test.ts b/test/array/longest-consecutive-sequence.test.ts deleted file mode 100644 index 07b760c..0000000 --- a/test/array/longest-consecutive-sequence.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { longestConsecutive } from '../../src/array/longest-consecutive-sequence'; - -describe('longest consecutive sequence', () => { - test('longest consecutive - test case 1', async () => { - expect(longestConsecutive([100, 4, 200, 1, 3, 2])).toBe(4); - }); -}); diff --git a/test/array/max-chunks-to-make-sorted.test.ts b/test/array/max-chunks-to-make-sorted.test.ts deleted file mode 100644 index dabadcc..0000000 --- a/test/array/max-chunks-to-make-sorted.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { maxChunksToSorted } from '../../src/array/max-chunks-to-make-sorted'; - -describe('max chunks to make sorted', () => { - test('max chunks to make sorted - test case 1', async () => { - expect(maxChunksToSorted([4, 3, 2, 1, 0])).toBe(1); - }); -}); diff --git a/test/array/maximum-swap.test.ts b/test/array/maximum-swap.test.ts deleted file mode 100644 index 6f846b4..0000000 --- a/test/array/maximum-swap.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { maximumSwap } from '../../src/array/maximum-swap'; - -describe('maximum swap', () => { - test('maximumSwap - test case 1', () => { - expect(maximumSwap(2736)).toEqual(7236); - }); - - test('maximumSwap - test case 2', () => { - expect(maximumSwap(1993)).toEqual(9913); - }); - - test('maximumSwap - test case 1', () => { - expect(maximumSwap(98368)).toEqual(98863); - }); -}); diff --git a/test/array/merge-sorted-array.test.ts b/test/array/merge-sorted-array.test.ts deleted file mode 100644 index 23f0569..0000000 --- a/test/array/merge-sorted-array.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { merge } from '../../src/array/merge-sorted-array'; - -describe('merge sorted array', () => { - test('merge sorted array - test case 1', async () => { - const nums1 = [1, 2, 3, 0, 0, 0]; - const m = 3; - const nums2 = [2, 5, 6]; - const n = 3; - - merge(nums1, m, nums2, n); - - expect(nums1).toStrictEqual([1, 2, 2, 3, 5, 6]); - }); - - test('merge sorted array - test case 2', async () => { - const nums1 = [4, 5, 6, 0, 0, 0]; - const m = 3; - const nums2 = [1, 2, 3]; - const n = 3; - - merge(nums1, m, nums2, n); - - expect(nums1).toStrictEqual([1, 2, 3, 4, 5, 6]); - }); -}); diff --git a/test/array/pascals-triangle.test.ts b/test/array/pascals-triangle.test.ts deleted file mode 100644 index 7d2cecc..0000000 --- a/test/array/pascals-triangle.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { generate } from '../../src/array/pascals-triangle'; - -describe('pascals triangle', () => { - test('pascals triangle - test case 1', async () => { - expect(generate(5)).toStrictEqual([[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]]); - }); -}); diff --git a/test/array/product-of-array-except-self.test.ts b/test/array/product-of-array-except-self.test.ts deleted file mode 100644 index 31113cc..0000000 --- a/test/array/product-of-array-except-self.test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { productExceptSelf } from '../../src/prefix-sum/product-of-array-except-self'; - -describe('product of array except self', () => { - test('product except self - test case 1', async () => { - expect(productExceptSelf([0, 2, 3, 4])).toStrictEqual([24, 0, 0, 0]); - expect(productExceptSelf([1, 2, 3, 4])).toStrictEqual([24, 12, 8, 6]); - }); -}); diff --git a/test/array/top-k-frequent-elements.test.ts b/test/array/top-k-frequent-elements.test.ts deleted file mode 100644 index a42587d..0000000 --- a/test/array/top-k-frequent-elements.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { topKFrequent } from '../../src/array/top-k-frequent-elements'; - -describe('top k frequent elements', () => { - test('top k frequent - test case 1', async () => { - expect(topKFrequent([1, 1, 1, 2, 2, 3], 2)).toStrictEqual([1, 2]); - }); -}); diff --git a/test/array/two-sum.test.ts b/test/array/two-sum.test.ts deleted file mode 100644 index 4d24646..0000000 --- a/test/array/two-sum.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { twoSum } from '../../src/array/two-sum'; - -describe('two sum', () => { - test('two sum - test case 2', async () => { - expect(twoSum([2, 7, 11, 15], 9)).toStrictEqual([1, 0]); - expect(twoSum([3, 2, 4], 6)).toStrictEqual([2, 1]); - expect(twoSum([3, 3], 6)).toStrictEqual([1, 0]); - }); -}); diff --git a/test/binary-search/find-first-and-last-position-of-element-in-sorted-array.test.ts b/test/binary-search/find-first-and-last-position-of-element-in-sorted-array.test.ts deleted file mode 100644 index c659b27..0000000 --- a/test/binary-search/find-first-and-last-position-of-element-in-sorted-array.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { searchRange } from '../../src/binary-search/find-first-and-last-position-of-element-in-sorted-array'; - -describe('find first and last position of element in sorted array', () => { - test('searchRange - test case i', async () => { - expect(searchRange([5, 7, 7, 8, 8, 10], 8)).toStrictEqual([3, 4]); - }); -}); diff --git a/test/binary-search/find-peak-element.test.ts b/test/binary-search/find-peak-element.test.ts deleted file mode 100644 index 49ec19b..0000000 --- a/test/binary-search/find-peak-element.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { findPeakElement } from '../../src/binary-search/find-peak-element'; - -describe('find peak element', () => { - test('find peak element - test case 1', async () => { - expect(findPeakElement([1, 2, 3, 1])).toBe(2); - }); - - test('find peak element - test case 2', async () => { - expect(findPeakElement([1, 2, 1, 3, 5, 6, 4])).toBe(5); - }); - - test('find peak element - test case 3', async () => { - expect(findPeakElement([1])).toBe(0); - }); - - test('find peak element - test case 4', async () => { - expect(findPeakElement([2, 1])).toBe(0); - }); - - test('find peak element - test case 5', async () => { - expect(findPeakElement([-2147483647, -2147483648])).toBe(0); - }); - - test('find peak element - test case 6', async () => { - expect(findPeakElement([1, 2])).toBe(1); - }); -}); diff --git a/test/binary-search/find-the-smallest-divisor-given-a-threshold.test.ts b/test/binary-search/find-the-smallest-divisor-given-a-threshold.test.ts deleted file mode 100644 index 62e84b3..0000000 --- a/test/binary-search/find-the-smallest-divisor-given-a-threshold.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { smallestDivisor } from '../../src/binary-search/find-the-smallest-divisor-given-a-threshold'; - -describe('find the smallest divisor given a threshold', () => { - test('find the smallest divisor given a threshold', async () => { - expect(smallestDivisor([1, 2, 5, 9], 6)).toBe(5); - }); -}); diff --git a/test/binary-search/k-th-missing-positive-number.test.ts b/test/binary-search/k-th-missing-positive-number.test.ts deleted file mode 100644 index 049362b..0000000 --- a/test/binary-search/k-th-missing-positive-number.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { findKthPositive } from '../../src/binary-search/k-th-missing-positive-number'; - -describe('k-th-missing-positive-number', () => { - test('find kth positive - test case 1', () => { - const arr = [2, 3, 4, 7, 11]; - const k = 5; - - expect(findKthPositive(arr, k)).toEqual(9); - }); - - test('find kth positive - test case 2', () => { - const arr = [1, 2, 3, 4]; - const k = 2; - - expect(findKthPositive(arr, k)).toEqual(6); - }); -}); diff --git a/test/binary-search/k-th-smallest-element-in-a-sorted-matrix.test.ts b/test/binary-search/k-th-smallest-element-in-a-sorted-matrix.test.ts deleted file mode 100644 index 75f4a63..0000000 --- a/test/binary-search/k-th-smallest-element-in-a-sorted-matrix.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { kthSmallest } from '../../src/binary-search/k-th-smallest-element-in-a-sorted-matrix'; - -describe('k-th smallest element in a sorted matrix', () => { - test('kthSmallest - test case 1', () => { - const matrix = [ - [1, 5, 9], - [10, 11, 13], - [12, 13, 15] - ]; - const k = 8; - - expect(kthSmallest(matrix, k)).toEqual(13); - }); -}); diff --git a/test/binary-search/longest-increasing-subsequence.test.ts b/test/binary-search/longest-increasing-subsequence.test.ts deleted file mode 100644 index 39c77e4..0000000 --- a/test/binary-search/longest-increasing-subsequence.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { lengthOfLIS } from '../../src/binary-search/longest-increasing-subsequence'; - -describe('longest increasing subsequence', () => { - test('lengthOfLIS - test case i', () => { - expect(lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18])).toEqual(4); - }); -}); diff --git a/test/binary-search/median-of-two-sorted-arrays.test.ts b/test/binary-search/median-of-two-sorted-arrays.test.ts deleted file mode 100644 index 30af627..0000000 --- a/test/binary-search/median-of-two-sorted-arrays.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { findMedianSortedArrays } from '../../src/binary-search/median-of-two-sorted-arrays'; - -describe('median of two sorted arrays', () => { - test('median of two sorted arrays - test case 4', async () => { - expect(findMedianSortedArrays([1, 3], [2])).toBe(2); - }); - - test('median of two sorted arrays - test case 5', async () => { - expect(findMedianSortedArrays([3, 2, 3, 1, 2, 4, 5, 5, 6], [4])).toBe(3); - }); -}); diff --git a/test/binary-search/search-in-rotated-sorted-array.test.ts b/test/binary-search/search-in-rotated-sorted-array.test.ts deleted file mode 100644 index 85c3903..0000000 --- a/test/binary-search/search-in-rotated-sorted-array.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { search } from '../../src/binary-search/search-in-rotated-sorted-array'; - -describe('search in rotated sorted array', () => { - test('search in rotated sorted array - test case 1', async () => { - expect(search([4, 5, 6, 7, 0, 1, 2], 0)).toBe(4); - }); - - test('search in rotated sorted array - test case 2', async () => { - expect(search([4, 5, 6, 7, 0, 1, 2], 3)).toBe(-1); - }); - - test('search in rotated sorted array - test case 3', async () => { - expect(search([1], 0)).toBe(-1); - }); - - test('search in rotated sorted array - test case 4', async () => { - expect(search([3, 1], 1)).toBe(1); - }); -}); diff --git a/test/dynamic-programming/arithmetic-slices-ii-subsequences.test.ts b/test/dynamic-programming/arithmetic-slices-ii-subsequences.test.ts deleted file mode 100644 index 9ee58f6..0000000 --- a/test/dynamic-programming/arithmetic-slices-ii-subsequences.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { numberOfArithmeticSlices } from '../../src/dynamic-programming/arithmetic-slices-ii-subsequences'; - -describe('arithmetic slices ii - subsequence', () => { - test('arithmetic slices ii - subsequence - test case 1', async () => { - const nums = [2, 4, 6, 8, 10]; - - expect(numberOfArithmeticSlices(nums)).toBe(7); - }); - - test('arithmetic slices ii - subsequence - test case 2', async () => { - const nums = [7, 7, 7, 7, 7]; - - expect(numberOfArithmeticSlices(nums)).toBe(16); - }); -}); diff --git a/test/dynamic-programming/max-subarray.test.ts b/test/dynamic-programming/max-subarray.test.ts deleted file mode 100644 index aaef8b2..0000000 --- a/test/dynamic-programming/max-subarray.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { maxSubArray } from '../../src/dynamic-programming/max-subarray'; - -describe('maximum subarray', () => { - test('maximum subarray - test case 1', async () => { - expect(maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4])).toBe(6); - }); -}); diff --git a/test/dynamic-programming/word-break-i.test.ts b/test/dynamic-programming/word-break-i.test.ts deleted file mode 100644 index c5898b3..0000000 --- a/test/dynamic-programming/word-break-i.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { wordBreak } from '../../src/dynamic-programming/word-break-i'; - -describe('word break i', () => { - test('word break i - test case 1', () => { - const s = 'leetcode'; - const wordDict = ['leet', 'code']; - - expect(wordBreak(s, wordDict)).toBe(true); - }); -}); diff --git a/test/graph/__snapshots__/parallel-job-scheduling.test.ts.snap b/test/graph/__snapshots__/parallel-job-scheduling.test.ts.snap deleted file mode 100644 index 05a79f1..0000000 --- a/test/graph/__snapshots__/parallel-job-scheduling.test.ts.snap +++ /dev/null @@ -1,36 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`parallel job scheduling parallel job scheduling - test case 1 1`] = ` -[ - [ - "C", - "D", - "E", - "F", - ], - [ - "B", - ], - [ - "A", - ], -] -`; - -exports[`parallel job scheduling parallel job scheduling - test case 2 1`] = ` -[ - [ - "C", - "D", - "E", - "G", - ], - [ - "B", - "F", - ], - [ - "A", - ], -] -`; diff --git a/test/graph/__snapshots__/word-search-ii.test.ts.snap b/test/graph/__snapshots__/word-search-ii.test.ts.snap deleted file mode 100644 index 49811c9..0000000 --- a/test/graph/__snapshots__/word-search-ii.test.ts.snap +++ /dev/null @@ -1,17 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`word search ii word search ii - test case 1 1`] = ` -[ - "oath", - "eat", -] -`; - -exports[`word search ii word search ii - test case 4 1`] = ` -[ - "oath", - "eat", - "hf", - "hklf", -] -`; diff --git a/test/graph/add-edges-to-make-degrees-even.test.ts b/test/graph/add-edges-to-make-degrees-even.test.ts deleted file mode 100644 index 661a0f4..0000000 --- a/test/graph/add-edges-to-make-degrees-even.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { isPossible } from '../../src/graph/add-edges-to-make-degrees-even'; - -describe('add edges to make degrees of all nodes even', () => { - test('add edges to make degrees even - test case 1', async () => { - const n = 5; - const edges = [ - [1, 2], - [2, 3], - [3, 4], - [4, 2], - [1, 4], - [2, 5] - ]; - - expect(isPossible(n, edges)).toBe(true); - }); - - test('add edges to make degrees even - test case 2', async () => { - const n = 4; - const edges = [ - [1, 2], - [3, 4] - ]; - - expect(isPossible(n, edges)).toBe(true); - }); - - test('add edges to make degrees even - test case 3', async () => { - const n = 4; - const edges = [ - [1, 2], - [1, 3], - [1, 4] - ]; - - expect(isPossible(n, edges)).toBe(false); - }); - - test('add edges to make degrees even - test case 4', async () => { - const n = 4; - const edges = [ - [1, 2], - [2, 3], - [2, 4], - [3, 4] - ]; - - expect(isPossible(n, edges)).toBe(false); - }); -}); diff --git a/test/graph/bus-routes.test.ts b/test/graph/bus-routes.test.ts deleted file mode 100644 index 38e5323..0000000 --- a/test/graph/bus-routes.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { numBusesToDestination } from '../../src/graph/bus-routes'; - -describe('bus routes', () => { - test('bus routes - test case 2', async () => { - const routes = [[7, 12], [4, 5, 15], [6], [15, 19], [9, 12, 13]]; - - expect(numBusesToDestination(routes, 15, 12)).toBe(-1); - }); - - test('bus routes - test case 3', async () => { - const data = fs.readFileSync(path.join(__dirname, '__data__', 'bus-routes.test.json')).toString(); - const routes = JSON.parse(data); - - expect(numBusesToDestination(routes, 0, 100000)).toBe(-1); - }); -}); diff --git a/test/graph/nested-list-weighted-sum-ii.test.ts b/test/graph/nested-list-weighted-sum-ii.test.ts deleted file mode 100644 index ab6b936..0000000 --- a/test/graph/nested-list-weighted-sum-ii.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NestedInteger } from '../../src/linked-list/common/nested-integer'; -import { depthSumInverse } from '../../src/graph/nested-list-weighted-sum-ii'; - -describe('nested list weighted sum ii', () => { - test('nested list weighted sum ii - test case 1', async () => { - expect(depthSumInverse([new NestedInteger(10)])).toBe(10); - }); - - test('nested list weighted sum ii - test case 2', async () => { - expect(depthSumInverse([new NestedInteger(10), new NestedInteger(20)])).toBe(30); - }); -}); diff --git a/test/graph/nested-list-weighted-sum.test.ts b/test/graph/nested-list-weighted-sum.test.ts deleted file mode 100644 index f8791e9..0000000 --- a/test/graph/nested-list-weighted-sum.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NestedInteger } from '../../src/linked-list/common/nested-integer'; -import { depthSum } from '../../src/graph/nested-list-weighted-sum'; - -describe('nested list weighted sum', () => { - test('nested list weighted sum - test case 1', async () => { - expect(depthSum([new NestedInteger(10)])).toBe(10); - }); - - test('nested list weighted sum - test case 2', async () => { - expect(depthSum([new NestedInteger(10), new NestedInteger(20)])).toBe(30); - }); -}); diff --git a/test/graph/number-of-islands.test.ts b/test/graph/number-of-islands.test.ts deleted file mode 100644 index debc8ff..0000000 --- a/test/graph/number-of-islands.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { numIslands } from '../../src/graph/number-of-islands'; - -describe('number of islands', () => { - test('number of islands - test case 1', async () => { - const grid = [ - ['1', '1', '1', '1', '0'], - ['1', '1', '0', '1', '0'], - ['1', '1', '0', '0', '0'], - ['0', '0', '0', '0', '0'] - ]; - - expect(numIslands(grid)).toBe(1); - }); -}); diff --git a/test/graph/parallel-job-scheduling.test.ts b/test/graph/parallel-job-scheduling.test.ts deleted file mode 100644 index 14610a4..0000000 --- a/test/graph/parallel-job-scheduling.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { parallelize } from '../../src/graph/parallel-job-scheduling'; - -describe('parallel job scheduling', () => { - test('parallel job scheduling - test case 1', async () => { - const jobs = ['A:B,C', 'B:D,E,F']; - - expect(parallelize(jobs)).toMatchSnapshot(); - }); - - test('parallel job scheduling - test case 2', async () => { - const jobs = ['A:B,C', 'B:D,E', 'F:G']; - - expect(parallelize(jobs)).toMatchSnapshot(); - }); -}); diff --git a/test/graph/shortest-path-in-binary-matrix.test.ts b/test/graph/shortest-path-in-binary-matrix.test.ts deleted file mode 100644 index 3784761..0000000 --- a/test/graph/shortest-path-in-binary-matrix.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { shortestPathBinaryMatrix } from '../../src/graph/shortest-path-in-binary-matrix'; - -describe('shortest path in binary matrix', () => { - test('shortest path in binary matrix - test case 1', async () => { - const grid = [ - [0, 1], - [1, 0] - ]; - - expect(shortestPathBinaryMatrix(grid)).toBe(2); - }); - - test('shortest path in binary matrix - test case 2', async () => { - const grid = [ - [0, 1, 1, 0, 0, 0], - [0, 1, 0, 1, 1, 0], - [0, 1, 1, 0, 1, 0], - [0, 0, 0, 1, 1, 0], - [1, 1, 1, 1, 1, 0], - [1, 1, 1, 1, 1, 0] - ]; - - expect(shortestPathBinaryMatrix(grid)).toBe(14); - }); -}); diff --git a/test/graph/smallest-greater-multiple-made-of-two-digits.test.ts b/test/graph/smallest-greater-multiple-made-of-two-digits.test.ts deleted file mode 100644 index a25a398..0000000 --- a/test/graph/smallest-greater-multiple-made-of-two-digits.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { findInteger } from '../../src/graph/smallest-greater-multiple-made-of-two-digits'; - -describe('smallest greater multiple made of two digits', () => { - test('smallest greater multiple made of two digits - test case 1', async () => { - expect(findInteger(2, 0, 2)).toBe(20); - }); - - test('smallest greater multiple made of two digits - test case 2', async () => { - expect(findInteger(3, 4, 2)).toBe(24); - }); - - test('smallest greater multiple made of two digits - test case 1', async () => { - expect(findInteger(2, 0, 0)).toBe(-1); - }); -}); diff --git a/test/graph/word-search-i.test.ts b/test/graph/word-search-i.test.ts deleted file mode 100644 index 1129965..0000000 --- a/test/graph/word-search-i.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { exist } from '../../src/graph/word-search-i'; - -describe('word search i', () => { - test('word search i - test case 1', async () => { - const board = [ - ['A', 'B', 'C', 'E'], - ['S', 'F', 'C', 'S'], - ['A', 'D', 'E', 'E'] - ]; - const word = 'ABCCED'; - - expect(exist(board, word)).toBe(true); - }); -}); diff --git a/test/graph/word-search-ii.test.ts b/test/graph/word-search-ii.test.ts deleted file mode 100644 index 0549401..0000000 --- a/test/graph/word-search-ii.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { findWords } from '../../src/graph/word-search-ii'; - -describe('word search ii', () => { - test('word search ii - test case 1', async () => { - const board = [ - ['o', 'a', 'a', 'n'], - ['e', 't', 'a', 'e'], - ['i', 'h', 'k', 'r'], - ['i', 'f', 'l', 'v'] - ]; - const words = ['oath', 'pea', 'eat', 'rain']; - - expect(findWords(board, words)).toMatchSnapshot(); - }); - - test('word search ii - test case 2', async () => { - const board = [ - ['a', 'b'], - ['c', 'd'] - ]; - const words = ['abcb']; - - expect(findWords(board, words)).toStrictEqual([]); - }); - - test('word search ii - test case 3', async () => { - const board = [['a', 'a']]; - const words = ['aaa']; - - expect(findWords(board, words)).toStrictEqual([]); - }); - - test('word search ii - test case 4', async () => { - const board = [ - ['o', 'a', 'a', 'n'], - ['e', 't', 'a', 'e'], - ['i', 'h', 'k', 'r'], - ['i', 'f', 'l', 'v'] - ]; - const words = ['oath', 'pea', 'eat', 'rain', 'hklf', 'hf']; - - expect(findWords(board, words)).toMatchSnapshot(); - }); -}); diff --git a/test/heap/__data__/kth-largest-element.test.json b/test/heap/__data__/kth-largest-element.test.json deleted file mode 100644 index d574998..0000000 --- a/test/heap/__data__/kth-largest-element.test.json +++ /dev/null @@ -1 +0,0 @@ -[1,2,3,4,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-5,-4,-3,-2,-1] \ No newline at end of file diff --git a/test/heap/furthest-building-you-can-reach.test.ts b/test/heap/furthest-building-you-can-reach.test.ts deleted file mode 100644 index fd47144..0000000 --- a/test/heap/furthest-building-you-can-reach.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { furthestBuilding } from '../../src/heap/furthest-building-you-can-reach'; - -describe('furthest building you can reach', () => { - test('furthest building - test case 1', async () => { - const heights = [4, 2, 7, 6, 9, 14, 12]; - expect(furthestBuilding(heights, 5, 1)).toBe(4); - }); - - test('furthest building - test case 2', async () => { - const heights = [4, 12, 2, 7, 3, 18, 20, 3, 19]; - expect(furthestBuilding(heights, 10, 2)).toBe(7); - }); - - test('furthest building - test case 3', async () => { - const heights = [14, 3, 19, 3]; - expect(furthestBuilding(heights, 17, 0)).toBe(3); - }); -}); diff --git a/test/heap/k-closest-points-to-origin.test.ts b/test/heap/k-closest-points-to-origin.test.ts deleted file mode 100644 index cf02156..0000000 --- a/test/heap/k-closest-points-to-origin.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { kClosest } from '../../src/heap/k-closest-points-to-origin'; - -describe('k closest points to origin', () => { - test('k closest - test case 1', () => { - expect( - kClosest( - [ - [1, 3], - [-2, 2] - ], - 1 - ) - ).toStrictEqual([[-2, 2]]); - }); -}); diff --git a/test/heap/kth-largest-element.test.ts b/test/heap/kth-largest-element.test.ts deleted file mode 100644 index 25ed297..0000000 --- a/test/heap/kth-largest-element.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { findKthLargest } from '../../src/heap/kth-largest-element'; - -describe('kth largest element in an array', () => { - test('find kth largest - test case 1', async () => { - expect(findKthLargest([3, 2, 1, 5, 6, 4], 2)).toBe(5); - }); - - test('find kth largest - test case 2', async () => { - expect(findKthLargest([3, 2, 3, 1, 2, 4, 5, 5, 6], 4)).toBe(4); - }); - - test.skip('find kth largest - test case 3', async () => { - const data = fs.readFileSync(path.join(__dirname, '__data__', 'kth-largest-element.test.json')).toString(); - const array = JSON.parse(data); - - expect(findKthLargest(array, 50000)).toBe(1); - }); -}); diff --git a/test/heap/minimize-deviation-in-array.test.ts b/test/heap/minimize-deviation-in-array.test.ts deleted file mode 100644 index 34c6733..0000000 --- a/test/heap/minimize-deviation-in-array.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { minimumDeviation } from '../../src/heap/minimize-deviation-in-array'; - -describe('minimize deviation in array', () => { - test('minimum deviation - test case 1', async () => { - const xs = [1, 2, 3, 4]; - - expect(minimumDeviation(xs)).toBe(1); - }); - - test('minimum deviation - test case 2', async () => { - const xs = [4, 1, 5, 20, 3]; - - expect(minimumDeviation(xs)).toBe(3); - }); - - test('minimum deviation - test case 3', async () => { - const xs = [2, 10, 8]; - - expect(minimumDeviation(xs)).toBe(3); - }); -}); diff --git a/test/heap/numbers-of-orders-in-the-backlog.test.ts b/test/heap/numbers-of-orders-in-the-backlog.test.ts deleted file mode 100644 index ced2874..0000000 --- a/test/heap/numbers-of-orders-in-the-backlog.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { getNumberOfBacklogOrders } from '../../src/heap/numbers-of-orders-in-the-backlog'; - -describe('number of orders in the backlog', () => { - test('number of backlog orders - test case 1', async () => { - const orders = [ - [10, 5, 0], - [15, 2, 1], - [25, 1, 1], - [30, 4, 0] - ]; - - expect(getNumberOfBacklogOrders(orders)).toBe(6); - }); - - test('number of backlog orders - test case 2', async () => { - const orders = [ - [7, 1000000000, 1], - [15, 3, 0], - [5, 999999995, 0], - [5, 1, 1] - ]; - - expect(getNumberOfBacklogOrders(orders)).toBe(999999984); - }); - - test('number of backlog orders - test case 3', async () => { - const orders = [ - [19, 28, 0], - [9, 4, 1], - [25, 15, 1] - ]; - - expect(getNumberOfBacklogOrders(orders)).toBe(39); - }); - - test('number of backlog orders - test case 4', async () => { - const orders = [ - [26, 7, 0], - [16, 1, 1], - [14, 20, 0], - [23, 15, 1], - [24, 26, 0], - [19, 4, 1], - [1, 1, 0] - ]; - - expect(getNumberOfBacklogOrders(orders)).toBe(34); - }); - - test('number of backlog orders - test case 5', async () => { - const orders = [ - [1, 29, 1], - [22, 7, 1], - [24, 1, 0], - [25, 15, 1], - [18, 8, 1], - [8, 22, 0], - [25, 15, 1], - [30, 1, 1], - [27, 30, 0] - ]; - - expect(getNumberOfBacklogOrders(orders)).toBe(22); - }); -}); diff --git a/test/heap/task-scheduler.test.ts b/test/heap/task-scheduler.test.ts deleted file mode 100644 index fb6bcdd..0000000 --- a/test/heap/task-scheduler.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { leastInterval } from '../../src/heap/task-scheduler'; - -describe('task scheduler', () => { - test('task scheduler - test case 1', () => { - // [A, B, _, A, B, _, A, B] - expect(leastInterval(['A', 'A', 'A', 'B', 'B', 'B'], 2)).toBe(8); - }); - - test('task scheduler - test case 2', () => { - // [A, B, A, B, C, D] - expect(leastInterval(['A', 'C', 'A', 'B', 'D', 'B'], 1)).toBe(6); - }); - - test('task scheduler - test case 3', () => { - // [A, B, _, _, A, B, _, _, A, B] - // [A, _, _, _, A, _, _, _] - expect(leastInterval(['A', 'A', 'A', 'B', 'B', 'B'], 3)).toBe(10); - }); - - test.skip('task scheduler - test case 4', async () => { - const data = fs.readFileSync(path.join(__dirname, '__data__', 'task-scheduler.test.json')).toString(); - const tasks = JSON.parse(data); - - expect(leastInterval(tasks, 1)).toBe(1000); - }); -}); diff --git a/test/interval/interval-list-intersection.test.ts b/test/interval/interval-list-intersection.test.ts deleted file mode 100644 index ce12705..0000000 --- a/test/interval/interval-list-intersection.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { intervalIntersection } from '../../src/interval/interval-list-intersections'; - -describe('interval list intersection', () => { - test('interval list intersection - test case 1', async () => { - const firstList = [ - [0, 2], - [5, 10], - [13, 23], - [24, 25] - ]; - const secondList = [ - [1, 5], - [8, 12], - [15, 24], - [25, 26] - ]; - - expect(intervalIntersection(firstList, secondList)).toStrictEqual([ - [1, 2], - [5, 5], - [8, 10], - [15, 23], - [24, 24], - [25, 25] - ]); - }); -}); diff --git a/test/interval/meeting-rooms.test.ts b/test/interval/meeting-rooms.test.ts deleted file mode 100644 index f4b36fa..0000000 --- a/test/interval/meeting-rooms.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { canAttendMeetings } from '../../src/interval/meeting-rooms'; - -describe('meeting rooms', () => { - test('meeting rooms - test case 1', async () => { - expect( - canAttendMeetings([ - [0, 30], - [5, 10], - [15, 20] - ]) - ).toBe(false); - }); -}); diff --git a/test/interval/meeting-scheduler.test.ts b/test/interval/meeting-scheduler.test.ts deleted file mode 100644 index acf582e..0000000 --- a/test/interval/meeting-scheduler.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { minAvailableDuration } from '../../src/interval/meeting-scheduler'; - -describe('meeting scheduler', () => { - test('meeting scheduler - test case 1', async () => { - expect( - minAvailableDuration( - [ - [10, 50], - [60, 120], - [140, 210] - ], - [ - [0, 15], - [60, 70] - ], - 8 - ) - ).toStrictEqual([60, 68]); - }); -}); diff --git a/test/interval/merge-intervals.test.ts b/test/interval/merge-intervals.test.ts deleted file mode 100644 index 1ad3777..0000000 --- a/test/interval/merge-intervals.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { merge } from '../../src/interval/merge-intervals'; - -describe('merge intervals', () => { - test('merge intervals - test case 1', async () => { - expect( - merge([ - [1, 3], - [2, 6], - [8, 10], - [15, 18] - ]) - ).toStrictEqual([ - [1, 6], - [8, 10], - [15, 18] - ]); - }); -}); diff --git a/test/jest.config.mjs b/test/jest.config.mjs deleted file mode 100644 index a5c5568..0000000 --- a/test/jest.config.mjs +++ /dev/null @@ -1,12 +0,0 @@ -export default { - clearMocks: true, - moduleFileExtensions: ['js', 'ts'], - modulePaths: [''], - testEnvironment: 'node', - testMatch: ['**/*.test.ts'], - testTimeout: 1000, - transform: { - '^.+\\.ts$': 'ts-jest' - }, - verbose: true -}; diff --git a/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py b/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py new file mode 100644 index 0000000..583dcbe --- /dev/null +++ b/test/leetcode/array/best_time_to_buy_and_sell_stock_ii_test.py @@ -0,0 +1,16 @@ +from leetcode.heap.furthest_building_you_can_reach import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.furthestBuilding([4, 2, 7, 6, 9, 14, 12], 5, 1) == 4 + + +def test_case_2(): + assert soln.furthestBuilding([4, 12, 2, 7, 3, 18, 20, 3, 19], 10, 2) == 7 + + +def test_case_3(): + assert soln.furthestBuilding([14, 3, 19, 3], 17, 0) == 3 diff --git a/test/leetcode/array/buildings_with_an_ocean_view_test.py b/test/leetcode/array/buildings_with_an_ocean_view_test.py new file mode 100644 index 0000000..bac7360 --- /dev/null +++ b/test/leetcode/array/buildings_with_an_ocean_view_test.py @@ -0,0 +1,8 @@ +from leetcode.array.buildings_with_an_ocean_view import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.findBuildings([4, 2, 3, 1]) == [0, 2, 3] diff --git a/test/leetcode/array/design_hit_counter_test.py b/test/leetcode/array/design_hit_counter_test.py new file mode 100644 index 0000000..7a5aa5a --- /dev/null +++ b/test/leetcode/array/design_hit_counter_test.py @@ -0,0 +1,17 @@ +from leetcode.array.design_hit_counter import HitCounter + + +hc = HitCounter() + + +def test_case_1(): + hc.hit(1) + hc.hit(2) + hc.hit(3) + + assert hc.getHits(4) == 3 + + hc.hit(300) + + assert hc.getHits(300) == 4 + assert hc.getHits(301) == 3 diff --git a/test/leetcode/array/dot_product_of_two_sparse_vectors_test.py b/test/leetcode/array/dot_product_of_two_sparse_vectors_test.py new file mode 100644 index 0000000..83f8303 --- /dev/null +++ b/test/leetcode/array/dot_product_of_two_sparse_vectors_test.py @@ -0,0 +1,8 @@ +from leetcode.array.dot_product_of_two_sparse_vectors import SparseVector + + +def test_case_1(): + a = SparseVector([1, 0, 0, 2, 3]) + b = SparseVector([0, 3, 0, 4, 0]) + + assert a.dotProduct(b) == 8 diff --git a/test/leetcode/array/group_anagrams_test.py b/test/leetcode/array/group_anagrams_test.py new file mode 100644 index 0000000..09b620f --- /dev/null +++ b/test/leetcode/array/group_anagrams_test.py @@ -0,0 +1,8 @@ +from leetcode.array.group_anagrams import Solution + + +soln = Solution() + + +def test_case_1(snapshot): + snapshot.assert_match(soln.groupAnagrams(["eat", "tea", "tan", "ate", "nat", "bat"])) diff --git a/test/leetcode/array/longest_consecutive_sequence_test.py b/test/leetcode/array/longest_consecutive_sequence_test.py new file mode 100644 index 0000000..ae656d2 --- /dev/null +++ b/test/leetcode/array/longest_consecutive_sequence_test.py @@ -0,0 +1,8 @@ +from leetcode.array.longest_consecutive_sequence import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.longestConsecutive([100, 4, 200, 1, 3, 2]) == 4 diff --git a/test/leetcode/array/max_chunks_to_make_sorted_test.py b/test/leetcode/array/max_chunks_to_make_sorted_test.py new file mode 100644 index 0000000..250e4a9 --- /dev/null +++ b/test/leetcode/array/max_chunks_to_make_sorted_test.py @@ -0,0 +1,8 @@ +from leetcode.array.max_chunks_to_make_sorted import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maxChunksToSorted([4, 3, 2, 1, 0]) == 1 diff --git a/test/leetcode/array/maximum_swap_test.py b/test/leetcode/array/maximum_swap_test.py new file mode 100644 index 0000000..c0702bb --- /dev/null +++ b/test/leetcode/array/maximum_swap_test.py @@ -0,0 +1,16 @@ +from leetcode.array.maximum_swap import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maximumSwap(2736) == 7236 + + +def test_case_2(): + assert soln.maximumSwap(1993) == 9913 + + +def test_case_3(): + assert soln.maximumSwap(98368) == 98863 diff --git a/test/leetcode/array/merge_sorted_array_test.py b/test/leetcode/array/merge_sorted_array_test.py new file mode 100644 index 0000000..7b3ec07 --- /dev/null +++ b/test/leetcode/array/merge_sorted_array_test.py @@ -0,0 +1,26 @@ +from leetcode.array.merge_sorted_array import Solution + + +soln = Solution() + + +def test_case_1(): + xs = [1, 2, 3, 0, 0, 0] + m = 3 + ys = [2, 5, 6] + n = 3 + + soln.merge(xs, m, ys, n) + + assert xs == [1, 2, 2, 3, 5, 6] + + +def test_case_2(): + xs = [4, 5, 6, 0, 0, 0] + m = 3 + ys = [1, 2, 3] + n = 3 + + soln.merge(xs, m, ys, n) + + assert xs == [1, 2, 3, 4, 5, 6] diff --git a/test/leetcode/array/pascals_triangle_test.py b/test/leetcode/array/pascals_triangle_test.py new file mode 100644 index 0000000..c02e794 --- /dev/null +++ b/test/leetcode/array/pascals_triangle_test.py @@ -0,0 +1,8 @@ +from leetcode.array.pascals_triangle import Solution + + +soln = Solution() + + +def test_case_1(snapshot): + snapshot.assert_match(soln.generate(5)) diff --git a/test/leetcode/array/snapshots/snap_group_anagrams_test.py b/test/leetcode/array/snapshots/snap_group_anagrams_test.py new file mode 100644 index 0000000..3136f04 --- /dev/null +++ b/test/leetcode/array/snapshots/snap_group_anagrams_test.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots["test_case_1 1"] = [["eat", "tea", "ate"], ["tan", "nat"], ["bat"]] diff --git a/test/leetcode/array/snapshots/snap_pascals_triangle_test.py b/test/leetcode/array/snapshots/snap_pascals_triangle_test.py new file mode 100644 index 0000000..197865d --- /dev/null +++ b/test/leetcode/array/snapshots/snap_pascals_triangle_test.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots["test_case_1 1"] = [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]] diff --git a/test/leetcode/array/top_k_frequent_elements_test.py b/test/leetcode/array/top_k_frequent_elements_test.py new file mode 100644 index 0000000..b6833a6 --- /dev/null +++ b/test/leetcode/array/top_k_frequent_elements_test.py @@ -0,0 +1,8 @@ +from leetcode.array.top_k_frequent_elements import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.topKFrequent([1, 1, 1, 2, 2, 3], 2) == [1, 2] diff --git a/test/leetcode/array/two_sum_test.py b/test/leetcode/array/two_sum_test.py new file mode 100644 index 0000000..5f08564 --- /dev/null +++ b/test/leetcode/array/two_sum_test.py @@ -0,0 +1,16 @@ +from leetcode.array.two_sum import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.twoSum([2, 7, 11, 15], 9) == [1, 0] + + +def test_case_2(): + assert soln.twoSum([3, 2, 4], 6) == [2, 1] + + +def test_case_3(): + assert soln.twoSum([3, 3], 6) == [1, 0] diff --git a/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py b/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py new file mode 100644 index 0000000..b7ac30b --- /dev/null +++ b/test/leetcode/binary_search/find_first_and_last_position_of_element_in_sorted_array_test.py @@ -0,0 +1,10 @@ +from leetcode.binary_search.find_first_and_last_position_of_element_in_sorted_array import ( + Solution, +) + + +soln = Solution() + + +def test_case_1(): + assert soln.searchRange([5, 7, 7, 8, 8, 10], 8) == [3, 4] diff --git a/test/leetcode/binary_search/find_peak_element_test.py b/test/leetcode/binary_search/find_peak_element_test.py new file mode 100644 index 0000000..42f5995 --- /dev/null +++ b/test/leetcode/binary_search/find_peak_element_test.py @@ -0,0 +1,28 @@ +from leetcode.binary_search.find_peak_element import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.findPeakElement([1, 2, 3, 1]) == 2 + + +def test_case_2(): + assert soln.findPeakElement([1, 2, 1, 3, 5, 6, 4]) == 5 + + +def test_case_3(): + assert soln.findPeakElement([1]) == 0 + + +def test_case_4(): + assert soln.findPeakElement([2, 1]) == 0 + + +def test_case_5(): + assert soln.findPeakElement([-2147483647, -2147483648]) == 0 + + +def test_case_6(): + assert soln.findPeakElement([1, 2]) == 1 diff --git a/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py b/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py new file mode 100644 index 0000000..6c757bc --- /dev/null +++ b/test/leetcode/binary_search/find_the_smallest_divisor_given_a_threshold_test.py @@ -0,0 +1,8 @@ +from leetcode.binary_search.find_the_smallest_divisor_given_a_threshold import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.smallestDivisor([1, 2, 5, 9], 6) == 5 diff --git a/test/leetcode/binary_search/kth_missing_positive_number_test.py b/test/leetcode/binary_search/kth_missing_positive_number_test.py new file mode 100644 index 0000000..22a4a63 --- /dev/null +++ b/test/leetcode/binary_search/kth_missing_positive_number_test.py @@ -0,0 +1,12 @@ +from leetcode.binary_search.kth_missing_positive_number import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.findKthPositive([2, 3, 4, 7, 11], 5) == 9 + + +def test_case_2(): + assert soln.findKthPositive([1, 2, 3, 4], 2) == 6 diff --git a/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py b/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py new file mode 100644 index 0000000..bce2baf --- /dev/null +++ b/test/leetcode/binary_search/kth_smallest_element_in_a_sorted_matrix_test.py @@ -0,0 +1,11 @@ +from leetcode.binary_search.kth_smallest_element_in_a_sorted_matrix import Solution + + +soln = Solution() + + +def test_case_1(): + matrix = [[1, 5, 9], [10, 11, 13], [12, 13, 15]] + k = 8 + + assert soln.kthSmallest(matrix, k) == 13 diff --git a/test/leetcode/binary_search/longest_increasing_subsequence_test.py b/test/leetcode/binary_search/longest_increasing_subsequence_test.py new file mode 100644 index 0000000..d73e756 --- /dev/null +++ b/test/leetcode/binary_search/longest_increasing_subsequence_test.py @@ -0,0 +1,8 @@ +from leetcode.binary_search.longest_increasing_subsequence import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18]) == 4 diff --git a/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py b/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py new file mode 100644 index 0000000..3b87ffc --- /dev/null +++ b/test/leetcode/binary_search/median_of_two_sorted_arrays_test.py @@ -0,0 +1,12 @@ +from leetcode.binary_search.median_of_two_sorted_arrays import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.findMedianSortedArrays([1, 3], [2]) == 2 + + +def test_case_2(): + assert soln.findMedianSortedArrays([3, 2, 3, 1, 2, 4, 5, 5, 6], [4]) == 3 diff --git a/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py b/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py new file mode 100644 index 0000000..957acac --- /dev/null +++ b/test/leetcode/binary_search/search_in_rotated_sorted_array_test.py @@ -0,0 +1,20 @@ +from leetcode.binary_search.search_in_rotated_sorted_array import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.search([4, 5, 6, 7, 0, 1, 2], 0) == 4 + + +def test_case_2(): + assert soln.search([4, 5, 6, 7, 0, 1, 2], 3) == -1 + + +def test_case_3(): + assert soln.search([1], 0) == -1 + + +def test_case_4(): + assert soln.search([3, 1], 1) == 1 diff --git a/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py b/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py new file mode 100644 index 0000000..5b148f9 --- /dev/null +++ b/test/leetcode/dynamic_programming/arithmetic_slices_ii_subsequences_test.py @@ -0,0 +1,12 @@ +from leetcode.dynamic_programming.arithmetic_slices_ii_subsequences import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.numberOfArithmeticSlices([2, 4, 6, 8, 10]) == 7 + + +def test_case_2(): + assert soln.numberOfArithmeticSlices([7, 7, 7, 7, 7]) == 16 diff --git a/test/leetcode/dynamic_programming/max_subarray_test.py b/test/leetcode/dynamic_programming/max_subarray_test.py new file mode 100644 index 0000000..802a692 --- /dev/null +++ b/test/leetcode/dynamic_programming/max_subarray_test.py @@ -0,0 +1,8 @@ +from leetcode.dynamic_programming.max_subarray import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4]) == 6 diff --git a/test/leetcode/dynamic_programming/word_break_i_test.py b/test/leetcode/dynamic_programming/word_break_i_test.py new file mode 100644 index 0000000..dc88c05 --- /dev/null +++ b/test/leetcode/dynamic_programming/word_break_i_test.py @@ -0,0 +1,8 @@ +from leetcode.dynamic_programming.word_break_i import Solution + + +soln = Solution() + + +def test_case_i(): + assert soln.wordBreak("leetcode", ["leet", "code"]) diff --git a/test/leetcode/graph/add_edges_to_make_degrees_even_test.py b/test/leetcode/graph/add_edges_to_make_degrees_even_test.py new file mode 100644 index 0000000..35e7754 --- /dev/null +++ b/test/leetcode/graph/add_edges_to_make_degrees_even_test.py @@ -0,0 +1,32 @@ +from leetcode.graph.add_edges_to_make_degrees_even import Solution + + +soln = Solution() + + +def test_case_1(): + n = 5 + edges = [[1, 2], [2, 3], [3, 4], [4, 2], [1, 4], [2, 5]] + + assert soln.isPossible(n, edges) + + +def test_case_2(): + n = 4 + edges = [[1, 2], [3, 4]] + + assert soln.isPossible(n, edges) + + +def test_case_3(): + n = 4 + edges = [[1, 2], [1, 3], [1, 4]] + + assert not soln.isPossible(n, edges) + + +def test_case_4(): + n = 4 + edges = [[1, 2], [2, 3], [2, 4], [3, 4]] + + assert not soln.isPossible(n, edges) diff --git a/test/leetcode/graph/bus_routes_test.py b/test/leetcode/graph/bus_routes_test.py new file mode 100644 index 0000000..2c22d1c --- /dev/null +++ b/test/leetcode/graph/bus_routes_test.py @@ -0,0 +1,18 @@ +import json +import pathlib +from leetcode.graph.bus_routes import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.numBusesToDestination([[7, 12], [4, 5, 15], [6], [15, 19], [9, 12, 13]], 15, 12) == -1 + + +def test_case_2(): + path = pathlib.Path(__file__).parent / "data" / "bus_routes.json" + assert path.exists() + with path.open("r", encoding="utf-8") as file: + routes = json.load(file) + assert soln.numBusesToDestination(routes, 0, 100000) == -1 diff --git a/test/graph/__data__/bus-routes.test.json b/test/leetcode/graph/data/bus_routes.json similarity index 100% rename from test/graph/__data__/bus-routes.test.json rename to test/leetcode/graph/data/bus_routes.json diff --git a/test/leetcode/graph/number_of_islands_test.py b/test/leetcode/graph/number_of_islands_test.py new file mode 100644 index 0000000..67044ac --- /dev/null +++ b/test/leetcode/graph/number_of_islands_test.py @@ -0,0 +1,10 @@ +from leetcode.graph.number_of_islands import Solution + + +soln = Solution() + + +def test_case_1(): + grid = [["1", "1", "1", "1", "0"], ["1", "1", "0", "1", "0"], ["1", "1", "0", "0", "0"], ["0", "0", "0", "0", "0"]] + + assert soln.numIslands(grid) == 1 diff --git a/test/leetcode/graph/parallel_job_scheduling_test.py b/test/leetcode/graph/parallel_job_scheduling_test.py new file mode 100644 index 0000000..4c00b8b --- /dev/null +++ b/test/leetcode/graph/parallel_job_scheduling_test.py @@ -0,0 +1,18 @@ +from leetcode.graph.parallel_job_scheduling import Solution + + +soln = Solution() + + +def test_case_1(snapshot): + result = soln.parallelize(["A:B,C", "B:D,E,F"]) + + expected = [sorted(xs) for xs in result] + snapshot.assert_match(sorted(expected)) + + +def test_case_2(snapshot): + result = soln.parallelize(["A:B,C", "B:D,E", "F:G"]) + + expected = [sorted(xs) for xs in result] + snapshot.assert_match(sorted(expected)) diff --git a/test/leetcode/graph/shortest_path_in_binary_matrix_test.py b/test/leetcode/graph/shortest_path_in_binary_matrix_test.py new file mode 100644 index 0000000..ea2891d --- /dev/null +++ b/test/leetcode/graph/shortest_path_in_binary_matrix_test.py @@ -0,0 +1,23 @@ +from leetcode.graph.shortest_path_in_binary_matrix import Solution + + +soln = Solution() + + +def test_case_1(): + grid = [[0, 1], [1, 0]] + + assert soln.shortestPathBinaryMatrix(grid) == 2 + + +def test_case_2(): + grid = [ + [0, 1, 1, 0, 0, 0], + [0, 1, 0, 1, 1, 0], + [0, 1, 1, 0, 1, 0], + [0, 0, 0, 1, 1, 0], + [1, 1, 1, 1, 1, 0], + [1, 1, 1, 1, 1, 0], + ] + + assert soln.shortestPathBinaryMatrix(grid) == 14 diff --git a/test/leetcode/graph/smallest_greater_multiple_made_of_two_digits_test.py b/test/leetcode/graph/smallest_greater_multiple_made_of_two_digits_test.py new file mode 100644 index 0000000..5d21677 --- /dev/null +++ b/test/leetcode/graph/smallest_greater_multiple_made_of_two_digits_test.py @@ -0,0 +1,16 @@ +from leetcode.graph.smallest_greater_multiple_made_of_two_digits import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.findInteger(2, 0, 2) == 20 + + +def test_case_2(): + assert soln.findInteger(3, 4, 2) == 24 + + +def test_case_3(): + assert soln.findInteger(2, 0, 0) == -1 diff --git a/test/leetcode/graph/snapshots/snap_parallel_job_scheduling_test.py b/test/leetcode/graph/snapshots/snap_parallel_job_scheduling_test.py new file mode 100644 index 0000000..0016a1f --- /dev/null +++ b/test/leetcode/graph/snapshots/snap_parallel_job_scheduling_test.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_case_1 1'] = [ + [ + 'A' + ], + [ + 'B', + 'C' + ], + [ + 'D', + 'E', + 'F' + ] +] + +snapshots['test_case_2 1'] = [ + [ + 'A', + 'F' + ], + [ + 'B', + 'C', + 'G' + ], + [ + 'D', + 'E' + ] +] diff --git a/test/leetcode/graph/snapshots/snap_word_search_ii_test.py b/test/leetcode/graph/snapshots/snap_word_search_ii_test.py new file mode 100644 index 0000000..0049be4 --- /dev/null +++ b/test/leetcode/graph/snapshots/snap_word_search_ii_test.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots["test_case_1 1"] = ["eat", "oath"] + +snapshots["test_case_4 1"] = ["eat", "hf", "hklf", "oath"] diff --git a/test/leetcode/graph/word_search_ii_test.py b/test/leetcode/graph/word_search_ii_test.py new file mode 100644 index 0000000..48cf660 --- /dev/null +++ b/test/leetcode/graph/word_search_ii_test.py @@ -0,0 +1,36 @@ +from leetcode.graph.word_search_ii import Solution + + +soln = Solution() + + +def test_case_1(snapshot): + board = [["o", "a", "a", "n"], ["e", "t", "a", "e"], ["i", "h", "k", "r"], ["i", "f", "l", "v"]] + words = ["oath", "pea", "eat", "rain"] + + result = soln.findWords(board, words) + + snapshot.assert_match(sorted(result)) + + +def test_case_2(): + board = [["a", "b"], ["c", "d"]] + words = ["abcb"] + + assert soln.findWords(board, words) == [] + + +def test_case_3(): + board = [["a", "a"]] + words = ["aaa"] + + assert soln.findWords(board, words) == [] + + +def test_case_4(snapshot): + board = [["o", "a", "a", "n"], ["e", "t", "a", "e"], ["i", "h", "k", "r"], ["i", "f", "l", "v"]] + words = ["oath", "pea", "eat", "rain", "hklf", "hf"] + + result = soln.findWords(board, words) + + snapshot.assert_match(sorted(result)) diff --git a/test/leetcode/graph/word_search_test.py b/test/leetcode/graph/word_search_test.py new file mode 100644 index 0000000..33fbeba --- /dev/null +++ b/test/leetcode/graph/word_search_test.py @@ -0,0 +1,18 @@ +from leetcode.graph.word_search import Solution + + +soln = Solution() + + +def test_case_1(): + board = [["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]] + word = "ABCCED" + + assert soln.exist(board, word) + + +def test_case_2(): + board = [["A", "B", "C", "E"], ["S", "F", "C", "S"], ["A", "D", "E", "E"]] + word = "SEE" + + assert soln.exist(board, word) diff --git a/test/leetcode/heap/furthest_building_you_can_reach_test.py b/test/leetcode/heap/furthest_building_you_can_reach_test.py new file mode 100644 index 0000000..583dcbe --- /dev/null +++ b/test/leetcode/heap/furthest_building_you_can_reach_test.py @@ -0,0 +1,16 @@ +from leetcode.heap.furthest_building_you_can_reach import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.furthestBuilding([4, 2, 7, 6, 9, 14, 12], 5, 1) == 4 + + +def test_case_2(): + assert soln.furthestBuilding([4, 12, 2, 7, 3, 18, 20, 3, 19], 10, 2) == 7 + + +def test_case_3(): + assert soln.furthestBuilding([14, 3, 19, 3], 17, 0) == 3 diff --git a/test/leetcode/heap/k_closest_points_to_origin_test.py b/test/leetcode/heap/k_closest_points_to_origin_test.py new file mode 100644 index 0000000..814811b --- /dev/null +++ b/test/leetcode/heap/k_closest_points_to_origin_test.py @@ -0,0 +1,8 @@ +from leetcode.heap.k_closest_points_to_origin import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.kClosest([[1, 3], [-2, 2]], 1) == [[-2, 2]] diff --git a/test/leetcode/heap/kth_largest_element_test.py b/test/leetcode/heap/kth_largest_element_test.py new file mode 100644 index 0000000..b199e7a --- /dev/null +++ b/test/leetcode/heap/kth_largest_element_test.py @@ -0,0 +1,12 @@ +from leetcode.heap.kth_largest_element import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.findKthLargest([3, 2, 1, 5, 6, 4], 2) == 5 + + +def test_case_2(): + assert soln.findKthLargest([3, 2, 3, 1, 2, 4, 5, 5, 6], 4) == 4 diff --git a/test/leetcode/heap/minimize_deviation_in_array_test.py b/test/leetcode/heap/minimize_deviation_in_array_test.py new file mode 100644 index 0000000..773aa81 --- /dev/null +++ b/test/leetcode/heap/minimize_deviation_in_array_test.py @@ -0,0 +1,16 @@ +from leetcode.heap.minimize_deviation_in_array import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.minimumDeviation([1, 2, 3, 4]) == 1 + + +def test_case_2(): + assert soln.minimumDeviation([4, 1, 5, 20, 3]) == 3 + + +def test_case_3(): + assert soln.minimumDeviation([2, 10, 8]) == 3 diff --git a/test/leetcode/heap/number_of_orders_in_the_backlog_test.py b/test/leetcode/heap/number_of_orders_in_the_backlog_test.py new file mode 100644 index 0000000..1200c2a --- /dev/null +++ b/test/leetcode/heap/number_of_orders_in_the_backlog_test.py @@ -0,0 +1,44 @@ +from leetcode.heap.number_of_orders_in_the_backlog import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.getNumberOfBacklogOrders([[10, 5, 0], [15, 2, 1], [25, 1, 1], [30, 4, 0]]) == 6 + + +def test_case_2(): + assert soln.getNumberOfBacklogOrders([[7, 1000000000, 1], [15, 3, 0], [5, 999999995, 0], [5, 1, 1]]) == 999999984 + + +def test_case_3(): + assert soln.getNumberOfBacklogOrders([[19, 28, 0], [9, 4, 1], [25, 15, 1]]) == 39 + + +def test_case_4(): + assert ( + soln.getNumberOfBacklogOrders( + [[26, 7, 0], [16, 1, 1], [14, 20, 0], [23, 15, 1], [24, 26, 0], [19, 4, 1], [1, 1, 0]] + ) + == 34 + ) + + +def test_case_5(): + assert ( + soln.getNumberOfBacklogOrders( + [ + [1, 29, 1], + [22, 7, 1], + [24, 1, 0], + [25, 15, 1], + [18, 8, 1], + [8, 22, 0], + [25, 15, 1], + [30, 1, 1], + [27, 30, 0], + ] + ) + == 22 + ) diff --git a/test/leetcode/heap/task_scheduler_test.py b/test/leetcode/heap/task_scheduler_test.py new file mode 100644 index 0000000..b84cdd7 --- /dev/null +++ b/test/leetcode/heap/task_scheduler_test.py @@ -0,0 +1,20 @@ +from leetcode.heap.task_scheduler import Solution + + +soln = Solution() + + +def test_case_1(): + # [A, B, _, A, B, _, A, B] + assert soln.leastInterval(["A", "A", "A", "B", "B", "B"], 2) == 8 + + +def test_case_2(): + # [A, B, A, B, C, D] + assert soln.leastInterval(["A", "C", "A", "B", "D", "B"], 1) == 6 + + +def test_case_3(): + # [A, B, _, _, A, B, _, _, A, B] + # [A, _, _, _, A, _, _, _] + assert soln.leastInterval(["A", "A", "A", "B", "B", "B"], 3) == 10 diff --git a/test/leetcode/hybrid/lru_cache_test.py b/test/leetcode/hybrid/lru_cache_test.py new file mode 100644 index 0000000..be93654 --- /dev/null +++ b/test/leetcode/hybrid/lru_cache_test.py @@ -0,0 +1,59 @@ +from leetcode.hybrid.lru_cache import LRUCache + + +def test_case_1(): + cache = LRUCache(2) + + cache.put(1, 1) + cache.put(2, 2) + + assert cache.get(1) == 1 + + cache.put(3, 3) + + assert cache.get(2) == -1 + + cache.put(4, 4) + + assert cache.get(1) == -1 + assert cache.get(3) == 3 + assert cache.get(4) == 4 + + +def test_case_2(): + cache = LRUCache(2) + + assert cache.get(2) == -1 + assert cache.get(2) == -1 + + cache.put(2, 6) + + assert cache.get(1) == -1 + + cache.put(1, 5) + cache.put(1, 2) + + assert cache.get(1) == 2 + assert cache.get(2) == 6 + + +def test_case_3(): + cache = LRUCache(3) + + cache.put(1, 1) + cache.put(2, 2) + cache.put(3, 3) + cache.put(4, 4) + + assert cache.get(4) == 4 + assert cache.get(3) == 3 + assert cache.get(2) == 2 + assert cache.get(1) == -1 + + cache.put(5, 5) + + assert cache.get(1) == -1 + assert cache.get(2) == 2 + assert cache.get(3) == 3 + assert cache.get(4) == -1 + assert cache.get(5) == 5 diff --git a/test/leetcode/hybrid/max_stack_test.py b/test/leetcode/hybrid/max_stack_test.py new file mode 100644 index 0000000..77fa93f --- /dev/null +++ b/test/leetcode/hybrid/max_stack_test.py @@ -0,0 +1,17 @@ +from leetcode.hybrid.max_stack import MaxStack + + +stack = MaxStack() + + +def test_case_1(): + stack.push(5) + stack.push(1) + stack.push(5) + + assert stack.top() == 5 + assert stack.popMax() == 5 + assert stack.top() == 1 + assert stack.peekMax() == 5 + assert stack.pop() == 1 + assert stack.top() == 5 diff --git a/test/leetcode/interval/interval_list_intersection_test.py b/test/leetcode/interval/interval_list_intersection_test.py new file mode 100644 index 0000000..5d3ba6e --- /dev/null +++ b/test/leetcode/interval/interval_list_intersection_test.py @@ -0,0 +1,11 @@ +from leetcode.interval.interval_list_intersections import Solution + + +soln = Solution() + + +def test_case_1(snapshot): + firstList = [[0, 2], [5, 10], [13, 23], [24, 25]] + secondList = [[1, 5], [8, 12], [15, 24], [25, 26]] + + snapshot.assert_match(soln.intervalIntersection(firstList, secondList)) diff --git a/test/leetcode/interval/meeting_rooms_test.py b/test/leetcode/interval/meeting_rooms_test.py new file mode 100644 index 0000000..333a4df --- /dev/null +++ b/test/leetcode/interval/meeting_rooms_test.py @@ -0,0 +1,8 @@ +from leetcode.interval.meeting_rooms import Solution + + +soln = Solution() + + +def test_case_1(): + assert not soln.canAttendMeetings([[0, 30], [5, 10], [15, 20]]) diff --git a/test/leetcode/interval/meeting_scheduler_test.py b/test/leetcode/interval/meeting_scheduler_test.py new file mode 100644 index 0000000..7fa8144 --- /dev/null +++ b/test/leetcode/interval/meeting_scheduler_test.py @@ -0,0 +1,12 @@ +from leetcode.interval.meeting_scheduler import Solution + + +soln = Solution() + + +def test_case_1(): + slot1 = [[10, 50], [60, 120], [140, 210]] + slot2 = [[0, 15], [60, 70]] + duration = 8 + + assert soln.minAvailableDuration(slot1, slot2, duration) == [60, 68] diff --git a/test/leetcode/interval/merge_intervals_test.py b/test/leetcode/interval/merge_intervals_test.py new file mode 100644 index 0000000..d71daca --- /dev/null +++ b/test/leetcode/interval/merge_intervals_test.py @@ -0,0 +1,9 @@ +from leetcode.interval.merge_intervals import Solution + + +soln = Solution() + + +def test_case_1(): + intervals = [[1, 3], [2, 6], [8, 10], [15, 18]] + assert soln.merge(intervals) == [[1, 6], [8, 10], [15, 18]] diff --git a/test/leetcode/interval/snapshots/snap_interval_list_intersection_test.py b/test/leetcode/interval/snapshots/snap_interval_list_intersection_test.py new file mode 100644 index 0000000..02e0420 --- /dev/null +++ b/test/leetcode/interval/snapshots/snap_interval_list_intersection_test.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_case_1 1'] = [ + [ + 1, + 2 + ], + [ + 5, + 5 + ], + [ + 8, + 10 + ], + [ + 15, + 23 + ], + [ + 24, + 24 + ], + [ + 25, + 25 + ] +] diff --git a/test/leetcode/linked_list/add_two_numbers_test.py b/test/leetcode/linked_list/add_two_numbers_test.py new file mode 100644 index 0000000..a5997ac --- /dev/null +++ b/test/leetcode/linked_list/add_two_numbers_test.py @@ -0,0 +1,32 @@ +from leetcode.linked_list.add_two_numbers import Solution +from leetcode.linked_list.common.list_node import array2list, list2array + + +soln = Solution() + + +def test_case_1(): + x = array2list([2, 4, 3]) + y = array2list([5, 6, 4]) + + assert x + assert y + assert list2array(soln.addTwoNumbers(x, y)) == [7, 0, 8] + + +def test_case_2(): + x = array2list([9, 9, 9, 9, 9, 9, 9]) + y = array2list([9, 9, 9, 9]) + + assert x + assert y + assert list2array(soln.addTwoNumbers(x, y)) == [8, 9, 9, 9, 0, 0, 0, 1] + + +def test_case_3(snapshot): + x = array2list([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) + y = array2list([5, 6, 4]) + + assert x + assert y + snapshot.assert_match(list2array(soln.addTwoNumbers(x, y))) diff --git a/test/leetcode/linked_list/all_one_test.py b/test/leetcode/linked_list/all_one_test.py new file mode 100644 index 0000000..0d90e4c --- /dev/null +++ b/test/leetcode/linked_list/all_one_test.py @@ -0,0 +1,34 @@ +from leetcode.linked_list.all_one import AllOne + + +def test_case_1(): + allone = AllOne() + allone.inc("hello") + allone.inc("hello") + + assert allone.getMaxKey() == "hello" + assert allone.getMinKey() == "hello" + + allone.inc("leet") + + assert allone.getMaxKey() == "hello" + assert allone.getMinKey() == "leet" + + +def test_case_2(): + allone = AllOne() + allone.inc("a") + allone.inc("b") + allone.inc("b") + allone.inc("c") + allone.inc("c") + allone.inc("c") + allone.dec("b") + allone.dec("b") + + assert allone.getMinKey() == "a" + + allone.dec("a") + + assert allone.getMaxKey() == "c" + assert allone.getMinKey() == "c" diff --git a/test/leetcode/linked_list/merge_two_sorted_lists_test.py b/test/leetcode/linked_list/merge_two_sorted_lists_test.py new file mode 100644 index 0000000..6d04610 --- /dev/null +++ b/test/leetcode/linked_list/merge_two_sorted_lists_test.py @@ -0,0 +1,39 @@ +from leetcode.linked_list.common.list_node import array2list, list2array +from leetcode.linked_list.merge_two_sorted_lists import Solution + + +soln = Solution() + + +def test_case_1(): + a = None + b = None + assert soln.mergeTwoLists(a, b) is None + + +def test_case_2(): + a = array2list([1]) + b = None + + assert list2array(soln.mergeTwoLists(a, b)) == [1] + + +def test_case_3(): + a = None + b = array2list([1]) + + assert list2array(soln.mergeTwoLists(a, b)) == [1] + + +def test_case_4(): + a = array2list([1]) + b = array2list([2]) + + assert list2array(soln.mergeTwoLists(a, b)) == [1, 2] + + +def test_case_5(): + a = array2list([1, 2, 4]) + b = array2list([2, 3, 4]) + + assert list2array(soln.mergeTwoLists(a, b)) == [1, 2, 2, 3, 4, 4] diff --git a/test/leetcode/linked_list/nested_list_weighted_sum_ii_test.py b/test/leetcode/linked_list/nested_list_weighted_sum_ii_test.py new file mode 100644 index 0000000..65ea204 --- /dev/null +++ b/test/leetcode/linked_list/nested_list_weighted_sum_ii_test.py @@ -0,0 +1,13 @@ +from leetcode.linked_list.common.nested_integer import NestedInteger +from leetcode.linked_list.nested_list_weighted_sum_ii import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.depthSumInverse([NestedInteger(10)]) == 10 + + +def test_case_2(): + assert soln.depthSumInverse([NestedInteger(10), NestedInteger(20)]) == 30 diff --git a/test/leetcode/linked_list/nested_list_weighted_sum_test.py b/test/leetcode/linked_list/nested_list_weighted_sum_test.py new file mode 100644 index 0000000..1f3ef57 --- /dev/null +++ b/test/leetcode/linked_list/nested_list_weighted_sum_test.py @@ -0,0 +1,13 @@ +from leetcode.linked_list.common.nested_integer import NestedInteger +from leetcode.linked_list.nested_list_weighted_sum import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.depthSum([NestedInteger(10)]) == 10 + + +def test_case_2(): + assert soln.depthSum([NestedInteger(10), NestedInteger(20)]) == 30 diff --git a/test/leetcode/linked_list/snapshots/snap_add_two_numbers_test.py b/test/leetcode/linked_list/snapshots/snap_add_two_numbers_test.py new file mode 100644 index 0000000..ddbeafb --- /dev/null +++ b/test/leetcode/linked_list/snapshots/snap_add_two_numbers_test.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_case_3 1'] = [ + 6, + 6, + 4, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1 +] diff --git a/test/leetcode/math/number_of_one_bits_test.py b/test/leetcode/math/number_of_one_bits_test.py new file mode 100644 index 0000000..48ac0d8 --- /dev/null +++ b/test/leetcode/math/number_of_one_bits_test.py @@ -0,0 +1,9 @@ +from leetcode.math.number_of_one_bits import Solution + + +soln = Solution() + + +def test_case_1(): + # Because 11 = 0b1001 + assert soln.hammingWeight(11) == 3 diff --git a/test/leetcode/math/pow_x_n_test.py b/test/leetcode/math/pow_x_n_test.py new file mode 100644 index 0000000..02cd0a7 --- /dev/null +++ b/test/leetcode/math/pow_x_n_test.py @@ -0,0 +1,32 @@ +from leetcode.math.pow_x_n import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.myPow(2, -2) == 0.25 + + +def test_case_2(): + assert soln.myPow(2, -1) == 0.5 + + +def test_case_3(): + assert soln.myPow(2, 0) == 1 + + +def test_case_4(): + assert soln.myPow(2, 1) == 2 + + +def test_case_5(): + assert soln.myPow(2, 4) == 16 + + +def test_case_6(): + assert soln.myPow(2, 5) == 32 + + +def test_case_7(): + assert soln.myPow(2, -2147483648) == 0 diff --git a/test/leetcode/math/reverse_integer_test.py b/test/leetcode/math/reverse_integer_test.py new file mode 100644 index 0000000..7b9f5bc --- /dev/null +++ b/test/leetcode/math/reverse_integer_test.py @@ -0,0 +1,8 @@ +from leetcode.math.reverse_integer import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.reverse(123) == 321 diff --git a/test/leetcode/math/rotate_image_test.py b/test/leetcode/math/rotate_image_test.py new file mode 100644 index 0000000..a885d39 --- /dev/null +++ b/test/leetcode/math/rotate_image_test.py @@ -0,0 +1,12 @@ +from leetcode.math.rotate_image import Solution + + +soln = Solution() + + +def test_case_1(): + matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] + + soln.rotate(matrix) + + assert matrix == [[7, 4, 1], [8, 5, 2], [9, 6, 3]] diff --git a/test/leetcode/math/sequential_digits_test.py b/test/leetcode/math/sequential_digits_test.py new file mode 100644 index 0000000..75715a4 --- /dev/null +++ b/test/leetcode/math/sequential_digits_test.py @@ -0,0 +1,12 @@ +from leetcode.math.sequential_digits import Solution + + +soln = Solution() + + +def test_case_1(snapshot): + snapshot.assert_match(soln.sequentialDigits(100, 300)) + + +def test_case_2(snapshot): + snapshot.assert_match(soln.sequentialDigits(1000, 13000)) diff --git a/test/leetcode/math/snapshots/snap_sequential_digits_test.py b/test/leetcode/math/snapshots/snap_sequential_digits_test.py new file mode 100644 index 0000000..f2cf46e --- /dev/null +++ b/test/leetcode/math/snapshots/snap_sequential_digits_test.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_case_1 1'] = [ + 123, + 234 +] + +snapshots['test_case_2 1'] = [ + 1234, + 2345, + 3456, + 4567, + 5678, + 6789, + 12345 +] diff --git a/test/leetcode/matrix/robot_bounded_in_circle_test.py b/test/leetcode/matrix/robot_bounded_in_circle_test.py new file mode 100644 index 0000000..9849313 --- /dev/null +++ b/test/leetcode/matrix/robot_bounded_in_circle_test.py @@ -0,0 +1,8 @@ +from leetcode.matrix.robot_bounded_in_circle import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.isRobotBounded("GGLLGG") diff --git a/test/leetcode/matrix/valid_word_square_test.py b/test/leetcode/matrix/valid_word_square_test.py new file mode 100644 index 0000000..db63b89 --- /dev/null +++ b/test/leetcode/matrix/valid_word_square_test.py @@ -0,0 +1,12 @@ +from leetcode.matrix.valid_word_square import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.validWordSquare(["abcd", "bnrt", "crmy", "dtye"]) + + +def test_case_2(): + assert soln.validWordSquare(["abcd", "bnrt", "crm", "dt"]) diff --git a/test/leetcode/prefix_sum/continuous_subarray_sum_test.py b/test/leetcode/prefix_sum/continuous_subarray_sum_test.py new file mode 100644 index 0000000..cca6874 --- /dev/null +++ b/test/leetcode/prefix_sum/continuous_subarray_sum_test.py @@ -0,0 +1,16 @@ +from leetcode.prefix_sum.continuous_subarray_sum import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.checkSubarraySum([23, 2, 4, 6, 7], 6) + + +def test_case_2(): + assert soln.checkSubarraySum([1, 1], 2) + + +def test_case_3(): + assert soln.checkSubarraySum([-10, 10], 1) diff --git a/test/leetcode/prefix_sum/max_sum_obtained_of_any_permutation_test.py b/test/leetcode/prefix_sum/max_sum_obtained_of_any_permutation_test.py new file mode 100644 index 0000000..987dc7f --- /dev/null +++ b/test/leetcode/prefix_sum/max_sum_obtained_of_any_permutation_test.py @@ -0,0 +1,8 @@ +from leetcode.prefix_sum.max_sum_obtained_of_any_permutation import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maxSumRangeQuery([1, 2, 3, 4, 5], [[1, 3], [0, 1]]) == 19 diff --git a/test/leetcode/prefix_sum/product_of_array_except_self_test.py b/test/leetcode/prefix_sum/product_of_array_except_self_test.py new file mode 100644 index 0000000..df3425a --- /dev/null +++ b/test/leetcode/prefix_sum/product_of_array_except_self_test.py @@ -0,0 +1,8 @@ +from leetcode.prefix_sum.product_of_array_except_self import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.productExceptSelf([1, 2, 3, 4]) == [24, 12, 8, 6] diff --git a/test/leetcode/prefix_sum/range_sum_query_immutable_test.py b/test/leetcode/prefix_sum/range_sum_query_immutable_test.py new file mode 100644 index 0000000..14e3ea9 --- /dev/null +++ b/test/leetcode/prefix_sum/range_sum_query_immutable_test.py @@ -0,0 +1,9 @@ +from leetcode.prefix_sum.range_sum_query_immutable import NumArray + + +def test_case_1(): + arr = NumArray([-2, 0, 3, -5, 2, -1]) + assert arr.sumRange(0, 2) == 1 + assert arr.sumRange(2, 5) == -1 + assert arr.sumRange(0, 5) == -3 + assert arr.sumRange(0, 0) == -2 diff --git a/test/leetcode/prefix_sum/running_sum_of_1d_array_test.py b/test/leetcode/prefix_sum/running_sum_of_1d_array_test.py new file mode 100644 index 0000000..d240628 --- /dev/null +++ b/test/leetcode/prefix_sum/running_sum_of_1d_array_test.py @@ -0,0 +1,8 @@ +from leetcode.prefix_sum.running_sum_of_1d_array import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.runningSum([1, 2, 3, 4]) == [1, 3, 6, 10] diff --git a/test/leetcode/prefix_sum/subarray_sum_equals_k_test.py b/test/leetcode/prefix_sum/subarray_sum_equals_k_test.py new file mode 100644 index 0000000..cb869c2 --- /dev/null +++ b/test/leetcode/prefix_sum/subarray_sum_equals_k_test.py @@ -0,0 +1,8 @@ +from leetcode.prefix_sum.subarray_sum_equals_k import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.subarraySum([1, 1, 1], 2) == 2 diff --git a/test/leetcode/recursion/generate_parentheses_test.py b/test/leetcode/recursion/generate_parentheses_test.py new file mode 100644 index 0000000..f646b85 --- /dev/null +++ b/test/leetcode/recursion/generate_parentheses_test.py @@ -0,0 +1,16 @@ +from leetcode.recursion.generate_parentheses import Solution + + +soln = Solution() + + +def test_case_1(): + assert set(soln.generateParenthesis(2)) == set(["()()", "(())"]) + + +def test_case_2(snapshot): + snapshot.assert_match(soln.generateParenthesis(3)) + + +def test_case_3(snapshot): + snapshot.assert_match(soln.generateParenthesis(4)) diff --git a/test/leetcode/recursion/letter_combinations_phone_number_test.py b/test/leetcode/recursion/letter_combinations_phone_number_test.py new file mode 100644 index 0000000..50bbf2b --- /dev/null +++ b/test/leetcode/recursion/letter_combinations_phone_number_test.py @@ -0,0 +1,16 @@ +from leetcode.recursion.letter_combinations_phone_number import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.letterCombinations("") == [] + + +def test_case_2(): + assert sorted(soln.letterCombinations("2")) == ["a", "b", "c"] + + +def test_case_3(): + assert sorted(soln.letterCombinations("23")) == ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"] diff --git a/test/leetcode/recursion/maximum_number_of_operations_with_the_same_score_test.py b/test/leetcode/recursion/maximum_number_of_operations_with_the_same_score_test.py new file mode 100644 index 0000000..43bbf7f --- /dev/null +++ b/test/leetcode/recursion/maximum_number_of_operations_with_the_same_score_test.py @@ -0,0 +1,16 @@ +from leetcode.recursion.maximum_number_of_operations_with_the_same_score import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maxOperations([3, 2, 1, 2, 3, 4]) == 3 + + +def test_case_2(): + assert soln.maxOperations([3, 2, 6, 1, 4]) == 2 + + +def test_case_3(): + assert soln.maxOperations([3, 2, 1, 4, 1]) == 2 diff --git a/test/leetcode/recursion/optimal_account_balancing_test.py b/test/leetcode/recursion/optimal_account_balancing_test.py new file mode 100644 index 0000000..1c152e8 --- /dev/null +++ b/test/leetcode/recursion/optimal_account_balancing_test.py @@ -0,0 +1,16 @@ +from leetcode.recursion.optimal_account_balancing import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.minTransfers([[0, 1, 10], [2, 0, 5]]) == 2 + + +def test_case_2(): + assert soln.minTransfers([[0, 1, 10], [1, 0, 1], [1, 2, 5], [2, 0, 5]]) == 1 + + +def test_case_3(): + assert soln.minTransfers([[0, 1, 1], [1, 2, 1], [2, 3, 4], [3, 4, 5]]) == 3 diff --git a/test/leetcode/recursion/permutations_test.py b/test/leetcode/recursion/permutations_test.py new file mode 100644 index 0000000..d870597 --- /dev/null +++ b/test/leetcode/recursion/permutations_test.py @@ -0,0 +1,20 @@ +from leetcode.recursion.permutations import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.permute([1, 2, 3]) == [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] + + +def test_case_2(): + assert soln.permute([0, 1]) == [[0, 1], [1, 0]] + + +def test_case_3(): + assert soln.permute([1]) == [[1]] + + +def test_case_4(): + assert soln.permute([]) == [] diff --git a/test/leetcode/recursion/snapshots/snap_generate_parentheses_test.py b/test/leetcode/recursion/snapshots/snap_generate_parentheses_test.py new file mode 100644 index 0000000..ce7c056 --- /dev/null +++ b/test/leetcode/recursion/snapshots/snap_generate_parentheses_test.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots["test_case_2 1"] = ["((()))", "(()())", "(())()", "()(())", "()()()"] + +snapshots["test_case_3 1"] = [ + "(((())))", + "((()()))", + "((())())", + "((()))()", + "(()(()))", + "(()()())", + "(()())()", + "(())(())", + "(())()()", + "()((()))", + "()(()())", + "()(())()", + "()()(())", + "()()()()", +] diff --git a/test/leetcode/recursion/snapshots/snap_subsets_test.py b/test/leetcode/recursion/snapshots/snap_subsets_test.py new file mode 100644 index 0000000..20e4d9b --- /dev/null +++ b/test/leetcode/recursion/snapshots/snap_subsets_test.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['test_case_1 1'] = set([ + frozenset([ + 2 + ]), + frozenset([ + 2, + 3 + ]), + frozenset([ + 1, + 2 + ]), + frozenset([ + 3 + ]), + frozenset([ + ]), + frozenset([ + 1 + ]), + frozenset([ + 1, + 2, + 3 + ]), + frozenset([ + 1, + 3 + ]) +]) diff --git a/test/leetcode/recursion/subsets_test.py b/test/leetcode/recursion/subsets_test.py new file mode 100644 index 0000000..90c8c24 --- /dev/null +++ b/test/leetcode/recursion/subsets_test.py @@ -0,0 +1,12 @@ +from leetcode.recursion.subsets import Solution + + +soln = Solution() + + +def test_case_1(snapshot): + subsets = soln.subsets([1, 2, 3]) + + subsets = {frozenset(subset) for subset in subsets} + + snapshot.assert_match(subsets) diff --git a/test/leetcode/recursion/word_break_test.py b/test/leetcode/recursion/word_break_test.py new file mode 100644 index 0000000..8feeb75 --- /dev/null +++ b/test/leetcode/recursion/word_break_test.py @@ -0,0 +1,20 @@ +from leetcode.recursion.word_break_ii import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.wordBreak("catsanddog", ["cat", "cats", "and", "sand", "dog"]) == ["cat sand dog", "cats and dog"] + + +def test_case_2(): + assert soln.wordBreak("pineapplepenapple", ["apple", "pen", "applepen", "pine", "pineapple"]) == [ + "pine apple pen apple", + "pine applepen apple", + "pineapple pen apple", + ] + + +def test_case_3(): + assert soln.wordBreak("catsandog", ["cats", "dog", "sand", "and", "cat"]) == [] diff --git a/test/leetcode/sliding_window/best_time_to_buy_and_sell_stock_test.py b/test/leetcode/sliding_window/best_time_to_buy_and_sell_stock_test.py new file mode 100644 index 0000000..9bcd357 --- /dev/null +++ b/test/leetcode/sliding_window/best_time_to_buy_and_sell_stock_test.py @@ -0,0 +1,12 @@ +from leetcode.sliding_window.best_time_to_buy_and_sell_stock import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maxProfit([7, 1, 5, 3, 6, 4]) == 5 + + +def test_case_2(): + assert soln.maxProfit([7, 6, 4, 3, 1]) == 0 diff --git a/test/leetcode/sliding_window/count_subarrays_where_max_elements_appears_test.py b/test/leetcode/sliding_window/count_subarrays_where_max_elements_appears_test.py new file mode 100644 index 0000000..ff65394 --- /dev/null +++ b/test/leetcode/sliding_window/count_subarrays_where_max_elements_appears_test.py @@ -0,0 +1,29 @@ +import json +import pathlib +from leetcode.sliding_window.count_subarrays_where_max_element_appears import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.countSubarrays([1, 3, 2, 3, 3], 3) == 2 + + +def test_case_2(): + assert soln.countSubarrays([1, 4, 2, 1], 3) == 0 + + +def test_case_3(): + xs = [61, 23, 38, 23, 56, 40, 82, 56, 82, 82, 82, 70, 8, 69, 8, 7, 19, 14, 58, 42, 82, 10, 82, 78, 15, 82] + k = 2 + + assert soln.countSubarrays(xs, k) == 224 + + +def test_case_4(): + path = pathlib.Path(__file__).parent / "data" / "count_subarrays_where_max_element_appears_test.json" + assert path.exists() + with path.open("r", encoding="utf-8") as file: + xs = json.load(file) + assert soln.countSubarrays(xs, 13) == 263559 diff --git a/test/sliding-window/__data__/count-subarrays-where-max-element-appears.test.json b/test/leetcode/sliding_window/data/count_subarrays_where_max_element_appears_test.json similarity index 100% rename from test/sliding-window/__data__/count-subarrays-where-max-element-appears.test.json rename to test/leetcode/sliding_window/data/count_subarrays_where_max_element_appears_test.json diff --git a/test/leetcode/sliding_window/length_of_longest_substring_test.py b/test/leetcode/sliding_window/length_of_longest_substring_test.py new file mode 100644 index 0000000..9070604 --- /dev/null +++ b/test/leetcode/sliding_window/length_of_longest_substring_test.py @@ -0,0 +1,16 @@ +from leetcode.sliding_window.length_of_longest_substring import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.lengthOfLongestSubstring("abcabcbb") == 3 + + +def test_case_2(): + assert soln.lengthOfLongestSubstring("bbbbb") == 1 + + +def test_case_3(): + assert soln.lengthOfLongestSubstring("pwwkew") == 3 diff --git a/test/leetcode/sliding_window/longest_continuous_subarray_test.py b/test/leetcode/sliding_window/longest_continuous_subarray_test.py new file mode 100644 index 0000000..c2754e0 --- /dev/null +++ b/test/leetcode/sliding_window/longest_continuous_subarray_test.py @@ -0,0 +1,20 @@ +from leetcode.sliding_window.longest_continuous_subarray import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.longestSubarray([8, 2, 4, 7], 4) == 2 + + +def test_case_2(): + assert soln.longestSubarray([10, 1, 2, 4, 7, 2], 5) == 4 + + +def test_case_3(): + assert soln.longestSubarray([4, 2, 2, 2, 4, 4, 2, 2], 0) == 3 + + +def test_case_4(): + assert soln.longestSubarray([1, 5, 6, 7, 8, 10, 6, 5, 6], 4) == 5 diff --git a/test/leetcode/sliding_window/max_sum_distinct_subarray_of_size_k_test.py b/test/leetcode/sliding_window/max_sum_distinct_subarray_of_size_k_test.py new file mode 100644 index 0000000..085d211 --- /dev/null +++ b/test/leetcode/sliding_window/max_sum_distinct_subarray_of_size_k_test.py @@ -0,0 +1,12 @@ +from leetcode.sliding_window.max_sum_distinct_subarray_of_size_k import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maximumSubarraySum([1, 5, 4, 2, 9, 9, 9], 3) == 15 + + +def test_case_2(): + assert soln.maximumSubarraySum([4, 4, 4], 3) == 0 diff --git a/test/leetcode/sliding_window/minimum_window_substring_test.py b/test/leetcode/sliding_window/minimum_window_substring_test.py new file mode 100644 index 0000000..6a24fb8 --- /dev/null +++ b/test/leetcode/sliding_window/minimum_window_substring_test.py @@ -0,0 +1,12 @@ +from leetcode.sliding_window.minimum_window_substring import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.minWindow("ADOBECODEBANC", "ABC") == "BANC" + + +def test_case_2(): + assert soln.minWindow("a", "a") == "a" diff --git a/test/leetcode/sliding_window/repeated_dna_sequence_test.py b/test/leetcode/sliding_window/repeated_dna_sequence_test.py new file mode 100644 index 0000000..e156600 --- /dev/null +++ b/test/leetcode/sliding_window/repeated_dna_sequence_test.py @@ -0,0 +1,12 @@ +from leetcode.sliding_window.repeated_dna_sequences import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.findRepeatedDnaSequences("AAAAAAAAAAAAA") == ["AAAAAAAAAA"] + + +def test_case_2(): + assert soln.findRepeatedDnaSequences("AAAAAAAAAAA") == ["AAAAAAAAAA"] diff --git a/test/leetcode/stack/basic_calculator.py b/test/leetcode/stack/basic_calculator.py new file mode 100644 index 0000000..cbc8e6c --- /dev/null +++ b/test/leetcode/stack/basic_calculator.py @@ -0,0 +1,16 @@ +from leetcode.stack.basic_calculator import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.calculate("1 + 1") == 2 + + +def test_case_2(): + assert soln.calculate(" 2-1 + 2 ") == 3 + + +def test_case_3(): + assert soln.calculate("(1+(4+5+2)-3)+(6+8)") == 23 diff --git a/test/leetcode/stack/basic_calculator_ii_test.py b/test/leetcode/stack/basic_calculator_ii_test.py new file mode 100644 index 0000000..06b7256 --- /dev/null +++ b/test/leetcode/stack/basic_calculator_ii_test.py @@ -0,0 +1,16 @@ +from leetcode.stack.basic_calculator_ii import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.calculate("3+2*2") == 7 + + +def test_case_2(): + assert soln.calculate(" 3/2 ") == 1 + + +def test_case_3(): + assert soln.calculate(" 3+5 / 2 ") == 5 diff --git a/test/leetcode/stack/key_value_store_nested_transactions_test.py b/test/leetcode/stack/key_value_store_nested_transactions_test.py new file mode 100644 index 0000000..f62d0bd --- /dev/null +++ b/test/leetcode/stack/key_value_store_nested_transactions_test.py @@ -0,0 +1,140 @@ +from leetcode.stack.key_value_store_nested_transactions import KeyValueStore + + +def test_case_1(): + store = KeyValueStore() + + # data: {a: 1, b: 2, c: 3} + store.set("a", 1) + store.set("b", 2) + store.set("c", 3) + + assert store.get("a") == 1 + assert store.get("b") == 2 + assert store.get("c") == 3 + + # data: {a: 1, b: 2} + store.delete("c") + + assert not store.get("c") + + +def test_case_2(): + store = KeyValueStore() + + # data: {a: 1, b: 2, c: 3} + store.set("a", 1) + store.set("b", 2) + store.set("c", 3) + + # data: {a: 1, b: 2, c: 3} + # tx1: {a: 10, b: 20} + store.begin() + store.set("a", 10) + store.set("b", 20) + store.delete("c") + + assert store.get("a") == (10) + assert store.get("b") == (20) + assert not store.get("c") + + # data: {a: 10, b: 20} + store.commit() + + assert store.get("a") == 10 + assert store.get("b") == 20 + + +def test_case_3(): + store = KeyValueStore() + + # data: {a: 1, b: 2, c: 3} + store.set("a", 1) + store.set("b", 2) + store.set("c", 3) + + # data: {a: 1, b: 2, c: 3} + # tx1: {a: 10, b: 20} + store.begin() + store.set("a", 10) + store.set("b", 20) + store.delete("c") + + assert store.get("a") == 10 + assert store.get("b") == 20 + assert not store.get("c") + + # data: {a: 1, b: 2, c: 3} + store.rollback() + + assert store.get("a") == 1 + assert store.get("b") == 2 + assert store.get("c") == 3 + + +def test_case_4(): + store = KeyValueStore() + + # data: {a: 1} + store.set("a", 1) + + # data: {a: 1} + # tx1: {a: 10} + store.begin() + store.set("a", 10) + + # data: {a: 1} + # tx1: {a: 10} + # tx2: {a: 100} + store.begin() + store.set("a", 100) + + assert store.get("a") == 100 + + # data: {a: 1} + # tx1: {a: 100} + store.commit() + + assert store.get("a") == 100 + + # data: {a: 100} + store.commit() + + assert store.get("a") == 100 + + +def test_case_5(): + store = KeyValueStore() + + # data: {a: 1, b: 2} + store.set("a", 1) + store.set("b", 2) + + # data: {a: 1, b: 2} + # tx1: {b: 20} + store.begin() + store.delete("a") + store.set("b", 20) + + # data: {a: 1, b: 2} + # tx1: {b: 20} + # tx2: {a: 100} + store.begin() + store.set("a", 100) + store.delete("b") + + assert store.get("a") == 100 + assert not store.get("b") + + # data: {a: 1, b: 2} + # tx1: {b: 20} + store.rollback() + + assert not store.get("a") + assert store.get("b") == 20 + + # data: {a: 1, b: 2} + store.rollback() + + assert store.get("a") == 1 + assert store.get("b") == 2 diff --git a/test/leetcode/stack/longest_absolute_file_path_test.py b/test/leetcode/stack/longest_absolute_file_path_test.py new file mode 100644 index 0000000..764a2ec --- /dev/null +++ b/test/leetcode/stack/longest_absolute_file_path_test.py @@ -0,0 +1,12 @@ +from leetcode.stack.longest_absolute_file_path import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.lengthLongestPath("dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext") == 20 + + +def test_case_2(): + assert soln.lengthLongestPath("dir\n file.txt") == 12 diff --git a/test/leetcode/stack/max_chunks_to_make_sorted_ii_test.py b/test/leetcode/stack/max_chunks_to_make_sorted_ii_test.py new file mode 100644 index 0000000..b78a104 --- /dev/null +++ b/test/leetcode/stack/max_chunks_to_make_sorted_ii_test.py @@ -0,0 +1,12 @@ +from leetcode.stack.max_chunks_to_make_sorted_ii import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maxChunksToSorted([5, 4, 3, 2, 1]) == 1 + + +def test_case_2(): + assert soln.maxChunksToSorted([2, 1, 3, 4, 4]) == 4 diff --git a/test/leetcode/stack/min_stack_test.py b/test/leetcode/stack/min_stack_test.py new file mode 100644 index 0000000..508a86d --- /dev/null +++ b/test/leetcode/stack/min_stack_test.py @@ -0,0 +1,14 @@ +from leetcode.stack.min_stack import MinStack + + +def test_case_1(): + stack = MinStack() + stack.push(0) + stack.push(-3) + + assert stack.getMin() == -3 + + stack.pop() + + assert stack.top() == 0 + assert stack.getMin() == 0 diff --git a/test/leetcode/stack/minimum_add_to_make_valid_parentheses_test.py b/test/leetcode/stack/minimum_add_to_make_valid_parentheses_test.py new file mode 100644 index 0000000..872462c --- /dev/null +++ b/test/leetcode/stack/minimum_add_to_make_valid_parentheses_test.py @@ -0,0 +1,12 @@ +from leetcode.stack.minimum_add_to_make_valid_parentheses import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.minAddToMakeValid("())") == 1 + + +def test_case_2(): + assert soln.minAddToMakeValid("(((") == 3 diff --git a/test/leetcode/stack/minimum_remove_to_make_valid_parentheses_test.py b/test/leetcode/stack/minimum_remove_to_make_valid_parentheses_test.py new file mode 100644 index 0000000..3e130ad --- /dev/null +++ b/test/leetcode/stack/minimum_remove_to_make_valid_parentheses_test.py @@ -0,0 +1,12 @@ +from leetcode.stack.minimum_remove_to_make_valid_parentheses import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.minRemoveToMakeValid("lee(t(c)o)de)") == "lee(t(c)o)de" + + +def test_case_2(): + assert soln.minRemoveToMakeValid("a)b(c)d") == "ab(c)d" diff --git a/test/leetcode/stack/simplify_path_test.py b/test/leetcode/stack/simplify_path_test.py new file mode 100644 index 0000000..7348b1d --- /dev/null +++ b/test/leetcode/stack/simplify_path_test.py @@ -0,0 +1,12 @@ +from leetcode.stack.simplify_path import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.simplifyPath("/home/") == "/home" + + +def test_case_2(): + assert soln.simplifyPath("/../") == "/" diff --git a/test/leetcode/stack/valid_number_test.py b/test/leetcode/stack/valid_number_test.py new file mode 100644 index 0000000..9113abc --- /dev/null +++ b/test/leetcode/stack/valid_number_test.py @@ -0,0 +1,132 @@ +from leetcode.stack.valid_number import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.isNumber("2") + + +def test_case_2(): + assert soln.isNumber("0089") + + +def test_case_3(): + assert soln.isNumber("-0.1") + + +def test_case_4(): + assert soln.isNumber("+3.14") + + +def test_case_5(): + assert soln.isNumber("4.") + + +def test_case_6(): + assert soln.isNumber("-.9") + + +def test_case_7(): + assert soln.isNumber("2e10") + + +def test_case_8(): + assert soln.isNumber("-90E3") + + +def test_case_9(): + assert soln.isNumber("3e+7") + + +def test_case_10(): + assert soln.isNumber("+6e-1") + + +def test_case_11(): + assert soln.isNumber("53.5e93") + + +def test_case_12(): + assert soln.isNumber("-123.456e789") + + +def test_case_13(): + assert soln.isNumber("46.e3") + + +def test_case_14(): + assert soln.isNumber("1.e10") + + +def test_case_15(): + assert soln.isNumber("9.e3") + + +def test_case_16(): + assert not soln.isNumber("abc") + + +def test_case_17(): + assert not soln.isNumber("1a") + + +def test_case_18(): + assert not soln.isNumber("1e") + + +def test_case_19(): + assert not soln.isNumber("e3") + + +def test_case_20(): + assert not soln.isNumber("99e2.5") + + +def test_case_21(): + assert not soln.isNumber("--6") + + +def test_case_22(): + assert not soln.isNumber("-+3") + + +def test_case_23(): + assert not soln.isNumber("95a54e53") + + +def test_case_24(): + assert not soln.isNumber("1ee3") + + +def test_case_25(): + assert not soln.isNumber(".") + + +def test_case_26(): + assert not soln.isNumber("1e+") + + +def test_case_27(): + assert not soln.isNumber("1e+.") + + +def test_case_28(): + assert not soln.isNumber("+.") + + +def test_case_29(): + assert not soln.isNumber(".+") + + +def test_case_30(): + assert not soln.isNumber("+e3") + + +def test_case_31(): + assert not soln.isNumber("46e.3") + + +def test_case_32(): + assert not soln.isNumber(".e3") diff --git a/test/leetcode/stack/valid_parentheses_test.py b/test/leetcode/stack/valid_parentheses_test.py new file mode 100644 index 0000000..50b9ecd --- /dev/null +++ b/test/leetcode/stack/valid_parentheses_test.py @@ -0,0 +1,16 @@ +from leetcode.stack.valid_parentheses import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.isValid("()") is True + + +def test_case_2(): + assert soln.isValid("()[]{}") is True + + +def test_case_3(): + assert not soln.isValid("(}") diff --git a/test/leetcode/string/find_the_closest_palindrome_test.py b/test/leetcode/string/find_the_closest_palindrome_test.py new file mode 100644 index 0000000..c325cac --- /dev/null +++ b/test/leetcode/string/find_the_closest_palindrome_test.py @@ -0,0 +1,40 @@ +from leetcode.string.find_the_closest_palindrome import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.nearestPalindromic("1234") == "1221" + + +def test_case_2(): + assert soln.nearestPalindromic("123") == "121" + + +def test_case_3(): + assert soln.nearestPalindromic("1") == "0" + + +def test_case_4(): + assert soln.nearestPalindromic("10") == "9" + + +def test_case_5(): + assert soln.nearestPalindromic("11911") == "11811" + + +def test_case_6(): + assert soln.nearestPalindromic("100") == "99" + + +def test_case_7(): + assert soln.nearestPalindromic("11011") == "11111" + + +def test_case_8(): + assert soln.nearestPalindromic("111111111") == "111101111" + + +def test_case_9(): + assert soln.nearestPalindromic("19991") == "20002" diff --git a/test/leetcode/string/int_to_roman_test.py b/test/leetcode/string/int_to_roman_test.py new file mode 100644 index 0000000..190e6c6 --- /dev/null +++ b/test/leetcode/string/int_to_roman_test.py @@ -0,0 +1,16 @@ +from leetcode.string.int_to_roman import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.intToRoman(3) == "III" + + +def test_case_2(): + assert soln.intToRoman(4) == "IV" + + +def test_case_3(): + assert soln.intToRoman(9) == "IX" diff --git a/test/leetcode/string/integer_to_english_words_test.py b/test/leetcode/string/integer_to_english_words_test.py new file mode 100644 index 0000000..0477a84 --- /dev/null +++ b/test/leetcode/string/integer_to_english_words_test.py @@ -0,0 +1,12 @@ +from leetcode.string.integer_to_english_words import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.numberToWords(123) == "One Hundred Twenty Three" + + +def test_case_2(): + assert soln.numberToWords(12345) == "Twelve Thousand Three Hundred Forty Five" diff --git a/test/leetcode/string/longest_palindromic_substring_test.py b/test/leetcode/string/longest_palindromic_substring_test.py new file mode 100644 index 0000000..0c0c17e --- /dev/null +++ b/test/leetcode/string/longest_palindromic_substring_test.py @@ -0,0 +1,12 @@ +from leetcode.string.longest_palindromic_substring import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.longestPalindrome("babad") == "bab" + + +def test_case_2(): + assert soln.longestPalindrome("cbbd") == "bb" diff --git a/test/leetcode/string/palindrome_number_test.py b/test/leetcode/string/palindrome_number_test.py new file mode 100644 index 0000000..9424696 --- /dev/null +++ b/test/leetcode/string/palindrome_number_test.py @@ -0,0 +1,8 @@ +from leetcode.string.palindrome_number import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.isPalindrome(121) diff --git a/test/leetcode/string/remove_all_adjacent_duplicates_ii_test.py b/test/leetcode/string/remove_all_adjacent_duplicates_ii_test.py new file mode 100644 index 0000000..0257c39 --- /dev/null +++ b/test/leetcode/string/remove_all_adjacent_duplicates_ii_test.py @@ -0,0 +1,12 @@ +from leetcode.string.remove_all_adjacent_duplicates_ii import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.removeDuplicates("abcd", 2) == "abcd" + + +def test_case_2(): + assert soln.removeDuplicates("deeedbbcccbdaa", 3) == "aa" diff --git a/test/leetcode/string/remove_all_adjacent_duplicates_test.py b/test/leetcode/string/remove_all_adjacent_duplicates_test.py new file mode 100644 index 0000000..27a0091 --- /dev/null +++ b/test/leetcode/string/remove_all_adjacent_duplicates_test.py @@ -0,0 +1,8 @@ +from leetcode.string.remove_all_adjacent_duplicates import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.removeDuplicates("abbaca") == "ca" diff --git a/test/leetcode/string/reverse_words_in_a_string_test.py b/test/leetcode/string/reverse_words_in_a_string_test.py new file mode 100644 index 0000000..5bca394 --- /dev/null +++ b/test/leetcode/string/reverse_words_in_a_string_test.py @@ -0,0 +1,8 @@ +from leetcode.string.reverse_words_in_a_string import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.reverseWords("the sky is blue") == "blue is sky the" diff --git a/test/leetcode/string/roman_to_int_test.py b/test/leetcode/string/roman_to_int_test.py new file mode 100644 index 0000000..3786583 --- /dev/null +++ b/test/leetcode/string/roman_to_int_test.py @@ -0,0 +1,8 @@ +from leetcode.string.roman_to_int import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.romanToInt("III") == 3 diff --git a/test/leetcode/string/time_needed_to_rearrange_binary_string_test.py b/test/leetcode/string/time_needed_to_rearrange_binary_string_test.py new file mode 100644 index 0000000..cf4b653 --- /dev/null +++ b/test/leetcode/string/time_needed_to_rearrange_binary_string_test.py @@ -0,0 +1,8 @@ +from leetcode.string.time_needed_to_rearrange_binary_string import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.secondsToRemoveOccurrences("0110101") == 4 diff --git a/test/leetcode/string/valid_word_abbreviation_test.py b/test/leetcode/string/valid_word_abbreviation_test.py new file mode 100644 index 0000000..9705506 --- /dev/null +++ b/test/leetcode/string/valid_word_abbreviation_test.py @@ -0,0 +1,12 @@ +from leetcode.string.valid_word_abbreviation import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.validWordAbbreviation("internationalization", "i12iz4n") is True + + +def test_case_2(): + assert soln.validWordAbbreviation("apple", "a2e") is False diff --git a/test/leetcode/string/vowel_spellchecker_test.py b/test/leetcode/string/vowel_spellchecker_test.py new file mode 100644 index 0000000..94ae396 --- /dev/null +++ b/test/leetcode/string/vowel_spellchecker_test.py @@ -0,0 +1,22 @@ +from leetcode.string.vowel_spellchecker import Solution + + +soln = Solution() + + +def test_case_1(): + wordlist = ["KiTe", "kite", "hare", "Hare"] + queries = ["kite", "Kite", "KiTe", "Hare", "HARE", "Hear", "hear", "keti", "keet", "keto"] + + assert soln.spellchecker(wordlist, queries) == [ + "kite", + "KiTe", + "KiTe", + "Hare", + "hare", + "", + "", + "KiTe", + "", + "KiTe", + ] diff --git a/test/leetcode/tree/binary_tree_level_order_traversal_test.py b/test/leetcode/tree/binary_tree_level_order_traversal_test.py new file mode 100644 index 0000000..b66a126 --- /dev/null +++ b/test/leetcode/tree/binary_tree_level_order_traversal_test.py @@ -0,0 +1,23 @@ +from leetcode.tree.binary_tree_level_order_traversal import Solution +from leetcode.tree.common.tree_node import array2tree + + +soln = Solution() + + +def test_case_1(): + tree = array2tree([3, 9, 20, None, None, 15, 7]) + + assert soln.levelOrder(tree) == [[3], [9, 20], [15, 7]] + + +def test_case_2(): + tree = array2tree([1]) + + assert soln.levelOrder(tree) == [[1]] + + +def test_case_3(): + tree = array2tree([]) + + assert soln.levelOrder(tree) == [] diff --git a/test/leetcode/tree/binary_tree_right_side_view_test.py b/test/leetcode/tree/binary_tree_right_side_view_test.py new file mode 100644 index 0000000..0f66153 --- /dev/null +++ b/test/leetcode/tree/binary_tree_right_side_view_test.py @@ -0,0 +1,11 @@ +from leetcode.tree.binary_tree_right_side_view import Solution +from leetcode.tree.common.tree_node import array2tree + + +soln = Solution() + + +def test_case_1(): + root = array2tree([1, 2, 3, None, 5, None, 4]) + + assert soln.rightSideView(root) == [1, 3, 4] diff --git a/test/leetcode/tree/binary_tree_vertical_order_traversal_test.py b/test/leetcode/tree/binary_tree_vertical_order_traversal_test.py new file mode 100644 index 0000000..942fd5d --- /dev/null +++ b/test/leetcode/tree/binary_tree_vertical_order_traversal_test.py @@ -0,0 +1,11 @@ +from leetcode.tree.binary_tree_vertical_order_traversal import Solution +from leetcode.tree.common.tree_node import array2tree + + +soln = Solution() + + +def test_case_1(): + tree = array2tree([3, 9, 20, None, None, 15, 7]) + + assert soln.verticalOrder(tree) == [[9], [3, 15], [20], [7]] diff --git a/test/leetcode/tree/design_in_memory_file_system_test.py b/test/leetcode/tree/design_in_memory_file_system_test.py new file mode 100644 index 0000000..de9f01e --- /dev/null +++ b/test/leetcode/tree/design_in_memory_file_system_test.py @@ -0,0 +1,13 @@ +from leetcode.tree.design_in_memory_file_system import FileSystem + + +def test_case_1(): + fs = FileSystem() + + assert fs.ls("/") == [] + + fs.mkdir("/a/b/c") + fs.addContentToFile("/a/b/c/d", "hello") + + assert fs.ls("/") == ["a"] + assert fs.readContentFromFile("/a/b/c/d") == "hello" diff --git a/test/leetcode/tree/diameter_of_binary_tree_test.py b/test/leetcode/tree/diameter_of_binary_tree_test.py new file mode 100644 index 0000000..582a008 --- /dev/null +++ b/test/leetcode/tree/diameter_of_binary_tree_test.py @@ -0,0 +1,17 @@ +from leetcode.tree.common.tree_node import array2tree +from leetcode.tree.diameter_of_binary_tree import Solution + + +soln = Solution() + + +def test_case_1(): + tree = array2tree([1, 2, 3]) + + assert soln.diameterOfBinaryTree(tree) == 2 + + +def test_case_2(): + tree = array2tree([1, 2, 3, 4, 5]) + + assert soln.diameterOfBinaryTree(tree) == 3 diff --git a/test/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_iii_test.py b/test/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_iii_test.py new file mode 100644 index 0000000..4d081f9 --- /dev/null +++ b/test/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_iii_test.py @@ -0,0 +1,17 @@ +from leetcode.tree.common.parent_node import array2tree +from leetcode.tree.lowest_common_ancestor_of_a_binary_tree_iii import Solution + + +soln = Solution() + + +def test_case_1(): + tree = array2tree([3, 5, 1, 6, 2, 0, 8, None, None, 7, 4]) + assert tree + assert tree.left + assert tree.right + + lca = soln.lowestCommonAncestor(tree.left, tree.right) + assert lca + + assert lca.val == 3 diff --git a/test/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_test.py b/test/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_test.py new file mode 100644 index 0000000..39af7fb --- /dev/null +++ b/test/leetcode/tree/lowest_common_ancestor_of_a_binary_tree_test.py @@ -0,0 +1,17 @@ +from leetcode.tree.common.tree_node import array2tree +from leetcode.tree.lowest_common_ancestor_of_a_binary_tree import Solution + + +soln = Solution() + + +def test_case_1(): + tree = array2tree([3, 5, 1, 6, 2, 0, 8, None, None, 7, 4]) + assert tree + assert tree.left + assert tree.right + + lca = soln.lowestCommonAncestor(tree, tree.left, tree.right) + assert lca + + assert lca.val == 3 diff --git a/test/leetcode/tree/range_sum_of_bst_test.py b/test/leetcode/tree/range_sum_of_bst_test.py new file mode 100644 index 0000000..19edf3d --- /dev/null +++ b/test/leetcode/tree/range_sum_of_bst_test.py @@ -0,0 +1,11 @@ +from leetcode.tree.common.tree_node import array2tree +from leetcode.tree.range_sum_of_bst import Solution + + +soln = Solution() + + +def test_case_1(): + tree = array2tree([10, 5, 15, 3, 7, None, 18]) + + assert soln.rangeSumBST(tree, 7, 15) == 32 diff --git a/test/leetcode/tree/same_tree_test.py b/test/leetcode/tree/same_tree_test.py new file mode 100644 index 0000000..9d5ac0e --- /dev/null +++ b/test/leetcode/tree/same_tree_test.py @@ -0,0 +1,16 @@ +from leetcode.tree.common.tree_node import array2tree +from leetcode.tree.same_tree import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.isSameTree(None, None) + + +def test_case_2(): + p = array2tree([10, None, None]) + q = array2tree([10, None, None]) + + assert soln.isSameTree(p, q) diff --git a/test/leetcode/tree/sum_root_to_leaf_number_test.py b/test/leetcode/tree/sum_root_to_leaf_number_test.py new file mode 100644 index 0000000..f847869 --- /dev/null +++ b/test/leetcode/tree/sum_root_to_leaf_number_test.py @@ -0,0 +1,13 @@ +from leetcode.tree.common.tree_node import TreeNode +from leetcode.tree.sum_root_to_leaf_numbers import Solution + + +soln = Solution() + + +def test_case_1(): + tree = TreeNode(1) + tree.left = TreeNode(2) + tree.right = TreeNode(3) + + assert soln.sumNumbers(tree) == 25 diff --git a/test/leetcode/two_pointers/bag_of_tokens_test.py b/test/leetcode/two_pointers/bag_of_tokens_test.py new file mode 100644 index 0000000..a9026e0 --- /dev/null +++ b/test/leetcode/two_pointers/bag_of_tokens_test.py @@ -0,0 +1,16 @@ +from leetcode.two_pointers.bag_of_tokens import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.bagOfTokensScore([100], 50) == 0 + + +def test_case_2(): + assert soln.bagOfTokensScore([200, 100], 150) == 1 + + +def test_case_3(): + assert soln.bagOfTokensScore([100, 200, 300, 400], 200) == 2 diff --git a/test/leetcode/two_pointers/container_with_most_water_test.py b/test/leetcode/two_pointers/container_with_most_water_test.py new file mode 100644 index 0000000..74dda0a --- /dev/null +++ b/test/leetcode/two_pointers/container_with_most_water_test.py @@ -0,0 +1,12 @@ +from leetcode.two_pointers.container_with_most_water import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.maxArea([1, 8, 6, 2, 5, 4, 8, 3, 7]) == 49 + + +def test_case_2(): + assert soln.maxArea([1, 1]) == 1 diff --git a/test/leetcode/two_pointers/longest_common_prefix_test.py b/test/leetcode/two_pointers/longest_common_prefix_test.py new file mode 100644 index 0000000..146b818 --- /dev/null +++ b/test/leetcode/two_pointers/longest_common_prefix_test.py @@ -0,0 +1,24 @@ +from leetcode.two_pointers.longest_common_prefix import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.longestCommonPrefix(["ab", "a"]) == "a" + + +def test_case_2(): + assert soln.longestCommonPrefix(["a"]) == "a" + + +def test_case_3(): + assert soln.longestCommonPrefix(["flower", "flow", "flight"]) == "fl" + + +def test_case_4(): + assert soln.longestCommonPrefix(["dog", "racecar", "car"]) == "" + + +def test_case_5(): + assert soln.longestCommonPrefix(["", "car"]) == "" diff --git a/test/leetcode/two_pointers/remove_duplicates_from_sorted_array_test.py b/test/leetcode/two_pointers/remove_duplicates_from_sorted_array_test.py new file mode 100644 index 0000000..7a9a7e7 --- /dev/null +++ b/test/leetcode/two_pointers/remove_duplicates_from_sorted_array_test.py @@ -0,0 +1,8 @@ +from leetcode.two_pointers.remove_duplicates_from_sorted_array import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.removeDuplicates([1, 1, 2]) == 2 diff --git a/test/leetcode/two_pointers/string_compression_test.py b/test/leetcode/two_pointers/string_compression_test.py new file mode 100644 index 0000000..6404364 --- /dev/null +++ b/test/leetcode/two_pointers/string_compression_test.py @@ -0,0 +1,12 @@ +from leetcode.two_pointers.string_compression import Solution + + +soln = Solution() + + +def test_case_1(): + cs = ["a", "a", "b", "b", "c", "c", "c"] + + count = soln.compress(cs) + + assert cs[0:count] == ["a", "2", "b", "2", "c", "3"] diff --git a/test/leetcode/two_pointers/three_sum_closest_test.py b/test/leetcode/two_pointers/three_sum_closest_test.py new file mode 100644 index 0000000..5dc8efe --- /dev/null +++ b/test/leetcode/two_pointers/three_sum_closest_test.py @@ -0,0 +1,12 @@ +from leetcode.two_pointers.three_sum_closest import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.threeSumClosest([-1, 2, 1, -4], 1) == 2 + + +def test_case_2(): + assert soln.threeSumClosest([1, 1, 1, 0], -100) == 2 diff --git a/test/leetcode/two_pointers/three_sum_test.py b/test/leetcode/two_pointers/three_sum_test.py new file mode 100644 index 0000000..5ebd7a5 --- /dev/null +++ b/test/leetcode/two_pointers/three_sum_test.py @@ -0,0 +1,12 @@ +from leetcode.two_pointers.three_sum import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.threeSum([-1, 0, 1, 2, -1, -4]) == [[-1, -1, 2], [-1, 0, 1]] + + +def test_case_2(): + assert soln.threeSum([]) == [] diff --git a/test/leetcode/two_pointers/trapping_rain_water_test.py b/test/leetcode/two_pointers/trapping_rain_water_test.py new file mode 100644 index 0000000..3100c02 --- /dev/null +++ b/test/leetcode/two_pointers/trapping_rain_water_test.py @@ -0,0 +1,16 @@ +from leetcode.two_pointers.trapping_rain_water import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]) == 6 + + +def test_case_2(): + assert soln.trap([4, 2, 0, 3, 2, 5]) == 9 + + +def test_case_3(): + assert soln.trap([4, 2, 3]) == 1 diff --git a/test/leetcode/two_pointers/two_sum_ii_input_array_sorted_test.py b/test/leetcode/two_pointers/two_sum_ii_input_array_sorted_test.py new file mode 100644 index 0000000..ccd6259 --- /dev/null +++ b/test/leetcode/two_pointers/two_sum_ii_input_array_sorted_test.py @@ -0,0 +1,12 @@ +from leetcode.two_pointers.two_sum_ii_input_array_sorted import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.twoSum([2, 7, 11, 15], 9) == [1, 2] + + +def test_case_2(): + assert soln.twoSum([2, 3, 4], 6) == [1, 3] diff --git a/test/leetcode/two_pointers/valid_palindrome_ii_test.py b/test/leetcode/two_pointers/valid_palindrome_ii_test.py new file mode 100644 index 0000000..c617aa8 --- /dev/null +++ b/test/leetcode/two_pointers/valid_palindrome_ii_test.py @@ -0,0 +1,8 @@ +from leetcode.two_pointers.valid_palindrome_ii import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.validPalindrome("aba") diff --git a/test/leetcode/two_pointers/valid_palindrome_test.py b/test/leetcode/two_pointers/valid_palindrome_test.py new file mode 100644 index 0000000..69867ba --- /dev/null +++ b/test/leetcode/two_pointers/valid_palindrome_test.py @@ -0,0 +1,8 @@ +from leetcode.two_pointers.valid_palindrome import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.isPalindrome("A man, a plan, a canal: Panama") diff --git a/test/leetcode/two_pointers/valid_triangle_number_test.py b/test/leetcode/two_pointers/valid_triangle_number_test.py new file mode 100644 index 0000000..4257498 --- /dev/null +++ b/test/leetcode/two_pointers/valid_triangle_number_test.py @@ -0,0 +1,8 @@ +from leetcode.two_pointers.valid_triangle_number import Solution + + +soln = Solution() + + +def test_case_1(): + assert soln.triangleNumber([2, 2, 3, 4]) == 3 diff --git a/test/linked-list/add-two-numbers.test.ts b/test/linked-list/add-two-numbers.test.ts deleted file mode 100644 index 93b7bbf..0000000 --- a/test/linked-list/add-two-numbers.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { array2list, list2array } from '../../src/linked-list/common/list-node'; -import { addTwoNumbers } from '../../src/linked-list/add-two-numbers'; - -describe('add two numbers', () => { - test('add two numbers - test case 1', async () => { - const x = array2list([2, 4, 3]); - const y = array2list([5, 6, 4]); - const z = addTwoNumbers(x, y); - - expect(list2array(z)).toStrictEqual([7, 0, 8]); - }); - - test('add two numbers - test case 2', async () => { - const x = array2list([9, 9, 9, 9, 9, 9, 9]); - const y = array2list([9, 9, 9, 9]); - const z = addTwoNumbers(x, y); - - expect(list2array(z)).toStrictEqual([8, 9, 9, 9, 0, 0, 0, 1]); - }); - - test('add two numbers - test case 3', async () => { - const x = array2list([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); - const y = array2list([5, 6, 4]); - const z = addTwoNumbers(x, y); - - expect(list2array(z)).toStrictEqual([ - 6, 6, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 - ]); - }); -}); diff --git a/test/linked-list/all-one.test.ts b/test/linked-list/all-one.test.ts deleted file mode 100644 index 3924a6e..0000000 --- a/test/linked-list/all-one.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { AllOne } from '../../src/linked-list/all-one'; - -describe('all O`one data structure', () => { - test('all one data structure - test case 1', async () => { - const allone = new AllOne(); - allone.inc('hello'); - allone.inc('hello'); - - expect(allone.getMaxKey()).toBe('hello'); - expect(allone.getMinKey()).toBe('hello'); - - allone.inc('leet'); - - expect(allone.getMaxKey()).toBe('hello'); - expect(allone.getMinKey()).toBe('leet'); - }); - - test('all one data structure - test case 2', async () => { - const allone = new AllOne(); - allone.inc('a'); - allone.inc('b'); - allone.inc('b'); - allone.inc('c'); - allone.inc('c'); - allone.inc('c'); - allone.dec('b'); - allone.dec('b'); - - expect(allone.getMinKey()).toBe('a'); - - allone.dec('a'); - - expect(allone.getMaxKey()).toBe('c'); - expect(allone.getMinKey()).toBe('c'); - }); -}); diff --git a/test/linked-list/lru-cache.test.ts b/test/linked-list/lru-cache.test.ts deleted file mode 100644 index 554e002..0000000 --- a/test/linked-list/lru-cache.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { LRUCache } from '../../src/linked-list/lru-cache'; - -describe('lru cache', () => { - test('lru cache - test case 1', async () => { - const cache = new LRUCache(2); - - cache.put(1, 1); - cache.put(2, 2); - - expect(cache.get(1)).toBe(1); - - cache.put(3, 3); - - expect(cache.get(2)).toBe(-1); - - cache.put(4, 4); - - expect(cache.get(1)).toBe(-1); - expect(cache.get(3)).toBe(3); - expect(cache.get(4)).toBe(4); - }); - - test('lru cache - test case 2', async () => { - const cache = new LRUCache(2); - - expect(cache.get(2)).toBe(-1); - expect(cache.get(2)).toBe(-1); - - cache.put(2, 6); - - expect(cache.get(1)).toBe(-1); - - cache.put(1, 5); - cache.put(1, 2); - - expect(cache.get(1)).toBe(2); - expect(cache.get(2)).toBe(6); - }); - - test('lru cache - test case 3', async () => { - const cache = new LRUCache(3); - - cache.put(1, 1); - cache.put(2, 2); - cache.put(3, 3); - cache.put(4, 4); - - expect(cache.get(4)).toBe(4); - expect(cache.get(3)).toBe(3); - expect(cache.get(2)).toBe(2); - expect(cache.get(1)).toBe(-1); - - cache.put(5, 5); - - expect(cache.get(1)).toBe(-1); - expect(cache.get(2)).toBe(2); - expect(cache.get(3)).toBe(3); - expect(cache.get(4)).toBe(-1); - expect(cache.get(5)).toBe(5); - }); -}); diff --git a/test/linked-list/merge-k-sorted-lists.test.ts b/test/linked-list/merge-k-sorted-lists.test.ts deleted file mode 100644 index 3c220f3..0000000 --- a/test/linked-list/merge-k-sorted-lists.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { array2list, list2array } from '../../src/linked-list/common/list-node'; -import { mergeKLists } from '../../src/heap/merge-k-sorted-lists'; - -describe('merge k sorted lists', () => { - test('merge k sorted lists - test case 1', async () => { - const lists = [null, null].map(array2list); - const merged = mergeKLists(lists); - - expect(merged).toBeNull(); - }); - - test('merge k sorted lists - test case 2', async () => { - const lists = [[1], null].map(array2list); - const merged = mergeKLists(lists); - - expect(list2array(merged)).toStrictEqual([1]); - }); - - test('merge k sorted lists - test case 3', async () => { - const lists = [null, [1]].map(array2list); - const merged = mergeKLists(lists); - - expect(list2array(merged)).toStrictEqual([1]); - }); - - test('merge k sorted lists - test case 4', async () => { - const lists = [[1], [2]].map(array2list); - const merged = mergeKLists(lists); - - expect(list2array(merged)).toStrictEqual([1, 2]); - }); - - test('merge k sorted lists - test case 5', async () => { - const lists = [ - [1, 2, 4], - [1, 3, 4] - ].map(array2list); - const merged = mergeKLists(lists); - - expect(list2array(merged)).toStrictEqual([1, 1, 2, 3, 4, 4]); - }); - - test('merge k sorted lists - test case 6', async () => { - const lists = [ - [1, 4, 5], - [1, 3, 4], - [2, 6] - ].map(array2list); - const merged = mergeKLists(lists); - - expect(list2array(merged)).toStrictEqual([1, 1, 2, 3, 4, 4, 5, 6]); - }); -}); diff --git a/test/linked-list/merge-two-sorted-lists.test.ts b/test/linked-list/merge-two-sorted-lists.test.ts deleted file mode 100644 index 7b09e9e..0000000 --- a/test/linked-list/merge-two-sorted-lists.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { array2list, list2array } from '../../src/linked-list/common/list-node'; -import { mergeTwoLists } from '../../src/linked-list/merge-two-sorted-lists'; - -describe('merge two sorted lists', () => { - test('merge two sorted lists - test case 1', async () => { - const a = null; - const b = null; - const c = mergeTwoLists(a, b); - - expect(c).toBeNull(); - }); - - test('merge two sorted lists - test case 2', async () => { - const a = array2list([1]); - const b = null; - const c = mergeTwoLists(a, b); - - expect(list2array(c)).toStrictEqual([1]); - }); - - test('merge two sorted lists - test case 3', async () => { - const a = null; - const b = array2list([1]); - const c = mergeTwoLists(a, b); - - expect(list2array(c)).toStrictEqual([1]); - }); - - test('merge two sorted lists - test case 4', async () => { - const a = array2list([1]); - const b = array2list([2]); - const c = mergeTwoLists(a, b); - - expect(list2array(c)).toStrictEqual([1, 2]); - }); - - test('merge two sorted lists - test case 5', async () => { - const a = array2list([1, 2, 4]); - const b = array2list([2, 3, 4]); - const c = mergeTwoLists(a, b); - - expect(list2array(c)).toStrictEqual([1, 2, 2, 3, 4, 4]); - }); -}); diff --git a/test/math/__data__/task-scheduler.test.json b/test/math/__data__/task-scheduler.test.json deleted file mode 100644 index e5a6199..0000000 --- a/test/math/__data__/task-scheduler.test.json +++ /dev/null @@ -1 +0,0 @@ -["G","C","A","H","A","G","G","F","G","J","H","C","A","G","E","A","H","E","F","D","B","D","H","H","E","G","F","B","C","G","F","H","J","F","A","C","G","D","I","J","A","G","D","F","B","F","H","I","G","J","G","H","F","E","H","J","C","E","H","F","C","E","F","H","H","I","G","A","G","D","C","B","I","D","B","C","J","I","B","G","C","H","D","I","A","B","A","J","C","E","B","F","B","J","J","D","D","H","I","I","B","A","E","H","J","J","A","J","E","H","G","B","F","C","H","C","B","J","B","A","H","B","D","I","F","A","E","J","H","C","E","G","F","G","B","G","C","G","A","H","E","F","H","F","C","G","B","I","E","B","J","D","B","B","G","C","A","J","B","J","J","F","J","C","A","G","J","E","G","J","C","D","D","A","I","A","J","F","H","J","D","D","D","C","E","D","D","F","B","A","J","D","I","H","B","A","F","E","B","J","A","H","D","E","I","B","H","C","C","C","G","C","B","E","A","G","H","H","A","I","A","B","A","D","A","I","E","C","C","D","A","B","H","D","E","C","A","H","B","I","A","B","E","H","C","B","A","D","H","E","J","B","J","A","B","G","J","J","F","F","H","I","A","H","F","C","H","D","H","C","C","E","I","G","J","H","D","E","I","J","C","C","H","J","C","G","I","E","D","E","H","J","A","H","D","A","B","F","I","F","J","J","H","D","I","C","G","J","C","C","D","B","E","B","E","B","G","B","A","C","F","E","H","B","D","C","H","F","A","I","A","E","J","F","A","E","B","I","G","H","D","B","F","D","B","I","B","E","D","I","D","F","A","E","H","B","I","G","F","D","E","B","E","C","C","C","J","J","C","H","I","B","H","F","H","F","D","J","D","D","H","H","C","D","A","J","D","F","D","G","B","I","F","J","J","C","C","I","F","G","F","C","E","G","E","F","D","A","I","I","H","G","H","H","A","J","D","J","G","F","G","E","E","A","H","B","G","A","J","J","E","I","H","A","G","E","C","D","I","B","E","A","G","A","C","E","B","J","C","B","A","D","J","E","J","I","F","F","C","B","I","H","C","F","B","C","G","D","A","A","B","F","C","D","B","I","I","H","H","J","A","F","J","F","J","F","H","G","F","D","J","G","I","E","B","C","G","I","F","F","J","H","H","G","A","A","J","C","G","F","B","A","A","E","E","A","E","I","G","F","D","B","I","F","A","B","J","F","F","J","B","F","J","F","J","F","I","E","J","H","D","G","G","D","F","G","B","J","F","J","A","J","E","G","H","I","E","G","D","I","B","D","J","A","A","G","A","I","I","A","A","I","I","H","E","C","A","G","I","F","F","C","D","J","J","I","A","A","F","C","J","G","C","C","H","E","A","H","F","B","J","G","I","A","A","H","G","B","E","G","D","I","C","G","J","C","C","I","H","B","D","J","H","B","J","H","B","F","J","E","J","A","G","H","B","E","H","B","F","F","H","E","B","E","G","H","J","G","J","B","H","C","H","A","A","B","E","I","H","B","I","D","J","J","C","D","G","I","J","G","J","D","F","J","E","F","D","E","B","D","B","C","B","B","C","C","I","F","D","E","I","G","G","I","B","H","G","J","A","A","H","I","I","H","A","I","F","C","D","A","C","G","E","G","E","E","H","D","C","G","D","I","A","G","G","D","A","H","H","I","F","E","I","A","D","H","B","B","G","I","C","G","B","I","I","D","F","F","C","C","A","I","E","A","E","J","A","H","C","D","A","C","B","G","H","G","J","G","I","H","B","A","C","H","I","D","D","C","F","G","B","H","E","B","B","H","C","B","G","G","C","F","B","E","J","B","B","I","D","H","D","I","I","A","A","H","G","F","B","J","F","D","E","G","F","A","G","G","D","A","B","B","B","J","A","F","H","H","D","C","J","I","A","H","G","C","J","I","F","J","C","A","E","C","H","J","H","H","F","G","E","A","C","F","J","H","D","G","G","D","D","C","B","H","B","C","E","F","B","D","J","H","J","J","J","A","F","F","D","E","F","C","I","B","H","H","D","E","A","I","A","B","F","G","F","F","I","E","E","G","A","I","D","F","C","H","E","C","G","H","F","F","H","J","H","G","A","E","H","B","G","G","D","D","D","F","I","A","F","F","D","E","H","J","E","D","D","A","J","F","E","E","E","F","I","D","A","F","F","J","E","I","J","D","D","G","A","C","G","G","I","E","G","E","H","E","D","E","J","B","G","I","J","C","H","C","C","A","A","B","C","G","B","D","I","D","E","H","J","J","B","F","E","J","H","H","I","G","B","D"] \ No newline at end of file diff --git a/test/math/__snapshots__/sequential-digits.test.ts.snap b/test/math/__snapshots__/sequential-digits.test.ts.snap deleted file mode 100644 index 2549a1c..0000000 --- a/test/math/__snapshots__/sequential-digits.test.ts.snap +++ /dev/null @@ -1,20 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`sequential digits sequential digits - test case 1 1`] = ` -[ - 123, - 234, -] -`; - -exports[`sequential digits sequential digits - test case 2 1`] = ` -[ - 1234, - 2345, - 3456, - 4567, - 5678, - 6789, - 12345, -] -`; diff --git a/test/math/number-of-one-bits.test.ts b/test/math/number-of-one-bits.test.ts deleted file mode 100644 index 23e3833..0000000 --- a/test/math/number-of-one-bits.test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { hammingWeight } from '../../src/math/number-of-one-bits'; - -describe('number of one bits', () => { - test('number of one bits - test case 1', async () => { - // Because 11 = 0b1001 - expect(hammingWeight(11)).toBe(3); - }); -}); diff --git a/test/math/pow-x-n.test.ts b/test/math/pow-x-n.test.ts deleted file mode 100644 index 7c01722..0000000 --- a/test/math/pow-x-n.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { myPow } from '../../src/math/pow-x-n'; - -describe('pow', () => { - test('pow - test case 1', async () => { - expect(myPow(2, -2)).toBe(0.25); - }); - - test('pow - test case 2', async () => { - expect(myPow(2, -1)).toBe(0.5); - }); - - test('pow - test case 3', async () => { - expect(myPow(2, 0)).toBe(1); - }); - - test('pow - test case 4', async () => { - expect(myPow(2, 1)).toBe(2); - }); - - test('pow - test case 5', async () => { - expect(myPow(2, 4)).toBe(16); - }); - - test('pow - test case 6', async () => { - expect(myPow(2, 5)).toBe(32); - }); - - test('pow - test case 7', async () => { - expect(myPow(2, -2147483648)).toBe(0); - }); -}); diff --git a/test/math/reverse-integer.test.ts b/test/math/reverse-integer.test.ts deleted file mode 100644 index 8c29656..0000000 --- a/test/math/reverse-integer.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { reverse } from '../../src/math/reverse-integer'; - -describe('reverse integer', () => { - test('reverse integer - test case 1', async () => { - expect(reverse(123)).toBe(321); - }); -}); diff --git a/test/math/rotate-image.test.ts b/test/math/rotate-image.test.ts deleted file mode 100644 index 2904663..0000000 --- a/test/math/rotate-image.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { rotate } from '../../src/math/rotate-image'; - -describe('rotate image', () => { - test('rotate image - test case 1', async () => { - const matrix = [ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9] - ]; - - rotate(matrix); - - expect(matrix).toStrictEqual([ - [7, 4, 1], - [8, 5, 2], - [9, 6, 3] - ]); - }); -}); diff --git a/test/math/sequential-digits.test.ts b/test/math/sequential-digits.test.ts deleted file mode 100644 index 17b70b1..0000000 --- a/test/math/sequential-digits.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { sequentialDigits } from '../../src/math/sequential-digits'; - -describe('sequential digits', () => { - test('sequential digits - test case 1', async () => { - expect(sequentialDigits(100, 300)).toMatchSnapshot(); - }); - - test('sequential digits - test case 2', async () => { - expect(sequentialDigits(1000, 13000)).toMatchSnapshot(); - }); -}); diff --git a/test/matrix/robot-bounded-in-circle.test.ts b/test/matrix/robot-bounded-in-circle.test.ts deleted file mode 100644 index 28ac884..0000000 --- a/test/matrix/robot-bounded-in-circle.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { isRobotBounded } from '../../src/matrix/robot-bounded-in-circle'; - -describe('robot bounded in circle', () => { - test('isRobotBounded - test case 1', () => { - const instructions = 'GGLLGG'; - - expect(isRobotBounded(instructions)).toEqual(true); - }); -}); diff --git a/test/matrix/valid-word-square.test.ts b/test/matrix/valid-word-square.test.ts deleted file mode 100644 index 9ed763b..0000000 --- a/test/matrix/valid-word-square.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { validWordSquare } from '../../src/matrix/valid-word-square'; - -describe('valid word square', () => { - test('valid word square - test case 1', async () => { - const words = ['abcd', 'bnrt', 'crmy', 'dtye']; - - expect(validWordSquare(words)).toBe(true); - }); - - test('valid word square - test case 2', async () => { - const words = ['abcd', 'bnrt', 'crm', 'dt']; - - expect(validWordSquare(words)).toBe(true); - }); -}); diff --git a/test/prefix-sum/continuous-subarray-sum.test.ts b/test/prefix-sum/continuous-subarray-sum.test.ts deleted file mode 100644 index ff98222..0000000 --- a/test/prefix-sum/continuous-subarray-sum.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { checkSubarraySum } from '../../src/prefix-sum/continuous-subarray-sum'; - -describe('continuous subarray sum', () => { - test('checkSubarraySum - test case 1', () => { - expect(checkSubarraySum([23, 2, 4, 6, 7], 6)).toBe(true); - }); - - test('checkSubarraySum - test case 2', () => { - expect(checkSubarraySum([1, 1], 2)).toBe(true); - }); - - test('checkSubarraySum - test case 3', () => { - expect(checkSubarraySum([-10, 10], 1)).toBe(true); - }); -}); diff --git a/test/prefix-sum/max-sum-obtained-of-any-permutation.test.ts b/test/prefix-sum/max-sum-obtained-of-any-permutation.test.ts deleted file mode 100644 index 26feb52..0000000 --- a/test/prefix-sum/max-sum-obtained-of-any-permutation.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { maxSumRangeQuery } from '../../src/prefix-sum/max-sum-obtained-of-any-permutation'; - -describe('maximum sum obtained of any permutation', () => { - test('maximum sum obtained of any permutation - test case 1', async () => { - expect( - maxSumRangeQuery( - [1, 2, 3, 4, 5], - [ - [1, 3], - [0, 1] - ] - ) - ).toBe(19); - }); -}); diff --git a/test/prefix-sum/range-sum-query-immutable.test.ts b/test/prefix-sum/range-sum-query-immutable.test.ts deleted file mode 100644 index d50c2d8..0000000 --- a/test/prefix-sum/range-sum-query-immutable.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NumArray } from '../../src/prefix-sum/range-sum-query-immutable'; - -describe('range sum query immutable', () => { - test('range sum query immutable - test case 1', async () => { - const arr = new NumArray([-2, 0, 3, -5, 2, -1]); - - expect(arr.sumRange(0, 2)).toBe(1); - expect(arr.sumRange(2, 5)).toBe(-1); - expect(arr.sumRange(0, 5)).toBe(-3); - }); -}); diff --git a/test/prefix-sum/running-sum-of-1d-array.test.ts b/test/prefix-sum/running-sum-of-1d-array.test.ts deleted file mode 100644 index 248c013..0000000 --- a/test/prefix-sum/running-sum-of-1d-array.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { runningSum } from '../../src/prefix-sum/running-sum-of-1d-array'; - -describe('running sum of 1d array', () => { - test('running sum - test case 1', async () => { - expect(runningSum([1, 2, 3, 4])).toStrictEqual([1, 3, 6, 10]); - }); -}); diff --git a/test/prefix-sum/subarray-sum-equals-k.test.ts b/test/prefix-sum/subarray-sum-equals-k.test.ts deleted file mode 100644 index 91be976..0000000 --- a/test/prefix-sum/subarray-sum-equals-k.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { subarraySum } from '../../src/prefix-sum/subarray-sum-equals-k'; - -describe('subarray sum equals k', () => { - test('subarray sum equals k', async () => { - expect(subarraySum([1, 1, 1], 2)).toBe(2); - }); -}); diff --git a/test/recursion/__snapshots__/generate-parentheses.test.ts.snap b/test/recursion/__snapshots__/generate-parentheses.test.ts.snap deleted file mode 100644 index 84edeb4..0000000 --- a/test/recursion/__snapshots__/generate-parentheses.test.ts.snap +++ /dev/null @@ -1,30 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`generate parentheses generate parenthesis - test case 4 1`] = ` -Set { - "((()))", - "(()())", - "(())()", - "()(())", - "()()()", -} -`; - -exports[`generate parentheses generate parenthesis - test case 5 1`] = ` -Set { - "(((())))", - "((()()))", - "((())())", - "((()))()", - "(()(()))", - "(()()())", - "(()())()", - "(())(())", - "(())()()", - "()((()))", - "()(()())", - "()(())()", - "()()(())", - "()()()()", -} -`; diff --git a/test/recursion/__snapshots__/permutations.test.ts.snap b/test/recursion/__snapshots__/permutations.test.ts.snap deleted file mode 100644 index 96ccf70..0000000 --- a/test/recursion/__snapshots__/permutations.test.ts.snap +++ /dev/null @@ -1,49 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`permute permute - test case 2 1`] = ` -[ - [ - 1, - 2, - 3, - ], - [ - 1, - 3, - 2, - ], - [ - 2, - 1, - 3, - ], - [ - 2, - 3, - 1, - ], - [ - 3, - 1, - 2, - ], - [ - 3, - 2, - 1, - ], -] -`; - -exports[`permute permute - test case 3 1`] = ` -[ - [ - 0, - 1, - ], - [ - 1, - 0, - ], -] -`; diff --git a/test/recursion/__snapshots__/subsets.test.ts.snap b/test/recursion/__snapshots__/subsets.test.ts.snap deleted file mode 100644 index eb64e59..0000000 --- a/test/recursion/__snapshots__/subsets.test.ts.snap +++ /dev/null @@ -1,42 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`subsets subsets - test case 2 1`] = ` -[ - [], - [ - 3, - ], - [ - 2, - ], - [ - 3, - 2, - ], - [ - 1, - ], - [ - 3, - 1, - ], - [ - 2, - 1, - ], - [ - 3, - 2, - 1, - ], -] -`; - -exports[`subsets subsets - test case 3 1`] = ` -[ - [], - [ - 0, - ], -] -`; diff --git a/test/recursion/__snapshots__/word-break-ii.test.ts.snap b/test/recursion/__snapshots__/word-break-ii.test.ts.snap deleted file mode 100644 index 342dee4..0000000 --- a/test/recursion/__snapshots__/word-break-ii.test.ts.snap +++ /dev/null @@ -1,16 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`word break ii word break ii - test case 1 1`] = ` -[ - "cat sand dog", - "cats and dog", -] -`; - -exports[`word break ii word break ii - test case 2 1`] = ` -[ - "pine apple pen apple", - "pine applepen apple", - "pineapple pen apple", -] -`; diff --git a/test/recursion/generate-parentheses.test.ts b/test/recursion/generate-parentheses.test.ts deleted file mode 100644 index 8ef5d92..0000000 --- a/test/recursion/generate-parentheses.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { generateParenthesis } from '../../src/recursion/generate-parentheses'; - -describe('generate parentheses', () => { - test('generate parenthesis - test case 3', async () => { - const set = new Set(generateParenthesis(2)); - - expect(set).toStrictEqual(new Set(['()()', '(())'])); - }); - - test('generate parenthesis - test case 4', async () => { - const set = new Set(generateParenthesis(3)); - - expect(set).toMatchSnapshot(); - }); - - test('generate parenthesis - test case 5', async () => { - const set = new Set(generateParenthesis(4)); - - expect(set).toMatchSnapshot(); - }); -}); diff --git a/test/recursion/maximum-number-of-operations-with-the-same-score.test.ts b/test/recursion/maximum-number-of-operations-with-the-same-score.test.ts deleted file mode 100644 index 02af347..0000000 --- a/test/recursion/maximum-number-of-operations-with-the-same-score.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { maxOperations } from '../../src/recursion/maximum-number-of-operations-with-the-same-score'; - -describe('maximum number of operations with the same score', () => { - test('max operations - test case 1', async () => { - expect(maxOperations([3, 2, 1, 2, 3, 4])).toBe(3); - }); - - test('max operations - test case 2', async () => { - expect(maxOperations([3, 2, 6, 1, 4])).toBe(2); - }); - - test('max operations - test case 3', async () => { - expect(maxOperations([3, 2, 1, 4, 1])).toBe(2); - }); -}); diff --git a/test/recursion/optimal-account-balancing.test.ts b/test/recursion/optimal-account-balancing.test.ts deleted file mode 100644 index c19f855..0000000 --- a/test/recursion/optimal-account-balancing.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { minTransfers } from '../../src/recursion/optimal-account-balancing'; - -describe('optimal account balancing', () => { - test('min transfers - test case 1', async () => { - expect( - minTransfers([ - [0, 1, 10], - [2, 0, 5] - ]) - ).toBe(2); - }); - - test('min transfers - test case 2', async () => { - expect( - minTransfers([ - [0, 1, 10], - [1, 0, 1], - [1, 2, 5], - [2, 0, 5] - ]) - ).toBe(1); - }); - - test('min transfers - test case 3', async () => { - expect( - minTransfers([ - [0, 1, 1], - [1, 2, 1], - [2, 3, 4], - [3, 4, 5] - ]) - ).toBe(3); - }); -}); diff --git a/test/recursion/permutations.test.ts b/test/recursion/permutations.test.ts deleted file mode 100644 index b28985a..0000000 --- a/test/recursion/permutations.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { permute } from '../../src/recursion/permutations'; - -describe('permute', () => { - test('permute - test case 1', async () => { - expect(permute([])).toStrictEqual([[]]); - }); - - test('permute - test case 2', async () => { - expect(permute([1, 2, 3])).toMatchSnapshot(); - }); - - test('permute - test case 3', async () => { - expect(permute([0, 1])).toMatchSnapshot(); - }); - - test('permute - test case 4', async () => { - expect(permute([1])).toStrictEqual([[1]]); - }); -}); diff --git a/test/recursion/subsets.test.ts b/test/recursion/subsets.test.ts deleted file mode 100644 index e5b1017..0000000 --- a/test/recursion/subsets.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { subsets } from '../../src/recursion/subsets'; - -describe('subsets', () => { - test('subsets - test case 1', async () => { - expect(subsets([])).toStrictEqual([[]]); - }); - - test('subsets - test case 2', async () => { - expect(subsets([1, 2, 3])).toMatchSnapshot(); - }); - - test('subsets - test case 3', async () => { - expect(subsets([0])).toMatchSnapshot(); - }); -}); diff --git a/test/recursion/word-break-ii.test.ts b/test/recursion/word-break-ii.test.ts deleted file mode 100644 index bd59dab..0000000 --- a/test/recursion/word-break-ii.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { wordBreak } from '../../src/recursion/word-break-ii'; - -describe('word break ii', () => { - test('word break ii - test case 1', async () => { - const s = 'catsanddog'; - const words = ['cat', 'cats', 'and', 'sand', 'dog']; - - expect(wordBreak(s, words)).toMatchSnapshot(); - }); - - test('word break ii - test case 2', async () => { - const s = 'pineapplepenapple'; - const words = ['apple', 'pen', 'applepen', 'pine', 'pineapple']; - - expect(wordBreak(s, words)).toMatchSnapshot(); - }); - - test('word break ii - test case 2', async () => { - const s = 'catsandog'; - const words = ['cats', 'dog', 'sand', 'and', 'cat']; - - expect(wordBreak(s, words)).toStrictEqual([]); - }); -}); diff --git a/test/sliding-window/best-time-to-buy-and-sell-stock-i.test.ts b/test/sliding-window/best-time-to-buy-and-sell-stock-i.test.ts deleted file mode 100644 index 2c4da89..0000000 --- a/test/sliding-window/best-time-to-buy-and-sell-stock-i.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { maxProfit } from '../../src/sliding-window/best-time-to-buy-and-sell-stock-i'; - -describe('best time to buy and sell stock i', () => { - test('max profit i - test case 1', async () => { - expect(maxProfit([7, 1, 5, 3, 6, 4])).toBe(5); - }); - - test('max profit i - test case 2', async () => { - expect(maxProfit([7, 6, 4, 3, 1])).toBe(0); - }); -}); diff --git a/test/sliding-window/count-subarrays-where-max-element-appears.test.ts b/test/sliding-window/count-subarrays-where-max-element-appears.test.ts deleted file mode 100644 index add1b31..0000000 --- a/test/sliding-window/count-subarrays-where-max-element-appears.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { countSubarrays } from '../../src/sliding-window/count-subarrays-where-max-element-appears'; - -describe('count subarrays where max element appears at least k times', () => { - test('count subarrays - test case 1', async () => { - expect(countSubarrays([1, 3, 2, 3, 3], 2)).toBe(6); - }); - - test.skip('count subarrays - test case 2', async () => { - expect(countSubarrays([1, 4, 2, 1], 3)).toBe(0); - }); - - test.skip('count subarrays - test case 3', async () => { - expect( - countSubarrays( - [61, 23, 38, 23, 56, 40, 82, 56, 82, 82, 82, 70, 8, 69, 8, 7, 19, 14, 58, 42, 82, 10, 82, 78, 15, 82], - 2 - ) - ).toBe(224); - }); - - test.skip('count subarrays - test case 4', async () => { - const data = fs - .readFileSync(path.join(__dirname, '__data__', 'count-subarrays-where-max-element-appears.test.json')) - .toString(); - const nums = JSON.parse(data); - - expect(countSubarrays(nums, 13)).toBe(263559); - }); -}); diff --git a/test/sliding-window/length-of-longest-substring.test.ts b/test/sliding-window/length-of-longest-substring.test.ts deleted file mode 100644 index 81dbb98..0000000 --- a/test/sliding-window/length-of-longest-substring.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { lengthOfLongestSubstring } from '../../src/two-pointer/longest-substring-without-repeating-chars'; - -describe('length of longest substring without repeated characters', () => { - test('length of longest substring - test case 1', async () => { - expect(lengthOfLongestSubstring('abcabcbb')).toBe(3); - }); - - test('length of longest substring - test case 2', async () => { - expect(lengthOfLongestSubstring('bbbbb')).toBe(1); - }); - - test('length of longest substring - test case 3', async () => { - expect(lengthOfLongestSubstring('pwwkew')).toBe(3); - }); -}); diff --git a/test/sliding-window/longest-continuous-subarray.test.ts b/test/sliding-window/longest-continuous-subarray.test.ts deleted file mode 100644 index a15ae8a..0000000 --- a/test/sliding-window/longest-continuous-subarray.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { longestSubarray } from '../../src/sliding-window/longest-continuous-subarray'; - -describe('longest continuous subarray with absolute diff less than or equal to limit', () => { - test('longest subarray - test case 1', async () => { - expect(longestSubarray([8, 2, 4, 7], 4)).toBe(2); - }); - - test('longest subarray - test case 2', async () => { - expect(longestSubarray([10, 1, 2, 4, 7, 2], 5)).toBe(4); - }); - - test('longest subarray - test case 3', async () => { - expect(longestSubarray([4, 2, 2, 2, 4, 4, 2, 2], 0)).toBe(3); - }); - - test('longest subarray - test case 4', async () => { - expect(longestSubarray([1, 5, 6, 7, 8, 10, 6, 5, 6], 4)).toBe(5); - }); -}); diff --git a/test/sliding-window/max-sum-distinct-subarray-of-size-k.test.ts b/test/sliding-window/max-sum-distinct-subarray-of-size-k.test.ts deleted file mode 100644 index cf0a2e9..0000000 --- a/test/sliding-window/max-sum-distinct-subarray-of-size-k.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { maximumSubarraySum } from '../../src/sliding-window/max-sum-distinct-subarray-of-size-k'; - -describe('max sum of distincts subarray with length k', () => { - test('max subarray sum - test case 1', async () => { - expect(maximumSubarraySum([1, 5, 4, 2, 9, 9, 9], 3)).toBe(15); - }); - - test('max subarray sum - test case 2', async () => { - expect(maximumSubarraySum([4, 4, 4], 3)).toBe(0); - }); -}); diff --git a/test/sliding-window/minimum-window-substring.test.ts b/test/sliding-window/minimum-window-substring.test.ts deleted file mode 100644 index 3f5fdb0..0000000 --- a/test/sliding-window/minimum-window-substring.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { minWindow } from '../../src/sliding-window/minimum-window-substring'; - -describe('minimum window substring', () => { - test('minWindow - test case 1', async () => { - expect(minWindow('ADOBECODEBANC', 'ABC')).toStrictEqual('BANC'); - }); -}); diff --git a/test/sliding-window/repeated-dna-sequences.test.ts b/test/sliding-window/repeated-dna-sequences.test.ts deleted file mode 100644 index 9c01809..0000000 --- a/test/sliding-window/repeated-dna-sequences.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { findRepeatedDnaSequences } from '../../src/sliding-window/repeated-dna-sequences'; - -describe('repeated dna sequences', () => { - test('repeated dna sequences - test case 1', async () => { - expect(findRepeatedDnaSequences('AAAAAAAAAAAAA')).toStrictEqual(['AAAAAAAAAA']); - }); - - test('repeated dna sequences - test case 2', async () => { - expect(findRepeatedDnaSequences('AAAAAAAAAAA')).toStrictEqual(['AAAAAAAAAA']); - }); -}); diff --git a/test/stack/basic-calculator-ii.test.ts b/test/stack/basic-calculator-ii.test.ts deleted file mode 100644 index af7218d..0000000 --- a/test/stack/basic-calculator-ii.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { calculate } from '../../src/stack/basic-calculator-ii'; - -describe('basic calculator ii', () => { - test('calculate - test case 1', async () => { - expect(calculate('3+2*2')).toStrictEqual(7); - }); - - test('calculate - test case 2', async () => { - expect(calculate(' 3/2 ')).toStrictEqual(1); - }); -}); diff --git a/test/stack/basic-calculator.test.ts b/test/stack/basic-calculator.test.ts deleted file mode 100644 index 0c3c8ad..0000000 --- a/test/stack/basic-calculator.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { calculate } from '../../src/stack/basic-calculator'; - -describe('basic calculator', () => { - test('calculate - test case 1', async () => { - expect(calculate('1 + 1')).toStrictEqual(2); - }); - - test('calculate - test case 2', async () => { - expect(calculate('(1+(4+5+2)-3)+(6+8)')).toStrictEqual(23); - }); -}); diff --git a/test/stack/key-value-store-nested-transactions.test.ts b/test/stack/key-value-store-nested-transactions.test.ts deleted file mode 100644 index ab10f17..0000000 --- a/test/stack/key-value-store-nested-transactions.test.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { KeyValueStore } from '../../src/stack/key-value-store-nested-transactions'; - -describe('key value store with nested transactions', () => { - test('key value store with nested transactions - test case 1', async () => { - const store = new KeyValueStore(); - - store.set('a', 1); - store.set('b', 2); - store.set('c', 3); - - expect(store.get('a')).toBe(1); - expect(store.get('b')).toBe(2); - expect(store.get('c')).toBe(3); - - store.delete('c'); - - expect(store.get('c')).toBeUndefined(); - }); - - test('key value store with nested transactions - test case 2', async () => { - const store = new KeyValueStore(); - - store.set('a', 1); - store.set('b', 2); - store.set('c', 3); - store.begin(); - store.set('a', 10); - store.set('b', 20); - store.delete('c'); - - expect(store.get('a')).toBe(10); - expect(store.get('b')).toBe(20); - expect(store.get('c')).toBeUndefined(); - - store.commit(); - - expect(store.get('a')).toBe(10); - expect(store.get('b')).toBe(20); - }); - - test('key value store with nested transactions - test case 3', async () => { - const store = new KeyValueStore(); - - store.set('a', 1); - store.set('b', 2); - store.set('c', 3); - store.begin(); - store.set('a', 10); - store.set('b', 20); - store.delete('c'); - - expect(store.get('a')).toBe(10); - expect(store.get('b')).toBe(20); - expect(store.get('c')).toBeUndefined(); - - store.rollback(); - - expect(store.get('a')).toBe(1); - expect(store.get('b')).toBe(2); - expect(store.get('c')).toBe(3); - }); - - test('key value store with nested transactions - test case 4', async () => { - const store = new KeyValueStore(); - - store.set('a', 1); - store.begin(); - store.set('a', 10); - store.begin(); - store.set('a', 100); - - expect(store.get('a')).toBe(100); - - store.commit(); - - expect(store.get('a')).toBe(100); - - store.commit(); - - expect(store.get('a')).toBe(100); - }); - - test('key value store with nested transactions - test case 5', async () => { - const store = new KeyValueStore(); - - store.set('a', 1); - store.set('b', 2); - store.begin(); - store.delete('a'); - store.set('b', 20); - store.begin(); - store.set('a', 100); - store.delete('b'); - - expect(store.get('a')).toBe(100); - expect(store.get('b')).toBeUndefined(); - - store.rollback(); - - expect(store.get('a')).toBeUndefined(); - expect(store.get('b')).toBe(20); - - store.rollback(); - - expect(store.get('a')).toBe(1); - expect(store.get('b')).toBe(2); - }); -}); diff --git a/test/stack/longest-absolute-file-path.test.ts b/test/stack/longest-absolute-file-path.test.ts deleted file mode 100644 index 12a1d89..0000000 --- a/test/stack/longest-absolute-file-path.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { lengthLongestPath } from '../../src/stack/longest-absolute-file-path'; - -describe('longest absolute file path', () => { - test('longest absolute file path - test case 1', async () => { - expect(lengthLongestPath('dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext')).toBe(20); - }); -}); diff --git a/test/stack/max-chunks-to-make-sorted-ii.test.ts b/test/stack/max-chunks-to-make-sorted-ii.test.ts deleted file mode 100644 index acb5f78..0000000 --- a/test/stack/max-chunks-to-make-sorted-ii.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { maxChunksToSorted } from '../../src/stack/max-chunks-to-make-sorted-ii'; - -describe('max chunks to make sorted ii', () => { - test('max chunks to make sorted ii - test case 1', async () => { - expect(maxChunksToSorted([5, 4, 3, 2, 1])).toBe(1); - }); -}); diff --git a/test/stack/max-stack.test.ts b/test/stack/max-stack.test.ts deleted file mode 100644 index 4dcd9d1..0000000 --- a/test/stack/max-stack.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MaxStack } from '../../src/heap/max-stack'; - -describe('max stack', () => { - test('max stack - test case 1', async () => { - const stack = new MaxStack(); - - stack.push(5); - stack.push(1); - stack.push(5); - - expect(stack.top()).toBe(5); - expect(stack.popMax()).toBe(5); - expect(stack.top()).toBe(1); - expect(stack.peekMax()).toBe(5); - expect(stack.pop()).toBe(1); - expect(stack.top()).toBe(5); - }); -}); diff --git a/test/stack/min-stack.test.ts b/test/stack/min-stack.test.ts deleted file mode 100644 index e1501db..0000000 --- a/test/stack/min-stack.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MinStack } from '../../src/stack/min-stack'; - -describe('min stack', () => { - test('min stack - test case 1', async () => { - const stack = new MinStack(); - stack.push(-2); - stack.push(0); - stack.push(-3); - - expect(stack.getMin()).toBe(-3); - - stack.pop(); - - expect(stack.top()).toBe(0); - expect(stack.getMin()).toBe(-2); - }); -}); diff --git a/test/stack/minimum-add-to-make-valid-parentheses.test.ts b/test/stack/minimum-add-to-make-valid-parentheses.test.ts deleted file mode 100644 index 812067c..0000000 --- a/test/stack/minimum-add-to-make-valid-parentheses.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { minAddToMakeValid } from '../../src/stack/minimum-add-to-make-valid-parentheses'; - -describe('minimum add to make valid parentheses', () => { - test('minAddToMakeValid - test case 1', async () => { - expect(minAddToMakeValid('())')).toBe(1); - }); -}); diff --git a/test/stack/minimum-remove-to-make-valid-parentheses.test.ts b/test/stack/minimum-remove-to-make-valid-parentheses.test.ts deleted file mode 100644 index dc70ac8..0000000 --- a/test/stack/minimum-remove-to-make-valid-parentheses.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { minRemoveToMakeValid } from '../../src/stack/minimum-remove-to-make-valid-parentheses'; - -describe('minimum remove to make valid parentheses', () => { - test('minimum remove to make valid parentheses - test case 1', async () => { - expect(minRemoveToMakeValid('lee(t(c)o)de)')).toBe('lee(t(c)o)de'); - }); -}); diff --git a/test/stack/simplify-path.test.ts b/test/stack/simplify-path.test.ts deleted file mode 100644 index 7f63648..0000000 --- a/test/stack/simplify-path.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { simplifyPath } from '../../src/stack/simplify-path'; - -describe('simplify path', () => { - test('simplify path - test case 1', () => { - expect(simplifyPath('/home/')).toEqual('/home'); - }); - - test('simplify path - test case 2', () => { - expect(simplifyPath('/../')).toEqual('/'); - }); -}); diff --git a/test/stack/valid-number.test.ts b/test/stack/valid-number.test.ts deleted file mode 100644 index b1231ef..0000000 --- a/test/stack/valid-number.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { isNumber } from '../../src/stack/valid-number'; - -describe('valid number', () => { - test('valid number - test case 1', async () => { - expect(isNumber('2')).toBe(true); - }); - - test('valid number - test case 2', async () => { - expect(isNumber('0089')).toBe(true); - }); - - test('valid number - test case 3', async () => { - expect(isNumber('-0.1')).toBe(true); - }); - - test('valid number - test case 4', async () => { - expect(isNumber('+3.14')).toBe(true); - }); - - test('valid number - test case 5', async () => { - expect(isNumber('4.')).toBe(true); - }); - - test('valid number - test case 6', async () => { - expect(isNumber('-.9')).toBe(true); - }); - - test('valid number - test case 7', async () => { - expect(isNumber('2e10')).toBe(true); - }); - - test('valid number - test case 8', async () => { - expect(isNumber('-90E3')).toBe(true); - }); - - test('valid number - test case 9', async () => { - expect(isNumber('3e+7')).toBe(true); - }); - - test('valid number - test case 10', async () => { - expect(isNumber('+6e-1')).toBe(true); - }); - - test('valid number - test case 11', async () => { - expect(isNumber('53.5e93')).toBe(true); - }); - - test('valid number - test case 12', async () => { - expect(isNumber('-123.456e789')).toBe(true); - }); - - test('valid number - test case 13', async () => { - expect(isNumber('46.e3')).toBe(true); - }); - - test('valid number - test case 14', async () => { - expect(isNumber('1.e10')).toBe(true); - }); - - test('valid number - test case 15', async () => { - expect(isNumber('9.e3')).toBe(true); - }); - - test('valid number - test case 16', async () => { - expect(isNumber('abc')).toBe(false); - }); - - test('valid number - test case 17', async () => { - expect(isNumber('1a')).toBe(false); - }); - - test('valid number - test case 18', async () => { - expect(isNumber('1e')).toBe(false); - }); - - test('valid number - test case 19', async () => { - expect(isNumber('e3')).toBe(false); - }); - - test('valid number - test case 20', async () => { - expect(isNumber('99e2.5')).toBe(false); - }); - - test('valid number - test case 21', async () => { - expect(isNumber('--6')).toBe(false); - }); - - test('valid number - test case 22', async () => { - expect(isNumber('-+3')).toBe(false); - }); - - test('valid number - test case 23', async () => { - expect(isNumber('95a54e53')).toBe(false); - }); - - test('valid number - test case 24', async () => { - expect(isNumber('1ee3')).toBe(false); - }); - - test('valid number - test case 25', async () => { - expect(isNumber('.')).toBe(false); - }); - - test('valid number - test case 26', async () => { - expect(isNumber('1e+')).toBe(false); - }); - - test('valid number - test case 27', async () => { - expect(isNumber('1e+.')).toBe(false); - }); - - test('valid number - test case 28', async () => { - expect(isNumber('+.')).toBe(false); - }); - - test('valid number - test case 29', async () => { - expect(isNumber('.+')).toBe(false); - }); - - test('valid number - test case 30', async () => { - expect(isNumber('+e3')).toBe(false); - }); - - test('valid number - test case 31', async () => { - expect(isNumber('46e.3')).toBe(false); - }); - - test('valid number - test case 32', async () => { - expect(isNumber('.e3')).toBe(false); - }); -}); diff --git a/test/stack/valid-parentheses.test.ts b/test/stack/valid-parentheses.test.ts deleted file mode 100644 index 566206e..0000000 --- a/test/stack/valid-parentheses.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { isValid } from '../../src/stack/valid-parentheses'; - -describe('valid parentheses', () => { - test('valid parentheses - test case 1', async () => { - expect(isValid('()')).toBe(true); - }); - - test('valid parentheses - test case 2', async () => { - expect(isValid('()[]{}')).toBe(true); - }); - - test('valid parentheses - test case 3', async () => { - expect(isValid('(}')).toBe(false); - }); -}); diff --git a/test/string/find-the-closest-palindrome.test.ts b/test/string/find-the-closest-palindrome.test.ts deleted file mode 100644 index 756d4b8..0000000 --- a/test/string/find-the-closest-palindrome.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { nearestPalindromic } from '../../src/string/find-the-closest-palindrome'; - -describe('find the closest palindrome', () => { - test('nearest palindromic - test case 1', async () => { - expect(nearestPalindromic('1234')).toBe('1221'); - }); - - test('nearest palindromic - test case 2', async () => { - expect(nearestPalindromic('123')).toBe('121'); - }); - - test('nearest palindromic - test case 3', async () => { - expect(nearestPalindromic('1')).toBe('0'); - }); - - test('nearest palindromic - test case 4', async () => { - expect(nearestPalindromic('10')).toBe('9'); - }); - - test('nearest palindromic - test case 5', async () => { - expect(nearestPalindromic('11911')).toBe('11811'); - }); - - test('nearest palindromic - test case 6', async () => { - expect(nearestPalindromic('100')).toBe('99'); - }); - - test('nearest palindromic - test case 7', async () => { - expect(nearestPalindromic('11011')).toBe('11111'); - }); - - test('nearest palindromic - test case 8', async () => { - expect(nearestPalindromic('111111111')).toBe('111101111'); - }); -}); diff --git a/test/string/int-to-roman.test.ts b/test/string/int-to-roman.test.ts deleted file mode 100644 index 4c23b3f..0000000 --- a/test/string/int-to-roman.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { intToRoman } from '../../src/string/int-to-roman'; - -describe('roman to integer', () => { - test('int to roman - test case 1', async () => { - expect(intToRoman(0)).toBe(''); - }); - - test('int to roman - test case 2', async () => { - expect(intToRoman(1)).toBe('I'); - }); - - test('int to roman - test case 3', async () => { - expect(intToRoman(5)).toBe('V'); - }); - - test('int to roman - test case 4', async () => { - expect(intToRoman(10)).toBe('X'); - }); - - test('int to roman - test case 5', async () => { - expect(intToRoman(40)).toBe('XL'); - }); - - test('int to roman - test case 6', async () => { - expect(intToRoman(50)).toBe('L'); - }); - - test('int to roman - test case 7', async () => { - expect(intToRoman(58)).toBe('LVIII'); - }); - - test('int to roman - test case 8', async () => { - expect(intToRoman(90)).toBe('XC'); - }); - - test('int to roman - test case 9', async () => { - expect(intToRoman(100)).toBe('C'); - }); - - test('int to roman - test case 10', async () => { - expect(intToRoman(1000)).toBe('M'); - }); -}); diff --git a/test/string/integer-to-english-words.test.ts b/test/string/integer-to-english-words.test.ts deleted file mode 100644 index aac5959..0000000 --- a/test/string/integer-to-english-words.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { numberToWords } from '../../src/string/integer-to-english-words'; - -describe('integer to english words', () => { - test('integer to english words - test case 1', async () => { - expect(numberToWords(123)).toBe('One Hundred Twenty Three'); - }); - - test('integer to english words - test case 2', async () => { - expect(numberToWords(12345)).toBe('Twelve Thousand Three Hundred Forty Five'); - }); - - test('integer to english words - test case 3', async () => { - expect(numberToWords(1234567)).toBe('One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven'); - }); - - test('integer to english words - test case 4', async () => { - expect(numberToWords(50868)).toBe('Fifty Thousand Eight Hundred Sixty Eight'); - }); -}); diff --git a/test/string/letter-combinations-phone-number.test.ts b/test/string/letter-combinations-phone-number.test.ts deleted file mode 100644 index 877c733..0000000 --- a/test/string/letter-combinations-phone-number.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { letterCombinations } from '../../src/string/letter-combinations-phone-number'; - -describe('letter combinations of a phone number', () => { - test('letter combinations - test case 1', async () => { - const letters = new Set(letterCombinations('')); - - expect(letters).toStrictEqual(new Set()); - }); - - test('letter combinations - test case 2', async () => { - const letters = new Set(letterCombinations('2')); - - expect(letters).toStrictEqual(new Set(['a', 'b', 'c'])); - }); - - test('letter combinations - test case 3', async () => { - const letters = new Set(letterCombinations('23')); - - expect(letters).toStrictEqual(new Set(['ad', 'ae', 'af', 'bd', 'be', 'bf', 'cd', 'ce', 'cf'])); - }); -}); diff --git a/test/string/palindrome-number.test.ts b/test/string/palindrome-number.test.ts deleted file mode 100644 index 065c97a..0000000 --- a/test/string/palindrome-number.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { isPalindrome } from '../../src/string/palindrome-number'; - -describe('palindrome-number', () => { - test('palindrome number - test case 1', () => { - expect(isPalindrome(121)).toBe(true); - }); -}); diff --git a/test/string/remove-all-adjacent-duplicates-i.test.ts b/test/string/remove-all-adjacent-duplicates-i.test.ts deleted file mode 100644 index 06c0f66..0000000 --- a/test/string/remove-all-adjacent-duplicates-i.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { removeDuplicates } from '../../src/string/remove-all-adjacent-duplicates-i'; - -describe('remove all adjacent duplicates i', () => { - test('remove all adjacent duplicates i - test case 1', async () => { - expect(removeDuplicates('abbaca')).toBe('ca'); - }); -}); diff --git a/test/string/remove-all-adjacent-duplicates-ii.test.ts b/test/string/remove-all-adjacent-duplicates-ii.test.ts deleted file mode 100644 index 4df4847..0000000 --- a/test/string/remove-all-adjacent-duplicates-ii.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { removeDuplicates } from '../../src/string/remove-all-adjacent-duplicates-ii'; - -describe('remove all adjacent duplicates ii', () => { - test('remove all adjacent duplicates ii - test case 1', async () => { - expect(removeDuplicates('abcd', 2)).toBe('abcd'); - }); - - test('remove all adjacent duplicates ii - test case 2', async () => { - expect(removeDuplicates('deeedbbcccbdaa', 3)).toBe('aa'); - }); -}); diff --git a/test/string/reverse-words-in-a-string.test.ts b/test/string/reverse-words-in-a-string.test.ts deleted file mode 100644 index b0d0d68..0000000 --- a/test/string/reverse-words-in-a-string.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { reverseWords } from '../../src/string/reverse-words-in-a-string'; - -describe('reverse words in a string', () => { - test('reverse words in a string - test case 1', async () => { - expect(reverseWords('the sky is blue')).toBe('blue is sky the'); - }); -}); diff --git a/test/string/roman-to-int.test.ts b/test/string/roman-to-int.test.ts deleted file mode 100644 index ad294b6..0000000 --- a/test/string/roman-to-int.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { romanToInt } from '../../src/string/roman-to-int'; - -describe('roman to int', () => { - test('roman to int - test case 1', () => { - expect(romanToInt('III')).toBe(3); - }); -}); diff --git a/test/string/time-needed-to-rearrange-binary-string.test.ts b/test/string/time-needed-to-rearrange-binary-string.test.ts deleted file mode 100644 index 4ec8a59..0000000 --- a/test/string/time-needed-to-rearrange-binary-string.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { secondsToRemoveOccurrences } from '../../src/string/time-needed-to-rearrange-binary-string'; - -describe('time needed to rearrange a binary string', () => { - test('time needed to rearrange a binary string - test case 1', async () => { - expect(secondsToRemoveOccurrences('0110101')).toBe(4); - }); -}); diff --git a/test/string/valid-word-abbreviation.test.ts b/test/string/valid-word-abbreviation.test.ts deleted file mode 100644 index afe9d13..0000000 --- a/test/string/valid-word-abbreviation.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { validWordAbbreviation } from '../../src/string/valid-word-abbreviation'; - -describe('valid word abbreviation', () => { - test('valid word abbreviation - test case 1', async () => { - expect(validWordAbbreviation('substitition', 's10n')).toBe(true); - }); -}); diff --git a/test/string/vowel-spellchecker.test.ts b/test/string/vowel-spellchecker.test.ts deleted file mode 100644 index 94cbe57..0000000 --- a/test/string/vowel-spellchecker.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { spellchecker } from '../../src/string/vowel-spellchecker'; - -describe('vowel spellchecker', () => { - test('vowel spellchecker - test case 1', async () => { - const wordlist = ['KiTe', 'kite', 'hare', 'Hare']; - const queries = ['kite', 'Kite', 'KiTe', 'Hare', 'HARE', 'Hear', 'hear', 'keti', 'keet', 'keto']; - - expect(spellchecker(wordlist, queries)).toStrictEqual([ - 'kite', - 'KiTe', - 'KiTe', - 'Hare', - 'hare', - '', - '', - 'KiTe', - '', - 'KiTe' - ]); - }); -}); diff --git a/test/tree/__snapshots__/construct-quad-tree.test.ts.snap b/test/tree/__snapshots__/construct-quad-tree.test.ts.snap deleted file mode 100644 index 47cd81c..0000000 --- a/test/tree/__snapshots__/construct-quad-tree.test.ts.snap +++ /dev/null @@ -1,107 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`construct quad tree quad tree - test case 1 1`] = ` -_Node { - "bottomLeft": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": true, - }, - "bottomRight": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": false, - }, - "isLeaf": false, - "topLeft": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": false, - }, - "topRight": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": true, - }, - "val": false, -} -`; - -exports[`construct quad tree quad tree - test case 2 1`] = ` -_Node { - "bottomLeft": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": true, - }, - "bottomRight": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": false, - }, - "isLeaf": false, - "topLeft": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": true, - }, - "topRight": _Node { - "bottomLeft": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": true, - }, - "bottomRight": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": true, - }, - "isLeaf": false, - "topLeft": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": false, - }, - "topRight": _Node { - "bottomLeft": null, - "bottomRight": null, - "isLeaf": true, - "topLeft": null, - "topRight": null, - "val": false, - }, - "val": false, - }, - "val": false, -} -`; diff --git a/test/tree/binary-tree-level-order-traversal.test.ts b/test/tree/binary-tree-level-order-traversal.test.ts deleted file mode 100644 index 4ea83e7..0000000 --- a/test/tree/binary-tree-level-order-traversal.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { levelOrder } from '../../src/tree/binary-tree-level-order-traversal'; -import { array2tree } from '../../src/tree/common/tree-node'; - -describe('binary tree level order traversal', () => { - test('level order - test case 1', async () => { - const tree = array2tree([3, 9, 20, null, null, 15, 7]); - - expect(levelOrder(tree)).toStrictEqual([[3], [9, 20], [15, 7]]); - }); - - test('level order - test case 2', async () => { - const tree = array2tree([1]); - - expect(levelOrder(tree)).toStrictEqual([[1]]); - }); - - test('level order - test case 3', async () => { - const tree = array2tree([]); - - expect(levelOrder(tree)).toStrictEqual([]); - }); -}); diff --git a/test/tree/binary-tree-right-side-view.test.ts b/test/tree/binary-tree-right-side-view.test.ts deleted file mode 100644 index c268847..0000000 --- a/test/tree/binary-tree-right-side-view.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { array2tree } from '../../src/tree/common/tree-node'; -import { rightSideView } from '../../src/tree/binary-tree-right-side-view'; - -describe('binary tree right side view', () => { - test('binary tree right side view', async () => { - const root = array2tree([1, 2, 3, null, 5, null, 4]); - - expect(rightSideView(root)).toStrictEqual([1, 3, 4]); - }); -}); diff --git a/test/tree/binary-tree-vertical-order-traversal.test.ts b/test/tree/binary-tree-vertical-order-traversal.test.ts deleted file mode 100644 index d790d02..0000000 --- a/test/tree/binary-tree-vertical-order-traversal.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { array2tree } from '../../src/tree/common/tree-node'; -import { verticalOrder } from '../../src/tree/binary-tree-vertical-order-traversal'; - -describe('binary tree vertical order traversal', () => { - test('binary tree vertical order traversal - test case 1', async () => { - const tree = array2tree([3, 9, 20, null, null, 15, 7]); - - expect(verticalOrder(tree)).toStrictEqual([[9], [3, 15], [20], [7]]); - }); -}); diff --git a/test/tree/construct-quad-tree.test.ts b/test/tree/construct-quad-tree.test.ts deleted file mode 100644 index 59e6761..0000000 --- a/test/tree/construct-quad-tree.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { construct } from '../../src/tree/construct-quad-tree'; - -describe('construct quad tree', () => { - test('quad tree - test case 1', async () => { - const grid = [ - [0, 1], - [1, 0] - ]; - - expect(construct(grid)).toMatchSnapshot(); - }); - - test('quad tree - test case 2', async () => { - const grid = [ - [1, 1, 1, 1, 0, 0, 0, 0], - [1, 1, 1, 1, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 0, 0, 0, 0], - [1, 1, 1, 1, 0, 0, 0, 0], - [1, 1, 1, 1, 0, 0, 0, 0], - [1, 1, 1, 1, 0, 0, 0, 0] - ]; - - expect(construct(grid)).toMatchSnapshot(); - }); -}); diff --git a/test/tree/design-in-memory-file-system.test.ts b/test/tree/design-in-memory-file-system.test.ts deleted file mode 100644 index f1379bc..0000000 --- a/test/tree/design-in-memory-file-system.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { FileSystem } from '../../src/tree/design-in-memory-file-system'; - -describe('design in memory file system', () => { - test('design in memory file system - test case 1', async () => { - const fs = new FileSystem(); - - expect(fs.ls('/')).toStrictEqual([]); - - fs.mkdir('/a/b/c'); - fs.addContentToFile('/a/b/c/d', 'hello'); - - expect(fs.ls('/')).toStrictEqual(['a']); - expect(fs.readContentFromFile('/a/b/c/d')).toBe('hello'); - }); -}); diff --git a/test/tree/diameter-of-binary-tree.test.ts b/test/tree/diameter-of-binary-tree.test.ts deleted file mode 100644 index 62ae07e..0000000 --- a/test/tree/diameter-of-binary-tree.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { array2tree } from '../../src/tree/common/tree-node'; -import { diameterOfBinaryTree } from '../../src/tree/diameter-of-binary-tree'; - -describe('diameter of binary tree', () => { - test('diameter of binary tree - test case 1', async () => { - const tree = array2tree([1, 2, 3]); - - expect(diameterOfBinaryTree(tree)).toBe(2); - }); - - test('diameter of binary tree - test case 2', async () => { - const tree = array2tree([1, 2, 3, 4, 5]); - - expect(diameterOfBinaryTree(tree)).toBe(3); - }); -}); diff --git a/test/tree/longest-palindromic-substring.test.ts b/test/tree/longest-palindromic-substring.test.ts deleted file mode 100644 index b42863f..0000000 --- a/test/tree/longest-palindromic-substring.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { longestPalindrome } from '../../src/string/longest-palindromic-substring'; - -describe('longest palindromic substring', () => { - test('longestPalindrome - test case 1', () => { - expect(longestPalindrome('babad')).toEqual('bab'); - }); -}); diff --git a/test/tree/lowest-common-ancestor-of-a-binary-tree-iii.test.ts b/test/tree/lowest-common-ancestor-of-a-binary-tree-iii.test.ts deleted file mode 100644 index f5d0ef9..0000000 --- a/test/tree/lowest-common-ancestor-of-a-binary-tree-iii.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { array2tree } from '../../src/tree/common/parent-node'; -import { lowestCommonAncestor } from '../../src/tree/lowest-common-ancestor-of-a-binary-tree-iii'; - -describe('lowest common ancestor of a binary tree iii', () => { - test('lowest common ancestor - test case 1', async () => { - const tree = array2tree([3, 5, 1, 6, 2, 0, 8, null, null, 7, 4]); - - expect(lowestCommonAncestor(tree!.left, tree!.right)?.val).toStrictEqual(3); - }); -}); diff --git a/test/tree/lowest-common-ancestor-of-a-binary-tree.test.ts b/test/tree/lowest-common-ancestor-of-a-binary-tree.test.ts deleted file mode 100644 index a3e1187..0000000 --- a/test/tree/lowest-common-ancestor-of-a-binary-tree.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { array2tree } from '../../src/tree/common/tree-node'; -import { lowestCommonAncestor } from '../../src/tree/lowest-common-ancestor-of-a-binary-tree'; - -describe('lowest common ancestor of a binary tree i', () => { - test('lowest common ancestor - test case 1', async () => { - const tree = array2tree([3, 5, 1, 6, 2, 0, 8, null, null, 7, 4]); - - expect(lowestCommonAncestor(tree, tree!.left, tree!.right)?.val).toStrictEqual(3); - }); -}); diff --git a/test/tree/range-sum-of-bst.test.ts b/test/tree/range-sum-of-bst.test.ts deleted file mode 100644 index 2be267f..0000000 --- a/test/tree/range-sum-of-bst.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { array2tree } from '../../src/tree/common/tree-node'; -import { rangeSumBST } from '../../src/tree/range-sum-of-bst'; - -describe('range sum of bst', () => { - test('range sum of bst - test case 1', async () => { - const tree = array2tree([10, 5, 15, 3, 7, null, 18]); - expect(rangeSumBST(tree, 7, 15)).toBe(32); - }); -}); diff --git a/test/tree/same-tree.test.ts b/test/tree/same-tree.test.ts deleted file mode 100644 index b9d1ef8..0000000 --- a/test/tree/same-tree.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { TreeNode } from '../../src/tree/common/tree-node'; -import { isSameTree } from '../../src/tree/same-tree'; - -describe('same tree', () => { - test('same tree - test case 1', async () => { - expect(isSameTree(null, null)).toBe(true); - }); - - test('same tree - test case 2', async () => { - expect(isSameTree(new TreeNode(10, null, null), new TreeNode(10, null, null))); - }); -}); diff --git a/test/tree/sum-root-to-leaf-numbers.test.ts b/test/tree/sum-root-to-leaf-numbers.test.ts deleted file mode 100644 index f25329e..0000000 --- a/test/tree/sum-root-to-leaf-numbers.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { TreeNode } from '../../src/tree/common/tree-node'; -import { sumNumbers } from '../../src/tree/sum-root-to-leaf-numbers'; - -describe('sum root to leaf numbers', () => { - test('sum root to leaf numbers - test case 1', async () => { - const tree = new TreeNode(1); - tree.left = new TreeNode(2); - tree.right = new TreeNode(3); - - expect(sumNumbers(tree)).toBe(25); - }); -}); diff --git a/test/two-pointer/__snapshots__/three-sum.test.ts.snap b/test/two-pointer/__snapshots__/three-sum.test.ts.snap deleted file mode 100644 index 1db462d..0000000 --- a/test/two-pointer/__snapshots__/three-sum.test.ts.snap +++ /dev/null @@ -1,66 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`three sum three sum - test case 1 1`] = ` -[ - [ - -1, - -1, - 2, - ], - [ - -1, - 0, - 1, - ], -] -`; - -exports[`three sum three sum - test case 2 1`] = ` -[ - [ - -4, - 0, - 4, - ], - [ - -4, - 1, - 3, - ], - [ - -3, - -1, - 4, - ], - [ - -3, - 0, - 3, - ], - [ - -3, - 1, - 2, - ], - [ - -2, - -1, - 3, - ], - [ - -2, - 0, - 2, - ], - [ - -1, - -1, - 2, - ], - [ - -1, - 0, - 1, - ], -] -`; diff --git a/test/two-pointer/bag-of-tokens.test.ts b/test/two-pointer/bag-of-tokens.test.ts deleted file mode 100644 index 781bfa2..0000000 --- a/test/two-pointer/bag-of-tokens.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { bagOfTokensScore } from '../../src/two-pointer/bag-of-tokens'; - -describe('bag of tokens', () => { - test('bag of tokens - test case 1', async () => { - expect(bagOfTokensScore([100], 50)).toBe(0); - }); - - test('bag of tokens - test case 2', async () => { - expect(bagOfTokensScore([200, 100], 150)).toBe(1); - }); - - test('bag of tokens - test case 3', async () => { - expect(bagOfTokensScore([100, 200, 300, 400], 200)).toBe(2); - }); -}); diff --git a/test/two-pointer/container-with-most-water.test.ts b/test/two-pointer/container-with-most-water.test.ts deleted file mode 100644 index 8e0866c..0000000 --- a/test/two-pointer/container-with-most-water.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { maxArea } from '../../src/two-pointer/container-with-most-water'; - -describe('container with most water', () => { - test('container with most water - test case 1', async () => { - expect(maxArea([1, 8, 6, 2, 5, 4, 8, 3, 7])).toBe(49); - }); -}); diff --git a/test/two-pointer/longest-common-prefix.test.ts b/test/two-pointer/longest-common-prefix.test.ts deleted file mode 100644 index 6ce7161..0000000 --- a/test/two-pointer/longest-common-prefix.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { longestCommonPrefix } from '../../src/two-pointer/longest-common-prefix'; - -describe('longest common prefix', () => { - test('longest common prefix - test case 1', async () => { - expect(longestCommonPrefix(['ab', 'a'])).toBe('a'); - }); - - test('longest common prefix - test case 2', async () => { - expect(longestCommonPrefix(['a'])).toBe('a'); - }); - - test('longest common prefix - test case 3', async () => { - expect(longestCommonPrefix(['flower', 'flow', 'flight'])).toBe('fl'); - }); - - test('longest common prefix - test case 4', async () => { - expect(longestCommonPrefix(['dog', 'racecar', 'car'])).toBe(''); - }); - - test('longest common prefix - test case 5', async () => { - expect(longestCommonPrefix(['', 'car'])).toBe(''); - }); -}); diff --git a/test/two-pointer/longest-substring-without-repeating-chars.test.ts b/test/two-pointer/longest-substring-without-repeating-chars.test.ts deleted file mode 100644 index bcbfcf3..0000000 --- a/test/two-pointer/longest-substring-without-repeating-chars.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { lengthOfLongestSubstring } from '../../src/two-pointer/longest-substring-without-repeating-chars'; - -describe('longest substring without repeating characters', () => { - test('length of longest substring - test case 4', async () => { - expect(lengthOfLongestSubstring('abcabcbb')).toBe(3); - }); - - test('length of longest substring - test case 5', async () => { - expect(lengthOfLongestSubstring('bbbbb')).toBe(1); - }); - - test('length of longest substring - test case 6', async () => { - expect(lengthOfLongestSubstring('pwwkew')).toBe(3); - }); -}); diff --git a/test/two-pointer/remove-duplicates-from-sorted-array.test.ts b/test/two-pointer/remove-duplicates-from-sorted-array.test.ts deleted file mode 100644 index 520acf3..0000000 --- a/test/two-pointer/remove-duplicates-from-sorted-array.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { removeDuplicates } from '../../src/two-pointer/remove-duplicates-from-sorted-array'; - -describe('remove duplicates from sorted array', () => { - test('remove duplicates from sorted array - test case 1', () => { - expect(removeDuplicates([1, 1, 2])).toBe(2); - }); -}); diff --git a/test/two-pointer/remove-element.test.ts b/test/two-pointer/remove-element.test.ts deleted file mode 100644 index fdec913..0000000 --- a/test/two-pointer/remove-element.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { removeElement } from '../../src/two-pointer/remove-element'; - -describe('remove element', () => { - test('remove element - test case 1', () => { - expect(removeElement([3, 2, 2, 3], 3)).toBe(2); - }); - - test('remove element - test case 2', () => { - expect(removeElement([0, 1, 2, 2, 3, 0, 4, 2], 2)).toBe(5); - }); -}); diff --git a/test/two-pointer/string-compression.test.ts b/test/two-pointer/string-compression.test.ts deleted file mode 100644 index 4813c75..0000000 --- a/test/two-pointer/string-compression.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { compress } from '../../src/two-pointer/string-compression'; - -describe('string compression', () => { - test('string compression - test case 1', async () => { - const cs = ['a', 'a', 'b', 'b', 'c', 'c', 'c']; - - const count = compress(cs); - - expect(cs.slice(0, count)).toStrictEqual(['a', '2', 'b', '2', 'c', '3']); - }); -}); diff --git a/test/two-pointer/three-sum-closest.test.ts b/test/two-pointer/three-sum-closest.test.ts deleted file mode 100644 index 6422080..0000000 --- a/test/two-pointer/three-sum-closest.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { threeSumClosest } from '../../src/two-pointer/three-sum-closest'; - -describe('three sum closest', () => { - test('three sum closest - test case 1', async () => { - expect(threeSumClosest([-1, 2, 1, -4], 1)).toStrictEqual(2); - }); - - test('three sum closest - test case 2', async () => { - expect(threeSumClosest([0, 0, 0], 1)).toStrictEqual(0); - }); -}); diff --git a/test/two-pointer/three-sum.test.ts b/test/two-pointer/three-sum.test.ts deleted file mode 100644 index 2c529a2..0000000 --- a/test/two-pointer/three-sum.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { threeSum } from '../../src/two-pointer/three-sum'; - -describe('three sum', () => { - test('three sum - test case 1', async () => { - expect(threeSum([-1, 0, 1, 2, -1, -4])).toMatchSnapshot(); - }); - - test('three sum - test case 2', async () => { - expect(threeSum([-1, 0, 1, 2, -1, -4, -2, -3, 3, 0, 4])).toMatchSnapshot(); - }); -}); diff --git a/test/two-pointer/trapping-rain-water.test.ts b/test/two-pointer/trapping-rain-water.test.ts deleted file mode 100644 index dfd60ed..0000000 --- a/test/two-pointer/trapping-rain-water.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { trap } from '../../src/two-pointer/trapping-rain-water'; - -describe('trapping rain water', () => { - test('trapping rain water - test case 1', async () => { - expect(trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1])).toBe(6); - }); - - test('trapping rain water - test case 2', async () => { - expect(trap([0, 1, 1, 0])).toBe(0); - }); - - test('trapping rain water - test case 3', async () => { - expect(trap([0, 1, 0, 1, 0])).toBe(1); - }); -}); diff --git a/test/two-pointer/two-sum-input-array-sorted.test.ts b/test/two-pointer/two-sum-input-array-sorted.test.ts deleted file mode 100644 index c23caee..0000000 --- a/test/two-pointer/two-sum-input-array-sorted.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { twoSum } from '../../src/two-pointer/two-sum-input-array-sorted'; - -describe('two sum - input array is sorted', () => { - test('two sum sorted - test case 1', async () => { - expect(twoSum([2, 7, 11, 15], 9)).toStrictEqual([1, 2]); - }); - - test('two sum sorted - test case 2', async () => { - expect(twoSum([2, 3, 4], 6)).toStrictEqual([1, 3]); - }); - - test('two sum sorted - test case 3', async () => { - expect(twoSum([-1, 0], -1)).toStrictEqual([1, 2]); - }); -}); diff --git a/test/two-pointer/valid-palindrome-ii.test.ts b/test/two-pointer/valid-palindrome-ii.test.ts deleted file mode 100644 index 1f34c88..0000000 --- a/test/two-pointer/valid-palindrome-ii.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { validPalindrome } from '../../src/two-pointer/valid-palindrome-ii'; - -describe('valid palindrome ii', () => { - test('valid palindrome ii - test case 1', async () => { - expect(validPalindrome('aba')).toBe(true); - }); -}); diff --git a/test/two-pointer/valid-palindrome.test.ts b/test/two-pointer/valid-palindrome.test.ts deleted file mode 100644 index 00644b7..0000000 --- a/test/two-pointer/valid-palindrome.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { isPalindrome } from '../../src/two-pointer/valid-palindrome'; - -describe('valid palindrome', () => { - test('valid palindrome - test case 1', async () => { - expect(isPalindrome('A man, a plan, a canal: Panama')).toBe(true); - }); - - test('valid palindrome - test case 2', async () => { - expect(isPalindrome('a')).toBe(true); - expect(isPalindrome('aa')).toBe(true); - expect(isPalindrome('aba')).toBe(true); - expect(isPalindrome('ab')).toBe(false); - }); -}); diff --git a/test/two-pointer/valid-triangle-number.test.ts b/test/two-pointer/valid-triangle-number.test.ts deleted file mode 100644 index 22d0a8a..0000000 --- a/test/two-pointer/valid-triangle-number.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { triangleNumber } from '../../src/two-pointer/valid-triangle-number'; - -describe('valid triangle number', () => { - test('valid triangle number - test case 1', async () => { - expect(triangleNumber([2, 2, 3, 4])).toBe(3); - }); -}); diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 2f75948..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - // See https://github.com/tsconfig/bases/blob/main/bases/recommended.json - "extends": "@tsconfig/recommended/tsconfig.json", - "compilerOptions": { - "declaration": true, - "declarationMap": true, - "esModuleInterop": true, - "module": "ESNext", - "moduleResolution": "node", - "outDir": "./build", - "sourceMap": true, - "strict": true, - "target": "ES2024", - "typeRoots": ["node_modules/@types"], - "types": ["jest", "node"] - }, - "include": ["script", "src", "test"], - "exclude": ["build", "dist", "node_modules"] -}